Compare commits
72 Commits
ed-06-10-2
...
ed-18-10-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
237716b353 | ||
|
|
f14177bf86 | ||
|
|
9a8d30022a | ||
|
|
ebca84d28f | ||
|
|
5b4a7f6edc | ||
|
|
a83d6f0d87 | ||
|
|
5d89812abc | ||
|
|
31d39b36ae | ||
|
|
dc47bb2283 | ||
|
|
d259191d3d | ||
|
|
519a6b2474 | ||
|
|
4425b77c90 | ||
|
|
796764d755 | ||
|
|
e5ad32fe93 | ||
|
|
648505dc15 | ||
|
|
af72f2e17c | ||
|
|
40959cf422 | ||
|
|
a6fc5e6ace | ||
|
|
e98af125dc | ||
|
|
bc64676747 | ||
|
|
e7b7e2270d | ||
|
|
740288eb96 | ||
|
|
93c7bdc134 | ||
|
|
3e078ab3e0 | ||
|
|
ac16a05fef | ||
|
|
327466a6e2 | ||
|
|
6d99597349 | ||
|
|
844b4d4616 | ||
|
|
fc1c709d44 | ||
|
|
5a41cc81b3 | ||
|
|
d450b613d6 | ||
|
|
b657aba2e1 | ||
|
|
ddaa0e83c6 | ||
|
|
1dbbf315c7 | ||
|
|
b9df2a495a | ||
|
|
357e998cbb | ||
|
|
384ff7e8f6 | ||
|
|
1ca7456363 | ||
|
|
dc2899c274 | ||
|
|
6f9b4f4226 | ||
|
|
28f576dae8 | ||
|
|
1366f6b405 | ||
|
|
0e0887bd49 | ||
|
|
1f04117edf | ||
|
|
922dd0bce6 | ||
|
|
33042b00d0 | ||
|
|
38fd54a1bf | ||
|
|
34df781668 | ||
|
|
f22f9e39c5 | ||
|
|
eecbfb63a0 | ||
|
|
6b10e00da4 | ||
|
|
a6c468b697 | ||
|
|
46a2eb545e | ||
|
|
35fc1b4037 | ||
|
|
867efe8b5b | ||
|
|
126c1786de | ||
|
|
386e59b462 | ||
|
|
eb4e422cb0 | ||
|
|
2287df13bd | ||
|
|
db64ad9c4d | ||
|
|
644d7ab682 | ||
|
|
ec4acdebbf | ||
|
|
94c8018ff3 | ||
|
|
fbb6f17add | ||
|
|
88f060d51a | ||
|
|
8b65186bfa | ||
|
|
9e054c1f9d | ||
|
|
423d394af1 | ||
|
|
295e63193c | ||
|
|
570c166517 | ||
|
|
7edd1c6d52 | ||
|
|
c0d67429ab |
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@@ -5,8 +5,8 @@ concurrency:
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
# schedule:
|
||||
# - cron: '0 10 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Buckle;
|
||||
using Content.Client.Gravity;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
[Dependency] private readonly GravitySystem _gravity = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly BuckleSystem _buckle = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeAllEvent<StartedWaddlingEvent>(OnStartWaddling);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeAllEvent<StoppedWaddlingEvent>(OnStopWaddling);
|
||||
}
|
||||
|
||||
private void OnStartWaddling(StartedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StartWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void OnStopWaddling(StoppedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StopWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void StartWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
if (_gravity.IsWeightless(entity.Owner))
|
||||
return;
|
||||
|
||||
if (!_actionBlocker.CanMove(entity.Owner, mover))
|
||||
return;
|
||||
|
||||
// Do nothing if buckled in
|
||||
if (_buckle.IsBuckled(entity.Owner))
|
||||
return;
|
||||
|
||||
// Do nothing if crit or dead (for obvious reasons)
|
||||
if (_mobState.IsIncapacitated(entity.Owner))
|
||||
return;
|
||||
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
private static float CalculateTumbleIntensity(WaddleAnimationComponent component)
|
||||
{
|
||||
return component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
}
|
||||
|
||||
private static float CalculateAnimationLength(WaddleAnimationComponent component, InputMoverComponent mover)
|
||||
{
|
||||
return mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(Entity<WaddleAnimationComponent> entity, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key != entity.Comp.KeyName)
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
private void StopWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (!_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
_animation.Stop(entity.Owner, entity.Comp.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(entity.Owner, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
}
|
||||
|
||||
private void PlayWaddleAnimationUsing(Entity<WaddleAnimationComponent> entity, float len, float tumbleIntensity)
|
||||
{
|
||||
entity.Comp.LastStep = !entity.Comp.LastStep;
|
||||
|
||||
var anim = new Animation()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(len),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Rotation),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), 0),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(tumbleIntensity), len/2),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), len/2),
|
||||
}
|
||||
},
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Offset),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), 0),
|
||||
new AnimationTrackProperty.KeyFrame(entity.Comp.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), len/2),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animation.Play(entity.Owner, anim, entity.Comp.KeyName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<GridContainer Columns="6">
|
||||
|
||||
<TextureRect Name="GoldView"
|
||||
MinSize="10 10"
|
||||
Stretch="KeepAspectCentered"/>
|
||||
<Label Name="GoldText"
|
||||
Margin="0,0,5,0"/>
|
||||
|
||||
<TextureRect Name="SilverView"
|
||||
MinSize="10 10"
|
||||
Stretch="KeepAspectCentered"/>
|
||||
<Label Name="SilverText"
|
||||
Margin="0,0,5,0"/>
|
||||
|
||||
<TextureRect Name="CopperView"
|
||||
MinSize="10 10"
|
||||
Stretch="KeepAspectCentered"/>
|
||||
<Label Name="CopperText"
|
||||
Margin="0,0,5,0"/>
|
||||
</GridContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,46 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._CP14.TravelingStoreShip;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CP14PriceControl : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private readonly SpriteSystem _sprite;
|
||||
|
||||
public CP14PriceControl(int price)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sprite = _entity.System<SpriteSystem>();
|
||||
|
||||
var rsiPath = new ResPath("_CP14/Interface/Misc/coins.rsi");
|
||||
|
||||
var total = price;
|
||||
|
||||
var gp = total / 100;
|
||||
total %= 100;
|
||||
|
||||
var sp = total / 10;
|
||||
total %= 10;
|
||||
|
||||
var cp = total;
|
||||
|
||||
CopperView.Texture = _sprite.Frame0(new SpriteSpecifier.Rsi(rsiPath, "c"));
|
||||
CopperText.Text = cp.ToString();
|
||||
|
||||
SilverView.Texture = _sprite.Frame0(new SpriteSpecifier.Rsi(rsiPath, "s"));
|
||||
SilverText.Text = sp.ToString();
|
||||
|
||||
GoldView.Texture = _sprite.Frame0(new SpriteSpecifier.Rsi(rsiPath, "g"));
|
||||
GoldText.Text = gp.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Shared._CP14.TravelingStoreShip;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._CP14.TravelingStoreShip;
|
||||
|
||||
public sealed class CP14StoreBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private CP14StoreWindow? _window;
|
||||
|
||||
public CP14StoreBoundUserInterface(EntityUid owner, [NotNull] Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<CP14StoreWindow>();
|
||||
}
|
||||
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CP14StoreUiState storeState:
|
||||
_window?.UpdateUI(storeState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<Button Name="ProductButton" Access="Public">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<TextureRect Name="View"
|
||||
MinSize="48 48"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Stretch="KeepAspectCentered"/>
|
||||
<RichTextLabel Name="ProductName" VerticalAlignment="Center" Access="Public"/>
|
||||
<BoxContainer Name="PriceHolder" VerticalAlignment="Center" HorizontalExpand="True" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
</Control>
|
||||
@@ -0,0 +1,44 @@
|
||||
using Content.Shared._CP14.TravelingStoreShip;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._CP14.TravelingStoreShip;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CP14StoreProductControl : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
|
||||
private readonly SpriteSystem _sprite;
|
||||
|
||||
public CP14StoreProductControl(CP14StoreUiProductEntry entry)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sprite = _entity.System<SpriteSystem>();
|
||||
|
||||
UpdateName(entry.Name);
|
||||
UpdateView(entry.Icon);
|
||||
UpdatePrice(entry.Price);
|
||||
}
|
||||
|
||||
private void UpdatePrice(int price)
|
||||
{
|
||||
PriceHolder.RemoveAllChildren();
|
||||
PriceHolder.AddChild(new CP14PriceControl(price));
|
||||
}
|
||||
|
||||
private void UpdateName(string name)
|
||||
{
|
||||
ProductName.Text = $"[bold]{name}[/bold]";
|
||||
}
|
||||
|
||||
private void UpdateView(SpriteSpecifier spriteSpecifier)
|
||||
{
|
||||
View.Texture = _sprite.Frame0(spriteSpecifier);
|
||||
}
|
||||
}
|
||||
31
Content.Client/_CP14/TravelingStoreShip/CP14StoreWindow.xaml
Normal file
31
Content.Client/_CP14/TravelingStoreShip/CP14StoreWindow.xaml
Normal file
@@ -0,0 +1,31 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'cp14-store-ui-title'}"
|
||||
MinSize="800 600"
|
||||
SetSize="800 600">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal">
|
||||
<!-- Product list (left side UI) -->
|
||||
<TabContainer SizeFlagsStretchRatio="0.5" Name="Tabs" HorizontalExpand="True" VerticalExpand="True" MinSize="0 200">
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" MinSize="0 200">
|
||||
<BoxContainer Name="BuyProductsContainer" Orientation="Vertical" HorizontalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" MinSize="0 200">
|
||||
<BoxContainer Name="SellProductsContainer" Orientation="Vertical" HorizontalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
</TabContainer>
|
||||
<!-- Station trading data (right side UI) -->
|
||||
<BoxContainer SizeFlagsStretchRatio="0.5" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="0 0 10 0">
|
||||
<controls:StripeBack>
|
||||
<PanelContainer>
|
||||
<Label Text="{Loc 'cp14-store-ui-order'}" Align="Center" Margin="0 5 0 3"/>
|
||||
</PanelContainer>
|
||||
</controls:StripeBack>
|
||||
<Label Name="TravelTimeLabel" Text="00:00" HorizontalAlignment="Center" HorizontalExpand="True" Margin="0 15 0 0"/>
|
||||
<controls:HLine Color="#404040" Thickness="2" Margin="0 5"/>
|
||||
<RichTextLabel Name="SelectedName" HorizontalExpand="True" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="5"/>
|
||||
<RichTextLabel Name="SelectedDesc" HorizontalExpand="True" VerticalExpand="True" VerticalAlignment="Top" Margin="5"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,79 @@
|
||||
using Content.Shared._CP14.TravelingStoreShip;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._CP14.TravelingStoreShip;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CP14StoreWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private TimeSpan? _nextTravelTime;
|
||||
private bool _onStation;
|
||||
|
||||
public CP14StoreWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Tabs.SetTabTitle(0, Loc.GetString("cp14-store-ui-tab-buy"));
|
||||
Tabs.SetTabTitle(1, Loc.GetString("cp14-store-ui-tab-sell"));
|
||||
}
|
||||
|
||||
public void UpdateUI(CP14StoreUiState state)
|
||||
{
|
||||
UpdateProducts(state);
|
||||
|
||||
_nextTravelTime = state.NextTravelTime;
|
||||
_onStation = state.OnStation;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
//Updating time
|
||||
if (_nextTravelTime is not null)
|
||||
{
|
||||
var time = _nextTravelTime.Value - _timing.CurTime;
|
||||
|
||||
TravelTimeLabel.Text =
|
||||
$"{Loc.GetString(_onStation ? "cp14-store-ui-next-travel-out" : "cp14-store-ui-next-travel-in")} {Math.Max(time.Minutes, 0):00}:{Math.Max(time.Seconds, 0):00}";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateProducts(CP14StoreUiState state)
|
||||
{
|
||||
BuyProductsContainer.RemoveAllChildren();
|
||||
SellProductsContainer.RemoveAllChildren();
|
||||
|
||||
foreach (var product in state.ProductsBuy)
|
||||
{
|
||||
var control = new CP14StoreProductControl(product);
|
||||
control.ProductButton.OnPressed += _ =>
|
||||
{
|
||||
SelectProduct(product);
|
||||
};
|
||||
BuyProductsContainer.AddChild(control);
|
||||
}
|
||||
|
||||
foreach (var product in state.ProductsSell)
|
||||
{
|
||||
var control = new CP14StoreProductControl(product);
|
||||
control.ProductButton.OnPressed += _ =>
|
||||
{
|
||||
SelectProduct(product);
|
||||
};
|
||||
SellProductsContainer.AddChild(control);
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectProduct(CP14StoreUiProductEntry? entry)
|
||||
{
|
||||
SelectedName.Text = entry is null ? string.Empty : $"[bold]{entry.Value.Name}[/bold]";
|
||||
SelectedDesc.Text = entry is null ? string.Empty : entry.Value.Desc;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.GameTicking;
|
||||
@@ -120,8 +121,8 @@ public sealed class NukeOpsTest
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind));
|
||||
Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True);
|
||||
Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False);
|
||||
var roles = roleSys.MindGetAllRoles(mind);
|
||||
var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander" && x.Component is NukeopsRoleComponent);
|
||||
var roles = roleSys.MindGetAllRoleInfo(mind);
|
||||
var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander");
|
||||
Assert.That(cmdRoles.Count(), Is.EqualTo(1));
|
||||
|
||||
// The second dummy player should be a medic
|
||||
@@ -131,8 +132,8 @@ public sealed class NukeOpsTest
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(dummyMind));
|
||||
Assert.That(factionSys.IsMember(dummyEnts[1], "Syndicate"), Is.True);
|
||||
Assert.That(factionSys.IsMember(dummyEnts[1], "NanoTrasen"), Is.False);
|
||||
roles = roleSys.MindGetAllRoles(dummyMind);
|
||||
cmdRoles = roles.Where(x => x.Prototype == "NukeopsMedic" && x.Component is NukeopsRoleComponent);
|
||||
roles = roleSys.MindGetAllRoleInfo(dummyMind);
|
||||
cmdRoles = roles.Where(x => x.Prototype == "NukeopsMedic");
|
||||
Assert.That(cmdRoles.Count(), Is.EqualTo(1));
|
||||
|
||||
// The other two players should have just spawned in as normal.
|
||||
@@ -141,13 +142,14 @@ public sealed class NukeOpsTest
|
||||
void CheckDummy(int i)
|
||||
{
|
||||
var ent = dummyEnts[i];
|
||||
var mind = mindSys.GetMind(ent)!.Value;
|
||||
var mindCrew = mindSys.GetMind(ent)!.Value;
|
||||
Assert.That(entMan.HasComponent<NukeOperativeComponent>(ent), Is.False);
|
||||
Assert.That(roleSys.MindIsAntagonist(mind), Is.False);
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind), Is.False);
|
||||
Assert.That(roleSys.MindIsAntagonist(mindCrew), Is.False);
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mindCrew), Is.False);
|
||||
Assert.That(factionSys.IsMember(ent, "Syndicate"), Is.False);
|
||||
Assert.That(factionSys.IsMember(ent, "NanoTrasen"), Is.True);
|
||||
Assert.That(roleSys.MindGetAllRoles(mind).Any(x => x.Component is NukeopsRoleComponent), Is.False);
|
||||
var nukeroles = new List<string>() { "Nukeops", "NukeopsMedic", "NukeopsCommander" };
|
||||
Assert.That(roleSys.MindGetAllRoleInfo(mindCrew).Any(x => nukeroles.Contains(x.Prototype)), Is.False);
|
||||
}
|
||||
|
||||
// The game rule exists, and all the stations/shuttles/maps are properly initialized
|
||||
@@ -238,7 +240,8 @@ public sealed class NukeOpsTest
|
||||
for (var i = 0; i < nukies.Length - 1; i++)
|
||||
{
|
||||
entMan.DeleteEntity(nukies[i]);
|
||||
Assert.That(roundEndSys.IsRoundEndRequested, Is.False,
|
||||
Assert.That(roundEndSys.IsRoundEndRequested,
|
||||
Is.False,
|
||||
$"The round ended, but {nukies.Length - i - 1} nukies are still alive!");
|
||||
}
|
||||
// Delete the last nukie and make sure the round ends.
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Internals;
|
||||
|
||||
@@ -25,10 +24,7 @@ public sealed class AutoInternalsTests
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var profile = new HumanoidCharacterProfile();
|
||||
var dummy = stationSpawning.SpawnPlayerMob(testMap.GridCoords, new JobComponent()
|
||||
{
|
||||
Prototype = "TestInternalsDummy"
|
||||
}, profile, station: null);
|
||||
var dummy = stationSpawning.SpawnPlayerMob(testMap.GridCoords, "TestInternalsDummy", profile, station: null);
|
||||
|
||||
Assert.That(atmos.HasAtmosphere(testMap.Grid), Is.False, "Test map has atmosphere - test needs adjustment!");
|
||||
Assert.That(internals.AreInternalsWorking(dummy), "Internals did not automatically connect!");
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Ghost.Roles;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Mind.Commands;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
@@ -18,7 +16,6 @@ using Robust.Server.Console;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -287,27 +284,27 @@ public sealed partial class MindTests
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId), Is.False);
|
||||
});
|
||||
|
||||
var traitorRole = new TraitorRoleComponent();
|
||||
var traitorRole = "MindRoleTraitor";
|
||||
|
||||
roleSystem.MindAddRole(mindId, traitorRole);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId), Is.False);
|
||||
});
|
||||
|
||||
var jobRole = new JobComponent();
|
||||
var jobRole = "";
|
||||
|
||||
roleSystem.MindAddRole(mindId, jobRole);
|
||||
roleSystem.MindAddJobRole(mindId, jobPrototype:jobRole);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId));
|
||||
});
|
||||
|
||||
roleSystem.MindRemoveRole<TraitorRoleComponent>(mindId);
|
||||
@@ -315,15 +312,15 @@ public sealed partial class MindTests
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId));
|
||||
});
|
||||
|
||||
roleSystem.MindRemoveRole<JobComponent>(mindId);
|
||||
roleSystem.MindRemoveRole<JobRoleComponent>(mindId);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId), Is.False);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Server.Station.Systems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -68,10 +67,7 @@ public sealed class LoadoutTests
|
||||
|
||||
profile.SetLoadout(new RoleLoadout("LoadoutTester"));
|
||||
|
||||
var tester = stationSystem.SpawnPlayerMob(testMap.GridCoords, job: new JobComponent()
|
||||
{
|
||||
Prototype = "LoadoutTester"
|
||||
}, profile, station: null);
|
||||
var tester = stationSystem.SpawnPlayerMob(testMap.GridCoords, job: "LoadoutTester", profile, station: null);
|
||||
|
||||
var slotQuery = inventorySystem.GetSlotEnumerator(tester);
|
||||
var checkedCount = 0;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Content.Server.Access.Systems
|
||||
if (!TryComp<IdCardComponent>(uid, out var idCard))
|
||||
return;
|
||||
|
||||
var state = new AgentIDCardBoundUserInterfaceState(idCard.FullName ?? "", idCard.JobTitle ?? "", idCard.JobIcon);
|
||||
var state = new AgentIDCardBoundUserInterfaceState(idCard.FullName ?? "", idCard.LocalizedJobTitle ?? "", idCard.JobIcon);
|
||||
_uiSystem.SetUiState(uid, AgentIDCardUiKey.Key, state);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
||||
PrivilegedIdIsAuthorized(uid, component),
|
||||
true,
|
||||
targetIdComponent.FullName,
|
||||
targetIdComponent.JobTitle,
|
||||
targetIdComponent.LocalizedJobTitle,
|
||||
targetAccessComponent.Tags.ToList(),
|
||||
possibleAccess,
|
||||
jobProto,
|
||||
|
||||
@@ -22,11 +22,17 @@ public sealed class TechAnomalySystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TechAnomalyComponent, MapInitEvent>(OnTechMapInit);
|
||||
SubscribeLocalEvent<TechAnomalyComponent, AnomalyPulseEvent>(OnPulse);
|
||||
SubscribeLocalEvent<TechAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
|
||||
SubscribeLocalEvent<TechAnomalyComponent, AnomalyStabilityChangedEvent>(OnStabilityChanged);
|
||||
}
|
||||
|
||||
private void OnTechMapInit(Entity<TechAnomalyComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextTimer = _timing.CurTime;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
@@ -11,7 +11,6 @@ using Content.Server.Preferences.Managers;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Roles.Jobs;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Antag;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.GameTicking;
|
||||
@@ -20,7 +19,6 @@ using Content.Shared.Ghost;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Server.Audio;
|
||||
@@ -37,14 +35,14 @@ namespace Content.Server.Antag;
|
||||
|
||||
public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelectionComponent>
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IServerPreferencesManager _pref = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly GhostRoleSystem _ghostRole = default!;
|
||||
[Dependency] private readonly JobSystem _jobs = default!;
|
||||
[Dependency] private readonly LoadoutSystem _loadout = default!;
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IServerPreferencesManager _pref = default!;
|
||||
[Dependency] private readonly RoleSystem _role = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
@@ -193,6 +191,9 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
||||
/// <summary>
|
||||
/// Chooses antagonists from the given selection of players
|
||||
/// </summary>
|
||||
/// <param name="ent">The antagonist rule entity</param>
|
||||
/// <param name="pool">The players to choose from</param>
|
||||
/// <param name="midround">Disable picking players for pre-spawn antags in the middle of a round</param>
|
||||
public void ChooseAntags(Entity<AntagSelectionComponent> ent, IList<ICommonSession> pool, bool midround = false)
|
||||
{
|
||||
if (ent.Comp.SelectionsComplete)
|
||||
@@ -209,8 +210,14 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
||||
/// <summary>
|
||||
/// Chooses antagonists from the given selection of players for the given antag definition.
|
||||
/// </summary>
|
||||
/// <param name="ent">The antagonist rule entity</param>
|
||||
/// <param name="pool">The players to choose from</param>
|
||||
/// <param name="def">The antagonist selection parameters and criteria</param>
|
||||
/// <param name="midround">Disable picking players for pre-spawn antags in the middle of a round</param>
|
||||
public void ChooseAntags(Entity<AntagSelectionComponent> ent, IList<ICommonSession> pool, AntagSelectionDefinition def, bool midround = false)
|
||||
public void ChooseAntags(Entity<AntagSelectionComponent> ent,
|
||||
IList<ICommonSession> pool,
|
||||
AntagSelectionDefinition def,
|
||||
bool midround = false)
|
||||
{
|
||||
var playerPool = GetPlayerPool(ent, pool, def);
|
||||
var count = GetTargetAntagCount(ent, GetTotalPlayerCount(pool), def);
|
||||
@@ -331,7 +338,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
||||
EntityManager.AddComponents(player, def.Components);
|
||||
|
||||
// Equip the entity's RoleLoadout and LoadoutGroup
|
||||
List<ProtoId<StartingGearPrototype>>? gear = new();
|
||||
List<ProtoId<StartingGearPrototype>> gear = new();
|
||||
if (def.StartingGear is not null)
|
||||
gear.Add(def.StartingGear.Value);
|
||||
|
||||
@@ -340,8 +347,8 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
||||
if (session != null)
|
||||
{
|
||||
var curMind = session.GetMind();
|
||||
|
||||
if (curMind == null ||
|
||||
|
||||
if (curMind == null ||
|
||||
!TryComp<MindComponent>(curMind.Value, out var mindComp) ||
|
||||
mindComp.OwnedEntity != antagEnt)
|
||||
{
|
||||
@@ -350,7 +357,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
||||
}
|
||||
|
||||
_mind.TransferTo(curMind.Value, antagEnt, ghostCheckOverride: true);
|
||||
_role.MindAddRoles(curMind.Value, def.MindComponents, null, true);
|
||||
_role.MindAddRoles(curMind.Value, def.MindRoles, null, true);
|
||||
ent.Comp.SelectedMinds.Add((curMind.Value, Name(player)));
|
||||
SendBriefing(session, def.Briefing);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Shared.Antag;
|
||||
using Content.Shared.Destructible.Thresholds;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
@@ -145,10 +144,17 @@ public partial struct AntagSelectionDefinition()
|
||||
|
||||
/// <summary>
|
||||
/// Components added to the player's mind.
|
||||
/// Do NOT use this to add role-type components. Add those as MindRoles instead
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ComponentRegistry MindComponents = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of Mind Role Prototypes to be added to the player's mind.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<EntityPrototype>>? MindRoles;
|
||||
|
||||
/// <summary>
|
||||
/// A set of starting gear that's equipped to the player.
|
||||
/// </summary>
|
||||
|
||||
@@ -239,7 +239,7 @@ public sealed class CryostorageSystem : SharedCryostorageSystem
|
||||
Loc.GetString(
|
||||
"earlyleave-cryo-announcement",
|
||||
("character", name),
|
||||
("entity", ent.Owner),
|
||||
("entity", ent.Owner), // gender things for supporting downstreams with other languages
|
||||
("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName))
|
||||
), Loc.GetString("earlyleave-cryo-sender"),
|
||||
playDefaultSound: false
|
||||
|
||||
@@ -420,6 +420,13 @@ public sealed partial class CargoSystem
|
||||
return false;
|
||||
|
||||
_nameIdentifier.GenerateUniqueName(uid, BountyNameIdentifierGroup, out var randomVal);
|
||||
var newBounty = new CargoBountyData(bounty, randomVal);
|
||||
// This bounty id already exists! Probably because NameIdentifierSystem ran out of ids.
|
||||
if (component.Bounties.Any(b => b.Id == newBounty.Id))
|
||||
{
|
||||
Log.Error("Failed to add bounty {ID} because another one with the same ID already existed!", newBounty.Id);
|
||||
return false;
|
||||
}
|
||||
component.Bounties.Add(new CargoBountyData(bounty, randomVal));
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low, $"Added bounty \"{bounty.ID}\" (id:{component.TotalBounties}) to station {ToPrettyString(uid)}");
|
||||
component.TotalBounties++;
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace Content.Server.Cloning
|
||||
|
||||
// TODO: Ideally, components like this should be components on the mind entity so this isn't necessary.
|
||||
// Add on special job components to the mob.
|
||||
if (_jobs.MindTryGetJob(mindEnt, out _, out var prototype))
|
||||
if (_jobs.MindTryGetJob(mindEnt, out var prototype))
|
||||
{
|
||||
foreach (var special in prototype.Special)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
||||
|
||||
[DataDefinition]
|
||||
public sealed partial class TimerStartBehavior : IThresholdBehavior
|
||||
{
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
|
||||
{
|
||||
system.TriggerSystem.StartTimer(owner, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,45 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Localizations;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Content.Shared.Station;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.EntityEffects.EffectConditions;
|
||||
|
||||
public sealed partial class JobCondition : EntityEffectCondition
|
||||
{
|
||||
[DataField(required: true)] public List<ProtoId<JobPrototype>> Job;
|
||||
|
||||
|
||||
public override bool Condition(EntityEffectBaseArgs args)
|
||||
{
|
||||
{
|
||||
args.EntityManager.TryGetComponent<MindContainerComponent>(args.TargetEntity, out var mindContainer);
|
||||
if (mindContainer != null && mindContainer.Mind != null)
|
||||
|
||||
if ( mindContainer is null
|
||||
|| !args.EntityManager.TryGetComponent<MindComponent>(mindContainer.Mind, out var mind))
|
||||
return false;
|
||||
|
||||
foreach (var roleId in mind.MindRoles)
|
||||
{
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
if (args.EntityManager.TryGetComponent<JobComponent>(mindContainer?.Mind, out var comp) && prototypeManager.TryIndex(comp.Prototype, out var prototype))
|
||||
{
|
||||
foreach (var jobId in Job)
|
||||
{
|
||||
if (prototype.ID == jobId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!args.EntityManager.HasComponent<JobRoleComponent>(roleId))
|
||||
continue;
|
||||
|
||||
if(!args.EntityManager.TryGetComponent<MindRoleComponent>(roleId, out var mindRole)
|
||||
|| mindRole.JobPrototype is null)
|
||||
continue;
|
||||
|
||||
if (Job.Contains(mindRole.JobPrototype.Value))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public override string GuidebookExplanation(IPrototypeManager prototype)
|
||||
{
|
||||
var localizedNames = Job.Select(jobId => prototype.Index(jobId).LocalizedName).ToList();
|
||||
return Loc.GetString("reagent-effect-condition-guidebook-job-condition", ("job", ContentLocalizationManager.FormatListToOr(localizedNames)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Explosion.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a trigger when signal is received.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TimerStartOnSignalComponent : Component
|
||||
{
|
||||
[DataField("port", customTypeSerializer: typeof(PrototypeIdSerializer<SinkPortPrototype>))]
|
||||
public string Port = "Timer";
|
||||
}
|
||||
}
|
||||
@@ -488,9 +488,12 @@ public sealed partial class ExplosionSystem
|
||||
&& physics.BodyType == BodyType.Dynamic)
|
||||
{
|
||||
var pos = _transformSystem.GetWorldPosition(xform);
|
||||
var dir = pos - epicenter.Position;
|
||||
if (dir.IsLengthZero())
|
||||
dir = _robustRandom.NextVector2().Normalized();
|
||||
_throwingSystem.TryThrow(
|
||||
uid,
|
||||
pos - epicenter.Position,
|
||||
dir,
|
||||
physics,
|
||||
xform,
|
||||
_projectileQuery,
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Content.Server.Explosion.EntitySystems
|
||||
{
|
||||
SubscribeLocalEvent<TriggerOnSignalComponent,SignalReceivedEvent>(OnSignalReceived);
|
||||
SubscribeLocalEvent<TriggerOnSignalComponent,ComponentInit>(OnInit);
|
||||
|
||||
SubscribeLocalEvent<TimerStartOnSignalComponent,SignalReceivedEvent>(OnTimerSignalReceived);
|
||||
SubscribeLocalEvent<TimerStartOnSignalComponent,ComponentInit>(OnTimerSignalInit);
|
||||
}
|
||||
|
||||
private void OnSignalReceived(EntityUid uid, TriggerOnSignalComponent component, ref SignalReceivedEvent args)
|
||||
@@ -24,5 +27,17 @@ namespace Content.Server.Explosion.EntitySystems
|
||||
{
|
||||
_signalSystem.EnsureSinkPorts(uid, component.Port);
|
||||
}
|
||||
|
||||
private void OnTimerSignalReceived(EntityUid uid, TimerStartOnSignalComponent component, ref SignalReceivedEvent args)
|
||||
{
|
||||
if (args.Port != component.Port)
|
||||
return;
|
||||
|
||||
StartTimer(uid, args.Trigger);
|
||||
}
|
||||
private void OnTimerSignalInit(EntityUid uid, TimerStartOnSignalComponent component, ComponentInit args)
|
||||
{
|
||||
_signalSystem.EnsureSinkPorts(uid, component.Port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Server.Discord;
|
||||
using Content.Server.GameTicking.Events;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.GameTicking;
|
||||
@@ -26,6 +27,7 @@ namespace Content.Server.GameTicking
|
||||
public sealed partial class GameTicker
|
||||
{
|
||||
[Dependency] private readonly DiscordWebhook _discord = default!;
|
||||
[Dependency] private readonly RoleSystem _role = default!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = default!;
|
||||
|
||||
private static readonly Counter RoundNumberMetric = Metrics.CreateCounter(
|
||||
@@ -339,8 +341,23 @@ namespace Content.Server.GameTicking
|
||||
|
||||
RunLevel = GameRunLevel.PostRound;
|
||||
|
||||
ShowRoundEndScoreboard(text);
|
||||
SendRoundEndDiscordMessage();
|
||||
try
|
||||
{
|
||||
ShowRoundEndScoreboard(text);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error while showing round end scoreboard: {e}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SendRoundEndDiscordMessage();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error while sending round end Discord message: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowRoundEndScoreboard(string text = "")
|
||||
@@ -373,7 +390,7 @@ namespace Content.Server.GameTicking
|
||||
var userId = mind.UserId ?? mind.OriginalOwnerUserId;
|
||||
|
||||
var connected = false;
|
||||
var observer = HasComp<ObserverRoleComponent>(mindId);
|
||||
var observer = _role.MindHasRole<ObserverRoleComponent>(mindId);
|
||||
// Continuing
|
||||
if (userId != null && _playerManager.ValidSessionId(userId.Value))
|
||||
{
|
||||
@@ -400,7 +417,7 @@ namespace Content.Server.GameTicking
|
||||
_pvsOverride.AddGlobalOverride(GetNetEntity(entity.Value), recursive: true);
|
||||
}
|
||||
|
||||
var roles = _roles.MindGetAllRoles(mindId);
|
||||
var roles = _roles.MindGetAllRoleInfo(mindId);
|
||||
|
||||
var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo()
|
||||
{
|
||||
|
||||
@@ -3,8 +3,6 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.GameTicking.Events;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Spawners.Components;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Station.Components;
|
||||
@@ -224,13 +222,12 @@ namespace Content.Server.GameTicking
|
||||
_mind.SetUserId(newMind, data.UserId);
|
||||
|
||||
var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId);
|
||||
var job = new JobComponent {Prototype = jobId};
|
||||
_roles.MindAddRole(newMind, job, silent: silent);
|
||||
_roles.MindAddJobRole(newMind, silent: silent, jobPrototype:jobId);
|
||||
var jobName = _jobs.MindTryGetJobName(newMind);
|
||||
|
||||
_playTimeTrackings.PlayerRolesChanged(player);
|
||||
|
||||
var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, job, character);
|
||||
var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, jobId, character);
|
||||
DebugTools.AssertNotNull(mobMaybe);
|
||||
var mob = mobMaybe!.Value;
|
||||
|
||||
@@ -271,13 +268,17 @@ namespace Content.Server.GameTicking
|
||||
_stationJobs.TryAssignJob(station, jobPrototype, player.UserId);
|
||||
|
||||
if (lateJoin)
|
||||
{
|
||||
_adminLogger.Add(LogType.LateJoin,
|
||||
LogImpact.Medium,
|
||||
$"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminLogger.Add(LogType.RoundStartJoin,
|
||||
LogImpact.Medium,
|
||||
$"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
|
||||
}
|
||||
|
||||
// Make sure they're aware of extended access.
|
||||
if (Comp<StationJobsComponent>(station).ExtendedAccess
|
||||
@@ -363,7 +364,7 @@ namespace Content.Server.GameTicking
|
||||
var (mindId, mindComp) = _mind.CreateMind(player.UserId, name);
|
||||
mind = (mindId, mindComp);
|
||||
_mind.SetUserId(mind.Value, player.UserId);
|
||||
_roles.MindAddRole(mind.Value, new ObserverRoleComponent());
|
||||
_roles.MindAddRole(mind.Value, "MindRoleObserver");
|
||||
}
|
||||
|
||||
var ghost = _ghost.SpawnGhost(mind.Value);
|
||||
|
||||
@@ -9,7 +9,6 @@ using Content.Server.RoundEnd;
|
||||
using Content.Server.Shuttles.Events;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Store.Components;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Mobs;
|
||||
@@ -31,9 +30,9 @@ namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly EmergencyShuttleSystem _emergency = default!;
|
||||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
|
||||
[Dependency] private readonly StoreSystem _store = default!;
|
||||
@@ -57,6 +56,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
SubscribeLocalEvent<NukeOperativeComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<NukeOperativeComponent, EntityZombifiedEvent>(OnOperativeZombified);
|
||||
|
||||
SubscribeLocalEvent<NukeopsRoleComponent, GetBriefingEvent>(OnGetBriefing);
|
||||
|
||||
SubscribeLocalEvent<ConsoleFTLAttemptEvent>(OnShuttleFTLAttempt);
|
||||
SubscribeLocalEvent<WarDeclaredEvent>(OnWarDeclared);
|
||||
SubscribeLocalEvent<CommunicationConsoleCallShuttleAttemptEvent>(OnShuttleCallAttempt);
|
||||
@@ -65,7 +66,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
SubscribeLocalEvent<NukeopsRuleComponent, RuleLoadedGridsEvent>(OnRuleLoadedGrids);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule,
|
||||
protected override void Started(EntityUid uid,
|
||||
NukeopsRuleComponent component,
|
||||
GameRuleComponent gameRule,
|
||||
GameRuleStartedEvent args)
|
||||
{
|
||||
var eligible = new List<Entity<StationEventEligibleComponent, NpcFactionMemberComponent>>();
|
||||
@@ -85,7 +88,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
protected override void AppendRoundEndText(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule,
|
||||
protected override void AppendRoundEndText(EntityUid uid,
|
||||
NukeopsRuleComponent component,
|
||||
GameRuleComponent gameRule,
|
||||
ref RoundEndTextAppendEvent args)
|
||||
{
|
||||
var winText = Loc.GetString($"nukeops-{component.WinType.ToString().ToLower()}");
|
||||
@@ -227,7 +232,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
|
||||
// If the disk is currently at Central Command, the crew wins - just slightly.
|
||||
// This also implies that some nuclear operatives have died.
|
||||
SetWinType(ent, diskAtCentCom
|
||||
SetWinType(ent,
|
||||
diskAtCentCom
|
||||
? WinType.CrewMinor
|
||||
: WinType.OpsMinor);
|
||||
ent.Comp.WinConditions.Add(diskAtCentCom
|
||||
@@ -456,8 +462,11 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
: WinCondition.AllNukiesDead);
|
||||
|
||||
SetWinType(ent, WinType.CrewMajor, false);
|
||||
_roundEndSystem.DoRoundEndBehavior(
|
||||
nukeops.RoundEndBehavior, nukeops.EvacShuttleTime, nukeops.RoundEndTextSender, nukeops.RoundEndTextShuttleCall, nukeops.RoundEndTextAnnouncement);
|
||||
_roundEndSystem.DoRoundEndBehavior(nukeops.RoundEndBehavior,
|
||||
nukeops.EvacShuttleTime,
|
||||
nukeops.RoundEndTextSender,
|
||||
nukeops.RoundEndTextShuttleCall,
|
||||
nukeops.RoundEndTextAnnouncement);
|
||||
|
||||
// prevent it called multiple times
|
||||
nukeops.RoundEndBehavior = RoundEndBehavior.Nothing;
|
||||
@@ -465,16 +474,22 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
|
||||
private void OnAfterAntagEntSelected(Entity<NukeopsRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
{
|
||||
if (ent.Comp.TargetStation is not { } station)
|
||||
return;
|
||||
var target = (ent.Comp.TargetStation is not null) ? Name(ent.Comp.TargetStation.Value) : "the target";
|
||||
|
||||
_antag.SendBriefing(args.Session, Loc.GetString("nukeops-welcome",
|
||||
("station", station),
|
||||
_antag.SendBriefing(args.Session,
|
||||
Loc.GetString("nukeops-welcome",
|
||||
("station", target),
|
||||
("name", Name(ent))),
|
||||
Color.Red,
|
||||
ent.Comp.GreetSoundNotification);
|
||||
}
|
||||
|
||||
private void OnGetBriefing(Entity<NukeopsRoleComponent> role, ref GetBriefingEvent args)
|
||||
{
|
||||
// TODO Different character screen briefing for the 3 nukie types
|
||||
args.Append(Loc.GetString("nukeops-briefing"));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Is this method the shitty glue holding together the last of my sanity? yes.
|
||||
/// Do i have a better solution? not presently.
|
||||
|
||||
@@ -15,7 +15,6 @@ using Content.Shared.Database;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Mobs;
|
||||
@@ -38,8 +37,8 @@ namespace Content.Server.GameTicking.Rules;
|
||||
public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
|
||||
[Dependency] private readonly EuiManager _euiMan = default!;
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
@@ -49,7 +48,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
//Used in OnPostFlash, no reference to the rule component is available
|
||||
public readonly ProtoId<NpcFactionPrototype> RevolutionaryNpcFaction = "Revolutionary";
|
||||
@@ -59,9 +58,12 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<CommandStaffComponent, MobStateChangedEvent>(OnCommandMobStateChanged);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, MobStateChangedEvent>(OnHeadRevMobStateChanged);
|
||||
SubscribeLocalEvent<RevolutionaryRoleComponent, GetBriefingEvent>(OnGetBriefing);
|
||||
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, AfterFlashedEvent>(OnPostFlash);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, MobStateChangedEvent>(OnHeadRevMobStateChanged);
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryRoleComponent, GetBriefingEvent>(OnGetBriefing);
|
||||
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
@@ -85,7 +87,9 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AppendRoundEndText(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule,
|
||||
protected override void AppendRoundEndText(EntityUid uid,
|
||||
RevolutionaryRuleComponent component,
|
||||
GameRuleComponent gameRule,
|
||||
ref RoundEndTextAppendEvent args)
|
||||
{
|
||||
base.AppendRoundEndText(uid, component, gameRule, ref args);
|
||||
@@ -101,7 +105,9 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
args.AddLine(Loc.GetString("rev-headrev-count", ("initialCount", sessionData.Count)));
|
||||
foreach (var (mind, data, name) in sessionData)
|
||||
{
|
||||
var count = CompOrNull<RevolutionaryRoleComponent>(mind)?.ConvertedCount ?? 0;
|
||||
_role.MindHasRole<RevolutionaryRoleComponent>(mind, out var role);
|
||||
var count = CompOrNull<RevolutionaryRoleComponent>(role)?.ConvertedCount ?? 0;
|
||||
|
||||
args.AddLine(Loc.GetString("rev-headrev-name-user",
|
||||
("name", name),
|
||||
("username", data.UserName),
|
||||
@@ -113,10 +119,8 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
|
||||
private void OnGetBriefing(EntityUid uid, RevolutionaryRoleComponent comp, ref GetBriefingEvent args)
|
||||
{
|
||||
if (!TryComp<MindComponent>(uid, out var mind) || mind.OwnedEntity == null)
|
||||
return;
|
||||
|
||||
var head = HasComp<HeadRevolutionaryComponent>(mind.OwnedEntity);
|
||||
var ent = args.Mind.Comp.OwnedEntity;
|
||||
var head = HasComp<HeadRevolutionaryComponent>(ent);
|
||||
args.Append(Loc.GetString(head ? "head-rev-briefing" : "rev-briefing"));
|
||||
}
|
||||
|
||||
@@ -145,15 +149,20 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
|
||||
if (ev.User != null)
|
||||
{
|
||||
_adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(ev.User.Value)} converted {ToPrettyString(ev.Target)} into a Revolutionary");
|
||||
_adminLogManager.Add(LogType.Mind,
|
||||
LogImpact.Medium,
|
||||
$"{ToPrettyString(ev.User.Value)} converted {ToPrettyString(ev.Target)} into a Revolutionary");
|
||||
|
||||
if (_mind.TryGetRole<RevolutionaryRoleComponent>(ev.User.Value, out var headrev))
|
||||
headrev.ConvertedCount++;
|
||||
if (_mind.TryGetMind(ev.User.Value, out var revMindId, out _))
|
||||
{
|
||||
if (_role.MindHasRole<RevolutionaryRoleComponent>(revMindId, out _, out var role))
|
||||
role.Value.Comp.ConvertedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (mindId == default || !_role.MindHasRole<RevolutionaryRoleComponent>(mindId))
|
||||
{
|
||||
_role.MindAddRole(mindId, new RevolutionaryRoleComponent { PrototypeId = RevPrototypeId });
|
||||
_role.MindAddRole(mindId, "MindRoleRevolutionary");
|
||||
}
|
||||
|
||||
if (mind?.Session != null)
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -24,32 +18,33 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
|
||||
SubscribeLocalEvent<ThiefRoleComponent, GetBriefingEvent>(OnGetBriefing);
|
||||
}
|
||||
|
||||
private void AfterAntagSelected(Entity<ThiefRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
// Greeting upon thief activation
|
||||
private void AfterAntagSelected(Entity<ThiefRuleComponent> mindId, ref AfterAntagEntitySelectedEvent args)
|
||||
{
|
||||
if (!_mindSystem.TryGetMind(args.EntityUid, out var mindId, out var mind))
|
||||
return;
|
||||
|
||||
//Generate objectives
|
||||
_antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null);
|
||||
var ent = args.EntityUid;
|
||||
_antag.SendBriefing(ent, MakeBriefing(ent), null, null);
|
||||
}
|
||||
|
||||
//Add mind briefing
|
||||
private void OnGetBriefing(Entity<ThiefRoleComponent> thief, ref GetBriefingEvent args)
|
||||
// Character screen briefing
|
||||
private void OnGetBriefing(Entity<ThiefRoleComponent> role, ref GetBriefingEvent args)
|
||||
{
|
||||
if (!TryComp<MindComponent>(thief.Owner, out var mind) || mind.OwnedEntity == null)
|
||||
return;
|
||||
var ent = args.Mind.Comp.OwnedEntity;
|
||||
|
||||
args.Append(MakeBriefing(mind.OwnedEntity.Value));
|
||||
if (ent is null)
|
||||
return;
|
||||
args.Append(MakeBriefing(ent.Value));
|
||||
}
|
||||
|
||||
private string MakeBriefing(EntityUid thief)
|
||||
private string MakeBriefing(EntityUid ent)
|
||||
{
|
||||
var isHuman = HasComp<HumanoidAppearanceComponent>(thief);
|
||||
var isHuman = HasComp<HumanoidAppearanceComponent>(ent);
|
||||
var briefing = isHuman
|
||||
? Loc.GetString("thief-role-greeting-human")
|
||||
: Loc.GetString("thief-role-greeting-animal");
|
||||
|
||||
briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
|
||||
if (isHuman)
|
||||
briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
|
||||
|
||||
return briefing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Mind;
|
||||
@@ -5,12 +6,11 @@ using Content.Server.Objectives;
|
||||
using Content.Server.PDA.Ringer;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Traitor.Uplink;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Content.Shared.Roles.RoleCodeword;
|
||||
@@ -25,29 +25,29 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
{
|
||||
private static readonly Color TraitorCodewordColor = Color.FromHex("#cc3b3b");
|
||||
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly SharedJobSystem _jobs = default!;
|
||||
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly UplinkSystem _uplink = default!;
|
||||
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;
|
||||
[Dependency] private readonly SharedJobSystem _jobs = default!;
|
||||
[Dependency] private readonly SharedRoleCodewordSystem _roleCodewordSystem = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;
|
||||
[Dependency] private readonly UplinkSystem _uplink = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TraitorRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
|
||||
|
||||
SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextPrependEvent>(OnObjectivesTextPrepend);
|
||||
}
|
||||
|
||||
protected override void Added(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
|
||||
{
|
||||
base.Added(uid, component, gameRule, args);
|
||||
SetCodewords(component);
|
||||
SetCodewords(component, args.RuleEntity);
|
||||
}
|
||||
|
||||
private void AfterEntitySelected(Entity<TraitorRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
@@ -55,9 +55,10 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
MakeTraitor(args.EntityUid, ent);
|
||||
}
|
||||
|
||||
private void SetCodewords(TraitorRuleComponent component)
|
||||
private void SetCodewords(TraitorRuleComponent component, EntityUid ruleEntity)
|
||||
{
|
||||
component.Codewords = GenerateTraitorCodewords(component);
|
||||
_adminLogger.Add(LogType.EventStarted, LogImpact.Low, $"Codewords generated for game rule {ToPrettyString(ruleEntity)}: {string.Join(", ", component.Codewords)}");
|
||||
}
|
||||
|
||||
public string[] GenerateTraitorCodewords(TraitorRuleComponent component)
|
||||
@@ -76,7 +77,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
|
||||
public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true)
|
||||
{
|
||||
//Grab the mind if it wasnt provided
|
||||
//Grab the mind if it wasn't provided
|
||||
if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
|
||||
return false;
|
||||
|
||||
@@ -88,7 +89,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
{
|
||||
// Calculate the amount of currency on the uplink.
|
||||
var startingBalance = component.StartingBalance;
|
||||
if (_jobs.MindTryGetJob(mindId, out _, out var prototype))
|
||||
if (_jobs.MindTryGetJob(mindId, out var prototype))
|
||||
startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0);
|
||||
|
||||
// creadth: we need to create uplink for the antag.
|
||||
@@ -107,14 +108,18 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
|
||||
_antag.SendBriefing(traitor, GenerateBriefing(component.Codewords, code, issuer), null, component.GreetSoundNotification);
|
||||
|
||||
|
||||
component.TraitorMinds.Add(mindId);
|
||||
|
||||
// Assign briefing
|
||||
_roleSystem.MindAddRole(mindId, new RoleBriefingComponent
|
||||
//Since this provides neither an antag/job prototype, nor antag status/roletype,
|
||||
//and is intrinsically related to the traitor role
|
||||
//it does not need to be a separate Mind Role Entity
|
||||
_roleSystem.MindHasRole<TraitorRoleComponent>(mindId, out var traitorRole);
|
||||
if (traitorRole is not null)
|
||||
{
|
||||
Briefing = briefing
|
||||
}, mind, true);
|
||||
AddComp<RoleBriefingComponent>(traitorRole.Value.Owner);
|
||||
Comp<RoleBriefingComponent>(traitorRole.Value.Owner).Briefing = briefing;
|
||||
}
|
||||
|
||||
// Send codewords to only the traitor client
|
||||
var color = TraitorCodewordColor; // Fall back to a dark red Syndicate color if a prototype is not found
|
||||
|
||||
@@ -13,6 +13,7 @@ using Content.Shared.Mind;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -22,15 +23,16 @@ namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly ZombieSystem _zombie = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ZombieSystem _zombie = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -41,23 +43,20 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
||||
SubscribeLocalEvent<IncurableZombieComponent, ZombifySelfActionEvent>(OnZombifySelf);
|
||||
}
|
||||
|
||||
private void OnGetBriefing(EntityUid uid, InitialInfectedRoleComponent component, ref GetBriefingEvent args)
|
||||
private void OnGetBriefing(Entity<InitialInfectedRoleComponent> role, ref GetBriefingEvent args)
|
||||
{
|
||||
if (!TryComp<MindComponent>(uid, out var mind) || mind.OwnedEntity == null)
|
||||
return;
|
||||
if (HasComp<ZombieRoleComponent>(uid)) // don't show both briefings
|
||||
return;
|
||||
args.Append(Loc.GetString("zombie-patientzero-role-greeting"));
|
||||
if (!_roles.MindHasRole<ZombieRoleComponent>(args.Mind.Owner))
|
||||
args.Append(Loc.GetString("zombie-patientzero-role-greeting"));
|
||||
}
|
||||
|
||||
private void OnGetBriefing(EntityUid uid, ZombieRoleComponent component, ref GetBriefingEvent args)
|
||||
private void OnGetBriefing(Entity<ZombieRoleComponent> role, ref GetBriefingEvent args)
|
||||
{
|
||||
if (!TryComp<MindComponent>(uid, out var mind) || mind.OwnedEntity == null)
|
||||
return;
|
||||
args.Append(Loc.GetString("zombie-infection-greeting"));
|
||||
}
|
||||
|
||||
protected override void AppendRoundEndText(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule,
|
||||
protected override void AppendRoundEndText(EntityUid uid,
|
||||
ZombieRuleComponent component,
|
||||
GameRuleComponent gameRule,
|
||||
ref RoundEndTextAppendEvent args)
|
||||
{
|
||||
base.AppendRoundEndText(uid, component, gameRule, ref args);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
namespace Content.Server.Ghost.Roles;
|
||||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Ghost.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for round end display of ghost roles.
|
||||
/// It may also be used to ensure some ghost roles count as antagonists in future.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class GhostRoleMarkerRoleComponent : Component
|
||||
public sealed partial class GhostRoleMarkerRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
[DataField("name")] public string? Name;
|
||||
}
|
||||
|
||||
@@ -71,18 +71,22 @@ public sealed class GhostRoleSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
|
||||
|
||||
SubscribeLocalEvent<GhostTakeoverAvailableComponent, MindAddedMessage>(OnMindAdded);
|
||||
SubscribeLocalEvent<GhostTakeoverAvailableComponent, MindRemovedMessage>(OnMindRemoved);
|
||||
SubscribeLocalEvent<GhostTakeoverAvailableComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
|
||||
|
||||
SubscribeLocalEvent<GhostRoleComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<GhostRoleComponent, ComponentStartup>(OnRoleStartup);
|
||||
SubscribeLocalEvent<GhostRoleComponent, ComponentShutdown>(OnRoleShutdown);
|
||||
SubscribeLocalEvent<GhostRoleComponent, EntityPausedEvent>(OnPaused);
|
||||
SubscribeLocalEvent<GhostRoleComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
|
||||
SubscribeLocalEvent<GhostRoleRaffleComponent, ComponentInit>(OnRaffleInit);
|
||||
SubscribeLocalEvent<GhostRoleRaffleComponent, ComponentShutdown>(OnRaffleShutdown);
|
||||
|
||||
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, TakeGhostRoleEvent>(OnSpawnerTakeRole);
|
||||
SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
|
||||
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, GetVerbsEvent<Verb>>(OnVerb);
|
||||
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, GhostRoleRadioMessage>(OnGhostRoleRadioMessage);
|
||||
_playerManager.PlayerStatusChanged += PlayerStatusChanged;
|
||||
@@ -509,7 +513,11 @@ public sealed class GhostRoleSystem : EntitySystem
|
||||
|
||||
var newMind = _mindSystem.CreateMind(player.UserId,
|
||||
EntityManager.GetComponent<MetaDataComponent>(mob).EntityName);
|
||||
_roleSystem.MindAddRole(newMind, new GhostRoleMarkerRoleComponent { Name = role.RoleName });
|
||||
|
||||
_roleSystem.MindAddRole(newMind, "MindRoleGhostMarker");
|
||||
|
||||
if(_roleSystem.MindHasRole<GhostRoleMarkerRoleComponent>(newMind, out _, out var markerRole))
|
||||
markerRole.Value.Comp.Name = role.RoleName;
|
||||
|
||||
_mindSystem.SetUserId(newMind, player.UserId);
|
||||
_mindSystem.TransferTo(newMind, mob);
|
||||
@@ -602,10 +610,7 @@ public sealed class GhostRoleSystem : EntitySystem
|
||||
|
||||
if (ghostRole.JobProto != null)
|
||||
{
|
||||
if (HasComp<JobComponent>(args.Mind))
|
||||
_roleSystem.MindRemoveRole<JobComponent>(args.Mind);
|
||||
|
||||
_roleSystem.MindAddRole(args.Mind, new JobComponent { Prototype = ghostRole.JobProto });
|
||||
_roleSystem.MindAddJobRole(args.Mind, args.Mind, silent:false,ghostRole.JobProto);
|
||||
}
|
||||
|
||||
ghostRole.Taken = true;
|
||||
|
||||
@@ -166,7 +166,7 @@ public sealed class IdentitySystem : SharedIdentitySystem
|
||||
if (_idCard.TryFindIdCard(target, out var id))
|
||||
{
|
||||
presumedName = string.IsNullOrWhiteSpace(id.Comp.FullName) ? null : id.Comp.FullName;
|
||||
presumedJob = id.Comp.JobTitle?.ToLowerInvariant();
|
||||
presumedJob = id.Comp.LocalizedJobTitle?.ToLowerInvariant();
|
||||
}
|
||||
|
||||
// If it didn't find a job, that's fine.
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Stack;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Kitchen;
|
||||
@@ -122,6 +123,9 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
if (solution.Volume > containerSolution.AvailableVolume)
|
||||
continue;
|
||||
|
||||
var dev = new DestructionEventArgs();
|
||||
RaiseLocalEvent(item, dev);
|
||||
|
||||
QueueDel(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,10 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
|
||||
var materialPerStack = composition.MaterialComposition[materialProto.ID];
|
||||
var amountToSpawn = amount / materialPerStack;
|
||||
overflowMaterial = amount - amountToSpawn * materialPerStack;
|
||||
|
||||
if (amountToSpawn == 0)
|
||||
return new List<EntityUid>();
|
||||
|
||||
return _stackSystem.SpawnMultiple(materialProto.StackEntity, amountToSpawn, coordinates);
|
||||
}
|
||||
|
||||
|
||||
@@ -363,8 +363,8 @@ public sealed class SuitSensorSystem : EntitySystem
|
||||
{
|
||||
if (card.Comp.FullName != null)
|
||||
userName = card.Comp.FullName;
|
||||
if (card.Comp.JobTitle != null)
|
||||
userJob = card.Comp.JobTitle;
|
||||
if (card.Comp.LocalizedJobTitle != null)
|
||||
userJob = card.Comp.LocalizedJobTitle;
|
||||
userJobIcon = card.Comp.JobIcon;
|
||||
|
||||
foreach (var department in card.Comp.JobDepartments)
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Content.Server.Mind.Commands
|
||||
builder.AppendFormat("player: {0}, mob: {1}\nroles: ", mind.UserId, mind.OwnedEntity);
|
||||
|
||||
var roles = _entities.System<SharedRoleSystem>();
|
||||
foreach (var role in roles.MindGetAllRoles(mindId))
|
||||
foreach (var role in roles.MindGetAllRoleInfo(mindId))
|
||||
{
|
||||
builder.AppendFormat("{0} ", role.Name);
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared.Movement.Systems;
|
||||
|
||||
namespace Content.Server.Movement.Systems;
|
||||
|
||||
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem;
|
||||
@@ -14,6 +14,7 @@ public sealed class NameIdentifierSystem : EntitySystem
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Free IDs available per <see cref="NameIdentifierGroupPrototype"/>.
|
||||
@@ -118,23 +119,39 @@ public sealed class NameIdentifierSystem : EntitySystem
|
||||
|
||||
private void InitialSetupPrototypes()
|
||||
{
|
||||
foreach (var proto in _prototypeManager.EnumeratePrototypes<NameIdentifierGroupPrototype>())
|
||||
{
|
||||
AddGroup(proto);
|
||||
}
|
||||
EnsureIds();
|
||||
}
|
||||
|
||||
private void AddGroup(NameIdentifierGroupPrototype proto)
|
||||
private void FillGroup(NameIdentifierGroupPrototype proto, List<int> values)
|
||||
{
|
||||
var values = new List<int>(proto.MaxValue - proto.MinValue);
|
||||
|
||||
values.Clear();
|
||||
for (var i = proto.MinValue; i < proto.MaxValue; i++)
|
||||
{
|
||||
values.Add(i);
|
||||
}
|
||||
|
||||
_robustRandom.Shuffle(values);
|
||||
CurrentIds.Add(proto.ID, values);
|
||||
}
|
||||
|
||||
private List<int> GetOrCreateIdList(NameIdentifierGroupPrototype proto)
|
||||
{
|
||||
if (!CurrentIds.TryGetValue(proto.ID, out var ids))
|
||||
{
|
||||
ids = new List<int>(proto.MaxValue - proto.MinValue);
|
||||
CurrentIds.Add(proto.ID, ids);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
private void EnsureIds()
|
||||
{
|
||||
foreach (var proto in _prototypeManager.EnumeratePrototypes<NameIdentifierGroupPrototype>())
|
||||
{
|
||||
var ids = GetOrCreateIdList(proto);
|
||||
|
||||
FillGroup(proto, ids);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReloadPrototypes(PrototypesReloadedEventArgs ev)
|
||||
@@ -159,19 +176,20 @@ public sealed class NameIdentifierSystem : EntitySystem
|
||||
|
||||
foreach (var proto in set.Modified.Values)
|
||||
{
|
||||
var name_proto = (NameIdentifierGroupPrototype) proto;
|
||||
|
||||
// Only bother adding new ones.
|
||||
if (CurrentIds.ContainsKey(proto.ID))
|
||||
continue;
|
||||
|
||||
AddGroup((NameIdentifierGroupPrototype) proto);
|
||||
var ids = GetOrCreateIdList(name_proto);
|
||||
FillGroup(name_proto, ids);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CleanupIds(RoundRestartCleanupEvent ev)
|
||||
{
|
||||
foreach (var values in CurrentIds.Values)
|
||||
{
|
||||
_robustRandom.Shuffle(values);
|
||||
}
|
||||
EnsureIds();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ using System.Linq;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Shared.Whitelist;
|
||||
using Content.Shared.Destructible;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems;
|
||||
|
||||
@@ -335,6 +336,9 @@ public sealed class FoodSystem : EntitySystem
|
||||
if (ev.Cancelled)
|
||||
return;
|
||||
|
||||
var dev = new DestructionEventArgs();
|
||||
RaiseLocalEvent(food, dev);
|
||||
|
||||
if (component.Trash.Count == 0)
|
||||
{
|
||||
QueueDel(food);
|
||||
|
||||
@@ -89,7 +89,7 @@ public sealed class KillPersonConditionSystem : EntitySystem
|
||||
foreach (var mind in allHumans)
|
||||
{
|
||||
// RequireAdminNotify used as a cheap way to check for command department
|
||||
if (_job.MindTryGetJob(mind, out _, out var prototype) && prototype.RequireAdminNotify)
|
||||
if (_job.MindTryGetJob(mind, out var prototype) && prototype.RequireAdminNotify)
|
||||
allHeads.Add(mind);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Warps;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Random;
|
||||
using Content.Server.Roles;
|
||||
|
||||
namespace Content.Server.Objectives.Systems;
|
||||
|
||||
@@ -16,6 +17,7 @@ public sealed class NinjaConditionsSystem : EntitySystem
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly NumberObjectiveSystem _number = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -46,10 +48,8 @@ public sealed class NinjaConditionsSystem : EntitySystem
|
||||
// spider charge
|
||||
private void OnSpiderChargeRequirementCheck(EntityUid uid, SpiderChargeConditionComponent comp, ref RequirementCheckEvent args)
|
||||
{
|
||||
if (args.Cancelled || !HasComp<NinjaRoleComponent>(args.MindId))
|
||||
{
|
||||
if (args.Cancelled || !_roles.MindHasRole<NinjaRoleComponent>(args.MindId))
|
||||
return;
|
||||
}
|
||||
|
||||
// choose spider charge detonation point
|
||||
var warps = new List<EntityUid>();
|
||||
|
||||
@@ -21,7 +21,7 @@ public sealed class NotCommandRequirementSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// cheap equivalent to checking that job department is command, since all command members require admin notification when leaving
|
||||
if (_job.MindTryGetJob(args.MindId, out _, out var prototype) && prototype.RequireAdminNotify)
|
||||
if (_job.MindTryGetJob(args.MindId, out var prototype) && prototype.RequireAdminNotify)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Server.Objectives.Systems;
|
||||
/// </summary>
|
||||
public sealed class NotJobRequirementSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedJobSystem _jobs = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -20,11 +22,10 @@ public sealed class NotJobRequirementSystem : EntitySystem
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
// if player has no job then don't care
|
||||
if (!TryComp<JobComponent>(args.MindId, out var job))
|
||||
return;
|
||||
_jobs.MindTryGetJob(args.MindId, out var proto);
|
||||
|
||||
if (job.Prototype == comp.Job)
|
||||
// if player has no job then don't care
|
||||
if (proto is not null && proto.ID == comp.Job)
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ public sealed class RoleRequirementSystem : EntitySystem
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
// this whitelist trick only works because roles are components on the mind and not entities
|
||||
// if that gets reworked then this will need changing
|
||||
if (_whitelistSystem.IsWhitelistFail(comp.Roles, args.MindId))
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace Content.Server.PDA
|
||||
{
|
||||
ActualOwnerName = pda.OwnerName,
|
||||
IdOwner = id?.FullName,
|
||||
JobTitle = id?.JobTitle,
|
||||
JobTitle = id?.LocalizedJobTitle,
|
||||
StationAlertLevel = pda.StationAlertLevel,
|
||||
StationAlertColor = pda.StationAlertColor
|
||||
},
|
||||
|
||||
@@ -30,14 +30,15 @@ namespace Content.Server.Players.PlayTimeTracking;
|
||||
/// </summary>
|
||||
public sealed class PlayTimeTrackingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IAfkManager _afk = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly MindSystem _minds = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingManager _tracking = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingManager _tracking = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -101,10 +102,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
|
||||
|
||||
public IEnumerable<string> GetTimedRoles(EntityUid mindId)
|
||||
{
|
||||
var ev = new MindGetAllRolesEvent(new List<RoleInfo>());
|
||||
RaiseLocalEvent(mindId, ref ev);
|
||||
|
||||
foreach (var role in ev.Roles)
|
||||
foreach (var role in _roles.MindGetAllRoleInfo(mindId))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(role.PlayTimeTrackerId))
|
||||
continue;
|
||||
|
||||
@@ -10,3 +10,5 @@ public sealed partial class CommandStaffComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//TODO this should probably be on a mind role, not the mob
|
||||
|
||||
@@ -4,9 +4,9 @@ using Content.Shared.Roles;
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Role used to keep track of space dragons for antag purposes.
|
||||
/// Added to mind role entities to tag that they are a space dragon.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(DragonSystem)), ExclusiveAntagonist]
|
||||
public sealed partial class DragonRoleComponent : AntagonistRoleComponent
|
||||
[RegisterComponent, Access(typeof(DragonSystem))]
|
||||
public sealed partial class DragonRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@ using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
[RegisterComponent, ExclusiveAntagonist]
|
||||
public sealed partial class InitialInfectedRoleComponent : AntagonistRoleComponent
|
||||
/// <summary>
|
||||
/// Added to mind role entities to tag that they are an initial infected.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class InitialInfectedRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public sealed class JobSystem : SharedJobSystem
|
||||
if (!_mind.TryGetSession(mindId, out var session))
|
||||
return;
|
||||
|
||||
if (!MindTryGetJob(mindId, out _, out var prototype))
|
||||
if (!MindTryGetJob(mindId, out var prototype))
|
||||
return;
|
||||
|
||||
_chat.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name",
|
||||
@@ -47,6 +47,6 @@ public sealed class JobSystem : SharedJobSystem
|
||||
if (MindHasJobWithId(mindId, jobPrototypeId))
|
||||
return;
|
||||
|
||||
_roles.MindAddRole(mindId, new JobComponent { Prototype = jobPrototypeId });
|
||||
_roles.MindAddJobRole(mindId, null, false, jobPrototypeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
[RegisterComponent, ExclusiveAntagonist]
|
||||
public sealed partial class NinjaRoleComponent : AntagonistRoleComponent
|
||||
/// <summary>
|
||||
/// Added to mind role entities to tag that they are a space ninja.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class NinjaRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ using Content.Shared.Roles;
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind entities to tag that they are a nuke operative.
|
||||
/// Added to mind role entities to tag that they are a nuke operative.
|
||||
/// </summary>
|
||||
[RegisterComponent, ExclusiveAntagonist]
|
||||
public sealed partial class NukeopsRoleComponent : AntagonistRoleComponent
|
||||
[RegisterComponent]
|
||||
public sealed partial class NukeopsRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Content.Server.Roles
|
||||
var roles = _entityManager.System<SharedRoleSystem>();
|
||||
var jobs = _entityManager.System<SharedJobSystem>();
|
||||
if (jobs.MindHasJobWithId(mind, args[1]))
|
||||
roles.MindRemoveRole<JobComponent>(mind.Value);
|
||||
roles.MindTryRemoveRole<JobRoleComponent>(mind.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ using Content.Shared.Roles;
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind entities to tag that they are a Revolutionary.
|
||||
/// Added to mind role entities to tag that they are a Revolutionary.
|
||||
/// </summary>
|
||||
[RegisterComponent, ExclusiveAntagonist]
|
||||
public sealed partial class RevolutionaryRoleComponent : AntagonistRoleComponent
|
||||
[RegisterComponent]
|
||||
public sealed partial class RevolutionaryRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// For headrevs, how many people you have converted.
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
public sealed class RoleSystem : SharedRoleSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
// TODO make roles entities
|
||||
base.Initialize();
|
||||
|
||||
SubscribeAntagEvents<DragonRoleComponent>();
|
||||
SubscribeAntagEvents<InitialInfectedRoleComponent>();
|
||||
SubscribeAntagEvents<NinjaRoleComponent>();
|
||||
SubscribeAntagEvents<NukeopsRoleComponent>();
|
||||
SubscribeAntagEvents<RevolutionaryRoleComponent>();
|
||||
SubscribeAntagEvents<SubvertedSiliconRoleComponent>();
|
||||
SubscribeAntagEvents<TraitorRoleComponent>();
|
||||
SubscribeAntagEvents<ZombieRoleComponent>();
|
||||
SubscribeAntagEvents<ThiefRoleComponent>();
|
||||
}
|
||||
|
||||
public string? MindGetBriefing(EntityUid? mindId)
|
||||
{
|
||||
if (mindId == null)
|
||||
{
|
||||
Log.Error($"MingGetBriefing failed for mind {mindId}");
|
||||
return null;
|
||||
}
|
||||
|
||||
TryComp<MindComponent>(mindId.Value, out var mindComp);
|
||||
|
||||
if (mindComp is null)
|
||||
{
|
||||
Log.Error($"MingGetBriefing failed for mind {mindId}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var ev = new GetBriefingEvent();
|
||||
RaiseLocalEvent(mindId.Value, ref ev);
|
||||
|
||||
// This is on the event because while this Entity<T> is also present on every Mind Role Entity's MindRoleComp
|
||||
// getting to there from a GetBriefing event subscription can be somewhat boilerplate
|
||||
// and this needs to be looked up for the event anyway so why calculate it again later
|
||||
ev.Mind = (mindId.Value, mindComp);
|
||||
|
||||
// Briefing is no longer raised on the mind entity itself
|
||||
// because all the components that briefings subscribe to should be on Mind Role Entities
|
||||
foreach(var role in mindComp.MindRoles)
|
||||
{
|
||||
RaiseLocalEvent(role, ref ev);
|
||||
}
|
||||
|
||||
return ev.Briefing;
|
||||
}
|
||||
}
|
||||
@@ -38,8 +46,16 @@ public sealed class RoleSystem : SharedRoleSystem
|
||||
[ByRefEvent]
|
||||
public sealed class GetBriefingEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The text that will be shown on the Character Screen
|
||||
/// </summary>
|
||||
public string? Briefing;
|
||||
|
||||
/// <summary>
|
||||
/// The Mind to whose Mind Role Entities the briefing is sent to
|
||||
/// </summary>
|
||||
public Entity<MindComponent> Mind;
|
||||
|
||||
public GetBriefingEvent(string? briefing = null)
|
||||
{
|
||||
Briefing = briefing;
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind role entities to tag that they are a hacked borg.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SubvertedSiliconRoleComponent : AntagonistRoleComponent
|
||||
public sealed partial class SubvertedSiliconRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind role entities to tag that they are a thief.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ThiefRoleComponent : AntagonistRoleComponent
|
||||
public sealed partial class ThiefRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
[RegisterComponent, ExclusiveAntagonist]
|
||||
public sealed partial class TraitorRoleComponent : AntagonistRoleComponent
|
||||
/// <summary>
|
||||
/// Added to mind role entities to tag that they are a syndicate traitor.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TraitorRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
[RegisterComponent, ExclusiveAntagonist]
|
||||
public sealed partial class ZombieRoleComponent : AntagonistRoleComponent
|
||||
/// <summary>
|
||||
/// Added to mind role entities to tag that they are a zombie.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ZombieRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
||||
@@ -124,6 +124,9 @@ public sealed partial class ShuttleConsoleSystem
|
||||
if (!TryComp(shuttleUid, out ShuttleComponent? shuttleComp))
|
||||
return;
|
||||
|
||||
if (shuttleComp.Enabled == false)
|
||||
return;
|
||||
|
||||
// Check shuttle can even FTL
|
||||
if (!_shuttle.CanFTL(shuttleUid.Value, out var reason))
|
||||
{
|
||||
|
||||
@@ -244,18 +244,28 @@ public sealed partial class ShuttleSystem
|
||||
/// </summary>
|
||||
public bool CanFTL(EntityUid shuttleUid, [NotNullWhen(false)] out string? reason)
|
||||
{
|
||||
// Currently in FTL already
|
||||
if (HasComp<FTLComponent>(shuttleUid))
|
||||
{
|
||||
reason = Loc.GetString("shuttle-console-in-ftl");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FTLMassLimit > 0 &&
|
||||
TryComp(shuttleUid, out PhysicsComponent? shuttlePhysics) &&
|
||||
shuttlePhysics.Mass > FTLMassLimit)
|
||||
if (TryComp<PhysicsComponent>(shuttleUid, out var shuttlePhysics))
|
||||
{
|
||||
reason = Loc.GetString("shuttle-console-mass");
|
||||
return false;
|
||||
// Static physics type is set when station anchor is enabled
|
||||
if (shuttlePhysics.BodyType == BodyType.Static)
|
||||
{
|
||||
reason = Loc.GetString("shuttle-console-static");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Too large to FTL
|
||||
if (FTLMassLimit > 0 && shuttlePhysics.Mass > FTLMassLimit)
|
||||
{
|
||||
reason = Loc.GetString("shuttle-console-mass");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasComp<PreventPilotComponent>(shuttleUid))
|
||||
|
||||
@@ -28,12 +28,12 @@ namespace Content.Server.Silicons.Laws;
|
||||
public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
@@ -178,10 +178,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
if (component.AntagonistRole == null || !_mind.TryGetMind(uid, out var mindId, out _))
|
||||
return;
|
||||
|
||||
if (_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
|
||||
return;
|
||||
|
||||
_roles.MindAddRole(mindId, new SubvertedSiliconRoleComponent { PrototypeId = component.AntagonistRole });
|
||||
if (!_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
|
||||
_roles.MindAddRole(mindId, "MindRoleSubvertedSilicon");
|
||||
}
|
||||
|
||||
public SiliconLawset GetLaws(EntityUid uid, SiliconLawBoundComponent? component = null)
|
||||
|
||||
@@ -12,10 +12,10 @@ namespace Content.Server.Spawners.EntitySystems;
|
||||
|
||||
public sealed class ContainerSpawnPointSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed class ContainerSpawnPointSystem : EntitySystem
|
||||
|
||||
// If it's just a spawn pref check if it's for cryo (silly).
|
||||
if (args.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Cryosleep &&
|
||||
(!_proto.TryIndex(args.Job?.Prototype, out var jobProto) || jobProto.JobEntity == null))
|
||||
(!_proto.TryIndex(args.Job, out var jobProto) || jobProto.JobEntity == null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public sealed class ContainerSpawnPointSystem : EntitySystem
|
||||
if (spawnPoint.SpawnType == SpawnPointType.Unset)
|
||||
{
|
||||
// make sure we also check the job here for various reasons.
|
||||
if (spawnPoint.Job == null || spawnPoint.Job == args.Job?.Prototype)
|
||||
if (spawnPoint.Job == null || spawnPoint.Job == args.Job)
|
||||
possibleContainers.Add((uid, spawnPoint, container, xform));
|
||||
continue;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ public sealed class ContainerSpawnPointSystem : EntitySystem
|
||||
|
||||
if (_gameTicker.RunLevel != GameRunLevel.InRound &&
|
||||
spawnPoint.SpawnType == SpawnPointType.Job &&
|
||||
(args.Job == null || spawnPoint.Job == args.Job.Prototype))
|
||||
(args.Job == null || spawnPoint.Job == args.Job))
|
||||
{
|
||||
possibleContainers.Add((uid, spawnPoint, container, xform));
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed class SpawnPointSystem : EntitySystem
|
||||
|
||||
if (_gameTicker.RunLevel != GameRunLevel.InRound &&
|
||||
spawnPoint.SpawnType == SpawnPointType.Job &&
|
||||
(args.Job == null || spawnPoint.Job == args.Job.Prototype))
|
||||
(args.Job == null || spawnPoint.Job == args.Job))
|
||||
{
|
||||
possiblePositions.Add(xform.Coordinates);
|
||||
}
|
||||
|
||||
@@ -18,13 +18,10 @@ using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Content.Shared.Station;
|
||||
using Content.Shared.StatusIcon;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
@@ -42,19 +39,19 @@ namespace Content.Server.Station.Systems;
|
||||
[PublicAPI]
|
||||
public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
|
||||
[Dependency] private readonly ActorSystem _actors = default!;
|
||||
[Dependency] private readonly ArrivalsSystem _arrivalsSystem = default!;
|
||||
[Dependency] private readonly CP14ExpeditionSystem _CP14expedition = default!;
|
||||
[Dependency] private readonly IdCardSystem _cardSystem = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly ContainerSpawnPointSystem _containerSpawnPointSystem = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
|
||||
[Dependency] private readonly IdCardSystem _cardSystem = default!;
|
||||
[Dependency] private readonly IdentitySystem _identity = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||
[Dependency] private readonly PdaSystem _pdaSystem = default!;
|
||||
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private bool _randomizeCharacters;
|
||||
|
||||
@@ -77,7 +74,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
/// <remarks>
|
||||
/// This only spawns the character, and does none of the mind-related setup you'd need for it to be playable.
|
||||
/// </remarks>
|
||||
public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null)
|
||||
public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, ProtoId<JobPrototype>? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null)
|
||||
{
|
||||
if (station != null && !Resolve(station.Value, ref stationSpawning))
|
||||
throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station));
|
||||
@@ -105,12 +102,12 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
/// <returns>The spawned entity</returns>
|
||||
public EntityUid SpawnPlayerMob(
|
||||
EntityCoordinates coordinates,
|
||||
JobComponent? job,
|
||||
ProtoId<JobPrototype>? job,
|
||||
HumanoidCharacterProfile? profile,
|
||||
EntityUid? station,
|
||||
EntityUid? entity = null)
|
||||
{
|
||||
_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype);
|
||||
_prototypeManager.TryIndex(job ?? string.Empty, out var prototype);
|
||||
RoleLoadout? loadout = null;
|
||||
|
||||
// Need to get the loadout up-front to handle names if we use an entity spawn override.
|
||||
@@ -204,9 +201,9 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
return entity.Value;
|
||||
}
|
||||
|
||||
private void DoJobSpecials(JobComponent? job, EntityUid entity)
|
||||
private void DoJobSpecials(ProtoId<JobPrototype>? job, EntityUid entity)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out JobPrototype? prototype))
|
||||
if (!_prototypeManager.TryIndex(job ?? string.Empty, out JobPrototype? prototype))
|
||||
return;
|
||||
|
||||
foreach (var jobSpecial in prototype.Special)
|
||||
@@ -273,7 +270,7 @@ public sealed class PlayerSpawningEvent : EntityEventArgs
|
||||
/// <summary>
|
||||
/// The job to use, if any.
|
||||
/// </summary>
|
||||
public readonly JobComponent? Job;
|
||||
public readonly ProtoId<JobPrototype>? Job;
|
||||
/// <summary>
|
||||
/// The profile to use, if any.
|
||||
/// </summary>
|
||||
@@ -283,7 +280,7 @@ public sealed class PlayerSpawningEvent : EntityEventArgs
|
||||
/// </summary>
|
||||
public readonly EntityUid? Station;
|
||||
|
||||
public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station)
|
||||
public PlayerSpawningEvent(ProtoId<JobPrototype>? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station)
|
||||
{
|
||||
Job = job;
|
||||
HumanoidCharacterProfile = humanoidCharacterProfile;
|
||||
|
||||
@@ -33,16 +33,16 @@ public sealed partial class BuyerAntagCondition : ListingCondition
|
||||
return true;
|
||||
|
||||
var roleSystem = ent.System<SharedRoleSystem>();
|
||||
var roles = roleSystem.MindGetAllRoles(mindId);
|
||||
var roles = roleSystem.MindGetAllRoleInfo(mindId);
|
||||
|
||||
if (Blacklist != null)
|
||||
{
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (role.Component is not AntagonistRoleComponent blacklistantag)
|
||||
if (!role.Antagonist || string.IsNullOrEmpty(role.Prototype))
|
||||
continue;
|
||||
|
||||
if (blacklistantag.PrototypeId != null && Blacklist.Contains(blacklistantag.PrototypeId))
|
||||
if (Blacklist.Contains(role.Prototype))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -52,10 +52,11 @@ public sealed partial class BuyerAntagCondition : ListingCondition
|
||||
var found = false;
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (role.Component is not AntagonistRoleComponent antag)
|
||||
|
||||
if (!role.Antagonist || string.IsNullOrEmpty(role.Prototype))
|
||||
continue;
|
||||
|
||||
if (antag.PrototypeId != null && Whitelist.Contains(antag.PrototypeId))
|
||||
if (Whitelist.Contains(role.Prototype))
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
|
||||
@@ -37,13 +37,13 @@ public sealed partial class BuyerDepartmentCondition : ListingCondition
|
||||
return true;
|
||||
|
||||
var jobs = ent.System<SharedJobSystem>();
|
||||
jobs.MindTryGetJob(mindId, out var job, out _);
|
||||
jobs.MindTryGetJob(mindId, out var job);
|
||||
|
||||
if (Blacklist != null && job?.Prototype != null)
|
||||
if (Blacklist != null && job != null)
|
||||
{
|
||||
foreach (var department in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||
{
|
||||
if (department.Roles.Contains(job.Prototype.Value) && Blacklist.Contains(department.ID))
|
||||
if (department.Roles.Contains(job.ID) && Blacklist.Contains(department.ID))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -52,11 +52,11 @@ public sealed partial class BuyerDepartmentCondition : ListingCondition
|
||||
{
|
||||
var found = false;
|
||||
|
||||
if (job?.Prototype != null)
|
||||
if (job != null)
|
||||
{
|
||||
foreach (var department in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||
{
|
||||
if (department.Roles.Contains(job.Prototype.Value) && Whitelist.Contains(department.ID))
|
||||
if (department.Roles.Contains(job.ID) && Whitelist.Contains(department.ID))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
|
||||
@@ -34,17 +34,17 @@ public sealed partial class BuyerJobCondition : ListingCondition
|
||||
return true;
|
||||
|
||||
var jobs = ent.System<SharedJobSystem>();
|
||||
jobs.MindTryGetJob(mindId, out var job, out _);
|
||||
jobs.MindTryGetJob(mindId, out var job);
|
||||
|
||||
if (Blacklist != null)
|
||||
{
|
||||
if (job?.Prototype != null && Blacklist.Contains(job.Prototype))
|
||||
if (job is not null && Blacklist.Contains(job.ID))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Whitelist != null)
|
||||
{
|
||||
if (job?.Prototype == null || !Whitelist.Contains(job.Prototype))
|
||||
if (job == null || !Whitelist.Contains(job.ID))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.Examine;
|
||||
using Content.Shared.Foldable;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Thief.Systems;
|
||||
@@ -18,7 +19,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -37,7 +38,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
|
||||
return;
|
||||
|
||||
var mind = _mind.GetMind(args.User);
|
||||
if (!HasComp<ThiefRoleComponent>(mind))
|
||||
if (mind == null || !_roles.MindHasRole<ThiefRoleComponent>(mind.Value))
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
|
||||
@@ -11,7 +11,6 @@ using Content.Server.Mind.Commands;
|
||||
using Content.Server.NPC;
|
||||
using Content.Server.NPC.HTN;
|
||||
using Content.Server.NPC.Systems;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Shared.CombatMode;
|
||||
@@ -47,18 +46,18 @@ namespace Content.Server.Zombies;
|
||||
/// </remarks>
|
||||
public sealed partial class ZombieSystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly ServerInventorySystem _inventory = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly IChatManager _chatMan = default!;
|
||||
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
|
||||
[Dependency] private readonly NpcFactionSystem _faction = default!;
|
||||
[Dependency] private readonly NPCSystem _npc = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoidAppearance = default!;
|
||||
[Dependency] private readonly IdentitySystem _identity = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
|
||||
[Dependency] private readonly IChatManager _chatMan = default!;
|
||||
[Dependency] private readonly ServerInventorySystem _inventory = default!;
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||
[Dependency] private readonly NPCSystem _npc = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Handles an entity turning into a zombie when they die or go into crit
|
||||
@@ -234,7 +233,7 @@ public sealed partial class ZombieSystem
|
||||
if (hasMind && _mind.TryGetSession(mindId, out var session))
|
||||
{
|
||||
//Zombie role for player manifest
|
||||
_roles.MindAddRole(mindId, new ZombieRoleComponent { PrototypeId = zombiecomp.ZombieRoleId });
|
||||
_roles.MindAddRole(mindId, "MindRoleZombie", mind: null, silent: true);
|
||||
|
||||
//Greeting message for new bebe zombers
|
||||
_chatMan.DispatchServerMessage(session, Loc.GetString("zombie-infection-greeting"));
|
||||
|
||||
216
Content.Server/_CP14/Currency/CP14CurrencySystem.cs
Normal file
216
Content.Server/_CP14/Currency/CP14CurrencySystem.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared._CP14.Currency;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server._CP14.Currency;
|
||||
|
||||
public sealed partial class CP14CurrencySystem : CP14SharedCurrencySystem
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly StackSystem _stack = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CP14CurrencyComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<CP14CurrencyConverterComponent, ExaminedEvent>(OnConverterExamine);
|
||||
|
||||
SubscribeLocalEvent<CP14CurrencyConverterComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<CP14CurrencyConverterComponent, GetVerbsEvent<Verb>>(OnGetVerb);
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<CP14CurrencyComponent> currency, ref ExaminedEvent args)
|
||||
{
|
||||
var total = GetTotalCurrency(currency, currency.Comp);
|
||||
|
||||
var push = Loc.GetString("cp14-currency-examine-title");
|
||||
push += GetCurrencyPrettyString(total);
|
||||
args.PushMarkup(push);
|
||||
}
|
||||
|
||||
private void OnConverterExamine(Entity<CP14CurrencyConverterComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var push = $"{Loc.GetString("cp14-currency-converter-examine-title")} {GetCurrencyPrettyString(ent.Comp.Balance)}";
|
||||
args.PushMarkup(push);
|
||||
}
|
||||
|
||||
private void OnInteractUsing(Entity<CP14CurrencyConverterComponent> ent, ref InteractUsingEvent args)
|
||||
{
|
||||
if (!TryComp<CP14CurrencyComponent>(args.Used, out var currency))
|
||||
return;
|
||||
|
||||
if (ent.Comp.Whitelist is not null && !_whitelist.IsValid(ent.Comp.Whitelist, args.Used))
|
||||
return;
|
||||
|
||||
var delta = GetTotalCurrency(args.Used);
|
||||
ent.Comp.Balance += delta;
|
||||
QueueDel(args.Used);
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("cp14-currency-converter-insert", ("cash", delta)), ent, args.User);
|
||||
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3));
|
||||
}
|
||||
|
||||
private void OnGetVerb(Entity<CP14CurrencyConverterComponent> ent, ref GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
var transform = Transform(ent);
|
||||
var coord = transform.Coordinates.Offset(transform.LocalRotation.RotateVec(ent.Comp.SpawnOffset));
|
||||
Verb copperVerb = new()
|
||||
{
|
||||
Text = Loc.GetString("cp14-currency-converter-get-cp"),
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/cp_coin.rsi/coin10.png")),
|
||||
Category = VerbCategory.CP14CurrencyConvert,
|
||||
Priority = 1,
|
||||
CloseMenu = false,
|
||||
Act = () =>
|
||||
{
|
||||
if (ent.Comp.Balance < CP.Value)
|
||||
return;
|
||||
|
||||
ent.Comp.Balance -= CP.Value;
|
||||
|
||||
var newEnt = Spawn(CP.Key, coord);
|
||||
_stack.TryMergeToContacts(newEnt);
|
||||
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(0.9f));
|
||||
},
|
||||
};
|
||||
args.Verbs.Add(copperVerb);
|
||||
Verb silverVerb = new()
|
||||
{
|
||||
Text = Loc.GetString("cp14-currency-converter-get-sp"),
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/sp_coin.rsi/coin10.png")),
|
||||
Category = VerbCategory.CP14CurrencyConvert,
|
||||
Priority = 2,
|
||||
CloseMenu = false,
|
||||
Act = () =>
|
||||
{
|
||||
if (ent.Comp.Balance < SP.Value)
|
||||
return;
|
||||
|
||||
ent.Comp.Balance -= SP.Value;
|
||||
var newEnt = Spawn(SP.Key, coord);
|
||||
_stack.TryMergeToContacts(newEnt);
|
||||
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(1.1f));
|
||||
},
|
||||
};
|
||||
args.Verbs.Add(silverVerb);
|
||||
Verb goldVerb = new()
|
||||
{
|
||||
Text = Loc.GetString("cp14-currency-converter-get-gp"),
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/gp_coin.rsi/coin10.png")),
|
||||
Category = VerbCategory.CP14CurrencyConvert,
|
||||
Priority = 3,
|
||||
CloseMenu = false,
|
||||
Act = () =>
|
||||
{
|
||||
if (ent.Comp.Balance < GP.Value)
|
||||
return;
|
||||
|
||||
ent.Comp.Balance -= GP.Value;
|
||||
var newEnt = Spawn(GP.Key, coord);
|
||||
_stack.TryMergeToContacts(newEnt);
|
||||
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(1.3f));
|
||||
},
|
||||
};
|
||||
args.Verbs.Add(goldVerb);
|
||||
Verb platinumVerb = new()
|
||||
{
|
||||
Text = Loc.GetString("cp14-currency-converter-get-pp"),
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/pp_coin.rsi/coin10.png")),
|
||||
Category = VerbCategory.CP14CurrencyConvert,
|
||||
Priority = 4,
|
||||
CloseMenu = false,
|
||||
Act = () =>
|
||||
{
|
||||
if (ent.Comp.Balance < PP.Value)
|
||||
return;
|
||||
|
||||
ent.Comp.Balance -= PP.Value;
|
||||
var newEnt = Spawn(PP.Key, coord);
|
||||
_stack.TryMergeToContacts(newEnt);
|
||||
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(1.5f));
|
||||
},
|
||||
};
|
||||
args.Verbs.Add(platinumVerb);
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GenerateMoney(EntProtoId currencyType, int target, EntityCoordinates coordinates)
|
||||
{
|
||||
return GenerateMoney(currencyType, target, coordinates, out _);
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GenerateMoney(EntProtoId currencyType, int target, EntityCoordinates coordinates, out int remainder)
|
||||
{
|
||||
remainder = target;
|
||||
HashSet<EntityUid> spawns = new();
|
||||
|
||||
if (!_proto.TryIndex(currencyType, out var indexedCurrency))
|
||||
return spawns;
|
||||
|
||||
var ent = Spawn(currencyType, coordinates);
|
||||
if (ProcessEntity(ent, ref remainder, spawns))
|
||||
return spawns;
|
||||
|
||||
while (remainder > 0)
|
||||
{
|
||||
var newEnt = Spawn(currencyType, coordinates);
|
||||
if (ProcessEntity(newEnt, ref remainder, spawns))
|
||||
break;
|
||||
}
|
||||
|
||||
return spawns;
|
||||
}
|
||||
|
||||
private bool ProcessEntity(EntityUid ent, ref int remainder, HashSet<EntityUid> spawns)
|
||||
{
|
||||
var singleCurrency = GetTotalCurrency(ent);
|
||||
|
||||
if (singleCurrency > remainder)
|
||||
{
|
||||
QueueDel(ent);
|
||||
return true;
|
||||
}
|
||||
|
||||
spawns.Add(ent);
|
||||
remainder -= singleCurrency;
|
||||
|
||||
if (TryComp<StackComponent>(ent, out var stack))
|
||||
{
|
||||
AdjustStack(ent, stack, singleCurrency, ref remainder);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AdjustStack(EntityUid ent, StackComponent stack, float singleCurrency, ref int remainder)
|
||||
{
|
||||
var singleStackCurrency = singleCurrency / stack.Count;
|
||||
var stackLeftSpace = stack.MaxCountOverride - stack.Count;
|
||||
|
||||
if (stackLeftSpace is not null)
|
||||
{
|
||||
var addedStack = MathF.Min((float)stackLeftSpace, MathF.Floor(remainder / singleStackCurrency));
|
||||
|
||||
if (addedStack > 0)
|
||||
{
|
||||
_stack.SetCount(ent, stack.Count + (int)addedStack);
|
||||
remainder -= (int)(addedStack * singleStackCurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,6 @@ public sealed partial class CP14CurrencyCollectConditionComponent : Component
|
||||
[DataField]
|
||||
public int Currency = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// Limits the goal to collecting values from a specific category.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string? Category;
|
||||
|
||||
[DataField(required: true)]
|
||||
public LocId ObjectiveText;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
[Dependency] private readonly CP14CurrencySystem _currency = default!;
|
||||
[Dependency] private readonly CP14SharedCurrencySystem _currency = default!;
|
||||
|
||||
private EntityQuery<ContainerManagerComponent> _containerQuery;
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
|
||||
private void OnAfterAssign(Entity<CP14CurrencyCollectConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
|
||||
{
|
||||
_metaData.SetEntityName(condition.Owner, Loc.GetString(condition.Comp.ObjectiveText), args.Meta);
|
||||
_metaData.SetEntityDescription(condition.Owner, Loc.GetString(condition.Comp.ObjectiveDescription, ("coins", _currency.GetPrettyCurrency(condition.Comp.Currency))), args.Meta);
|
||||
_metaData.SetEntityDescription(condition.Owner, Loc.GetString(condition.Comp.ObjectiveDescription, ("coins", _currency.GetCurrencyPrettyString(condition.Comp.Currency))), args.Meta);
|
||||
_objectives.SetIcon(condition.Owner, condition.Comp.ObjectiveSprite);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
|
||||
foreach (var entity in container.ContainedEntities)
|
||||
{
|
||||
// check if this is the item
|
||||
count += CheckCurrency(entity, condition);
|
||||
count += _currency.GetTotalCurrency(entity);
|
||||
|
||||
// if it is a container check its contents
|
||||
if (_containerQuery.TryGetComponent(entity, out var containerManager))
|
||||
@@ -88,7 +88,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
|
||||
private void CheckEntity(EntityUid entity, CP14CurrencyCollectConditionComponent condition, ref Stack<ContainerManagerComponent> containerStack, ref int counter)
|
||||
{
|
||||
// check if this is the item
|
||||
counter += CheckCurrency(entity, condition);
|
||||
counter += _currency.GetTotalCurrency(entity);
|
||||
|
||||
//we don't check the inventories of sentient entity
|
||||
if (!TryComp<MindContainerComponent>(entity, out _))
|
||||
@@ -98,16 +98,4 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
|
||||
containerStack.Push(containerManager);
|
||||
}
|
||||
}
|
||||
|
||||
private int CheckCurrency(EntityUid entity, CP14CurrencyCollectConditionComponent condition)
|
||||
{
|
||||
// check if this is the target
|
||||
if (!TryComp<CP14CurrencyComponent>(entity, out var target))
|
||||
return 0;
|
||||
|
||||
if (target.Category != condition.Category)
|
||||
return 0;
|
||||
|
||||
return _currency.GetTotalCurrency(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ public sealed class CP14ExpeditionSystem : EntitySystem
|
||||
var possiblePositions = new List<EntityCoordinates>();
|
||||
while (points.MoveNext(out var uid, out var spawnPoint, out var xform))
|
||||
{
|
||||
if (ev.Job != null && spawnPoint.Job != ev.Job.Prototype)
|
||||
if (ev.Job != null && spawnPoint.Job != ev.Job.Value)
|
||||
continue;
|
||||
|
||||
if (xform.GridUid != gridUid)
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Numerics;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Events;
|
||||
using Content.Shared._CP14.TravelingStoreShip;
|
||||
using Content.Shared._CP14.TravelingStoreShip.Prototype;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._CP14.TravelingStoreShip;
|
||||
|
||||
public sealed partial class CP14CargoSystem
|
||||
{
|
||||
private EntityQuery<ArrivalsBlacklistComponent> _blacklistQuery;
|
||||
private void InitializeShuttle()
|
||||
{
|
||||
_blacklistQuery = GetEntityQuery<ArrivalsBlacklistComponent>();
|
||||
SubscribeLocalEvent<CP14TravelingStoreShipComponent, FTLCompletedEvent>(OnFTLCompleted);
|
||||
}
|
||||
|
||||
private void UpdateShuttle()
|
||||
{
|
||||
var query = EntityQueryEnumerator<CP14StationTravelingStoreShipTargetComponent>();
|
||||
while (query.MoveNext(out var uid, out var ship))
|
||||
{
|
||||
if (_timing.CurTime < ship.NextTravelTime || ship.NextTravelTime == TimeSpan.Zero)
|
||||
continue;
|
||||
|
||||
if (Transform(ship.Shuttle).MapUid == Transform(ship.TradePostMap).MapUid)
|
||||
{
|
||||
// if landed on trade post
|
||||
ship.NextTravelTime = _timing.CurTime + ship.StationWaitTime;
|
||||
SendShuttleToStation((uid, ship));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if landed on station
|
||||
ship.NextTravelTime = _timing.CurTime + ship.TradePostWaitTime;
|
||||
SendShuttleToTradepost((uid, ship));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendShuttleToStation(Entity<CP14StationTravelingStoreShipTargetComponent> station, float startupTime = 0f)
|
||||
{
|
||||
var targetPoints = new List<EntityUid>();
|
||||
var targetEnumerator = EntityQueryEnumerator<CP14TravelingStoreShipFTLTargetComponent, TransformComponent>(); //TODO - different method position location
|
||||
while (targetEnumerator.MoveNext(out var uid, out _, out _))
|
||||
{
|
||||
targetPoints.Add(uid);
|
||||
}
|
||||
if (targetPoints.Count == 0)
|
||||
return;
|
||||
|
||||
var target = _random.Pick(targetPoints);
|
||||
var targetXform = Transform(target);
|
||||
|
||||
var shuttleComp = Comp<ShuttleComponent>(station.Comp.Shuttle);
|
||||
|
||||
_shuttles.FTLToCoordinates(station.Comp.Shuttle, shuttleComp, targetXform.Coordinates, targetXform.LocalRotation, hyperspaceTime: 5f, startupTime: startupTime);
|
||||
}
|
||||
|
||||
private void SendShuttleToTradepost(Entity<CP14StationTravelingStoreShipTargetComponent> station)
|
||||
{
|
||||
var shuttleComp = Comp<ShuttleComponent>(station.Comp.Shuttle);
|
||||
|
||||
_shuttles.FTLToCoordinates(station.Comp.Shuttle, shuttleComp, new EntityCoordinates(station.Comp.TradePostMap, Vector2.Zero), Angle.Zero, hyperspaceTime: 5f);
|
||||
}
|
||||
|
||||
private void OnFTLCompleted(Entity<CP14TravelingStoreShipComponent> ent, ref FTLCompletedEvent args)
|
||||
{
|
||||
if (!TryComp<CP14StationTravelingStoreShipTargetComponent>(ent.Comp.Station, out var station))
|
||||
return;
|
||||
|
||||
if (Transform(ent).MapUid == Transform(station.TradePostMap).MapUid) //Landed on tradepost
|
||||
{
|
||||
station.OnStation = false;
|
||||
|
||||
SellingThings((ent.Comp.Station, station));
|
||||
UpdateStorePositions((ent.Comp.Station, station));
|
||||
}
|
||||
else //Landed on station
|
||||
{
|
||||
station.OnStation = true;
|
||||
}
|
||||
UpdateAllStores();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Text;
|
||||
using Content.Shared._CP14.TravelingStoreShip;
|
||||
using Content.Shared.UserInterface;
|
||||
|
||||
namespace Content.Server._CP14.TravelingStoreShip;
|
||||
|
||||
public sealed partial class CP14CargoSystem
|
||||
{
|
||||
public void InitializeStore()
|
||||
{
|
||||
SubscribeLocalEvent<CP14CargoStoreComponent, BeforeActivatableUIOpenEvent>(OnBeforeUIOpen);
|
||||
}
|
||||
|
||||
private void TryInitStore(Entity<CP14CargoStoreComponent> ent)
|
||||
{
|
||||
//TODO: There's no support for multiple stations. (settlements).
|
||||
var stations = _station.GetStations();
|
||||
|
||||
if (stations.Count == 0)
|
||||
return;
|
||||
|
||||
if (!TryComp<CP14StationTravelingStoreShipTargetComponent>(stations[0], out var station))
|
||||
return;
|
||||
|
||||
ent.Comp.Station = new Entity<CP14StationTravelingStoreShipTargetComponent>(stations[0], station);
|
||||
}
|
||||
|
||||
private void OnBeforeUIOpen(Entity<CP14CargoStoreComponent> ent, ref BeforeActivatableUIOpenEvent args)
|
||||
{
|
||||
if (ent.Comp.Station is null)
|
||||
TryInitStore(ent);
|
||||
|
||||
UpdateUIProducts(ent);
|
||||
}
|
||||
|
||||
//TODO: redo
|
||||
private void UpdateAllStores()
|
||||
{
|
||||
var query = EntityQueryEnumerator<CP14CargoStoreComponent>();
|
||||
while (query.MoveNext(out var uid, out var store))
|
||||
{
|
||||
UpdateUIProducts((uid, store));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUIProducts(Entity<CP14CargoStoreComponent> ent)
|
||||
{
|
||||
if (ent.Comp.Station is null)
|
||||
return;
|
||||
|
||||
var prodBuy = new HashSet<CP14StoreUiProductEntry>();
|
||||
var prodSell = new HashSet<CP14StoreUiProductEntry>();
|
||||
|
||||
foreach (var proto in ent.Comp.Station.Value.Comp.CurrentBuyPositions)
|
||||
{
|
||||
if (!_proto.TryIndex(proto.Key, out var indexedProto))
|
||||
continue;
|
||||
|
||||
var name = Loc.GetString(indexedProto.Name);
|
||||
var desc = new StringBuilder();
|
||||
desc.Append(Loc.GetString(indexedProto.Desc) + "\n");
|
||||
foreach (var service in indexedProto.Services)
|
||||
{
|
||||
desc.Append(service.GetDescription(_proto, EntityManager));
|
||||
}
|
||||
|
||||
prodBuy.Add(new CP14StoreUiProductEntry(proto.Key.Id, indexedProto.Icon, name, desc.ToString(), proto.Value));
|
||||
}
|
||||
|
||||
foreach (var proto in ent.Comp.Station.Value.Comp.CurrentSellPositions)
|
||||
{
|
||||
if (!_proto.TryIndex(proto.Key, out var indexedProto))
|
||||
continue;
|
||||
|
||||
var name = Loc.GetString(indexedProto.Name);
|
||||
|
||||
var desc = new StringBuilder();
|
||||
desc.Append(Loc.GetString(indexedProto.Desc) + "\n");
|
||||
desc.Append(indexedProto.Service.GetDescription(_proto, EntityManager));
|
||||
|
||||
prodSell.Add(new CP14StoreUiProductEntry(proto.Key.Id, indexedProto.Icon, name, desc.ToString(), proto.Value));
|
||||
}
|
||||
|
||||
var stationComp = ent.Comp.Station.Value.Comp;
|
||||
_userInterface.SetUiState(ent.Owner, CP14StoreUiKey.Key, new CP14StoreUiState(prodBuy, prodSell, stationComp.OnStation, stationComp.NextTravelTime));
|
||||
}
|
||||
}
|
||||
205
Content.Server/_CP14/TravelingStoreShip/CP14CargoSystem.cs
Normal file
205
Content.Server/_CP14/TravelingStoreShip/CP14CargoSystem.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using Content.Server._CP14.Currency;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Events;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared._CP14.Currency;
|
||||
using Content.Shared._CP14.TravelingStoreShip;
|
||||
using Content.Shared._CP14.TravelingStoreShip.Prototype;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._CP14.TravelingStoreShip;
|
||||
|
||||
public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly ShuttleSystem _shuttles = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly CP14CurrencySystem _currency = default!;
|
||||
[Dependency] private readonly SharedStorageSystem _storage = default!;
|
||||
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
private IEnumerable<CP14StoreBuyPositionPrototype>? _buyProto;
|
||||
private IEnumerable<CP14StoreSellPositionPrototype>? _sellProto;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
InitializeStore();
|
||||
InitializeShuttle();
|
||||
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
_buyProto = _proto.EnumeratePrototypes<CP14StoreBuyPositionPrototype>();
|
||||
_sellProto = _proto.EnumeratePrototypes<CP14StoreSellPositionPrototype>();
|
||||
|
||||
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
|
||||
SubscribeLocalEvent<CP14StationTravelingStoreShipTargetComponent, StationPostInitEvent>(OnPostInit);
|
||||
}
|
||||
|
||||
private void OnProtoReload(PrototypesReloadedEventArgs ev)
|
||||
{
|
||||
_buyProto = _proto.EnumeratePrototypes<CP14StoreBuyPositionPrototype>();
|
||||
_sellProto = _proto.EnumeratePrototypes<CP14StoreSellPositionPrototype>();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
UpdateShuttle();
|
||||
}
|
||||
|
||||
private void OnPostInit(Entity<CP14StationTravelingStoreShipTargetComponent> station, ref StationPostInitEvent args)
|
||||
{
|
||||
if (!Deleted(station.Comp.Shuttle))
|
||||
return;
|
||||
|
||||
var tradepostMap = _mapManager.CreateMap();
|
||||
|
||||
if (!_loader.TryLoad(tradepostMap, station.Comp.ShuttlePath.ToString(), out var shuttleUids))
|
||||
return;
|
||||
|
||||
var shuttle = shuttleUids[0];
|
||||
station.Comp.Shuttle = shuttle;
|
||||
station.Comp.TradePostMap = _mapManager.GetMapEntityId(tradepostMap);
|
||||
var travelingStoreShipComp = EnsureComp<CP14TravelingStoreShipComponent>(station.Comp.Shuttle);
|
||||
travelingStoreShipComp.Station = station;
|
||||
|
||||
station.Comp.NextTravelTime = _timing.CurTime + TimeSpan.FromSeconds(10f);
|
||||
UpdateStorePositions(station);
|
||||
}
|
||||
|
||||
private void UpdateStorePositions(Entity<CP14StationTravelingStoreShipTargetComponent> station)
|
||||
{
|
||||
station.Comp.CurrentBuyPositions.Clear();
|
||||
station.Comp.CurrentSellPositions.Clear();
|
||||
|
||||
if (_buyProto is not null)
|
||||
{
|
||||
foreach (var buyPos in _buyProto)
|
||||
{
|
||||
station.Comp.CurrentBuyPositions.Add(buyPos, buyPos.Price.Next(_random));
|
||||
}
|
||||
}
|
||||
if (_sellProto is not null)
|
||||
{
|
||||
foreach (var sellPos in _sellProto)
|
||||
{
|
||||
station.Comp.CurrentSellPositions.Add(sellPos, sellPos.Price.Next(_random));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SellingThings(Entity<CP14StationTravelingStoreShipTargetComponent> station)
|
||||
{
|
||||
var shuttle = station.Comp.Shuttle;
|
||||
|
||||
//Get all entities sent to trading posts
|
||||
var toSell = new HashSet<EntityUid>();
|
||||
|
||||
var query = EntityQueryEnumerator<CP14SellingPalettComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out _, out var palletXform))
|
||||
{
|
||||
if (palletXform.ParentUid != shuttle || !palletXform.Anchored)
|
||||
continue;
|
||||
|
||||
var sentEntities = new HashSet<EntityUid>();
|
||||
|
||||
_lookup.GetEntitiesInRange(uid, 1, sentEntities, LookupFlags.Dynamic | LookupFlags.Sundries);
|
||||
|
||||
foreach (var ent in sentEntities)
|
||||
{
|
||||
if (toSell.Contains(ent) || !_xformQuery.TryGetComponent(ent, out _))
|
||||
continue;
|
||||
|
||||
toSell.Add(ent);
|
||||
}
|
||||
}
|
||||
|
||||
var cash = 0;
|
||||
foreach (var sellPos in station.Comp.CurrentSellPositions)
|
||||
{
|
||||
if (!_proto.TryIndex(sellPos.Key, out var indexedPos))
|
||||
continue;
|
||||
|
||||
while (indexedPos.Service.TrySell(EntityManager, toSell))
|
||||
{
|
||||
cash += sellPos.Value;
|
||||
}
|
||||
}
|
||||
|
||||
var moneyBox = GetMoneyBox(station);
|
||||
if (moneyBox is not null)
|
||||
{
|
||||
var coord = Transform(moneyBox.Value).Coordinates;
|
||||
|
||||
if (cash > 0)
|
||||
{
|
||||
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.PP.Key, cash, coord, out var remainder);
|
||||
cash = remainder;
|
||||
foreach (var coin in coins)
|
||||
{
|
||||
_storage.Insert(moneyBox.Value, coin, out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (cash > 0)
|
||||
{
|
||||
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.GP.Key, cash, coord, out var remainder);
|
||||
cash = remainder;
|
||||
foreach (var coin in coins)
|
||||
{
|
||||
_storage.Insert(moneyBox.Value, coin, out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (cash > 0)
|
||||
{
|
||||
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.SP.Key, cash, coord, out var remainder);
|
||||
cash = remainder;
|
||||
foreach (var coin in coins)
|
||||
{
|
||||
_storage.Insert(moneyBox.Value, coin, out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (cash > 0)
|
||||
{
|
||||
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.CP.Key, cash, coord);
|
||||
foreach (var coin in coins)
|
||||
{
|
||||
_storage.Insert(moneyBox.Value, coin, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EntityUid? GetMoneyBox(Entity<CP14StationTravelingStoreShipTargetComponent> station)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CP14CargoMoneyBoxComponent, TransformComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _, out var xform))
|
||||
{
|
||||
if (xform.GridUid != station.Comp.Shuttle)
|
||||
continue;
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server._CP14.TravelingStoreShip;
|
||||
|
||||
[RegisterComponent, Access(typeof(CP14CargoSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class CP14TravelingStoreShipComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public EntityUid Station;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
namespace Content.Server._CP14.TravelingStoreShip;
|
||||
|
||||
/// <summary>
|
||||
/// One of the possible points where an traveling store ship might land
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14CargoSystem))]
|
||||
public sealed partial class CP14TravelingStoreShipFTLTargetComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -30,7 +30,6 @@ public sealed partial class CP14WorkbenchSystem : SharedCP14WorkbenchSystem
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
private EntityQuery<StackComponent> _stackQuery;
|
||||
|
||||
@@ -86,6 +85,7 @@ public sealed partial class CP14WorkbenchSystem : SharedCP14WorkbenchSystem
|
||||
}
|
||||
|
||||
var resultEntity = Spawn(_proto.Index(args.Recipe).Result);
|
||||
_stack.TryMergeToContacts(resultEntity);
|
||||
|
||||
_solutionContainer.TryGetSolution(resultEntity, recipe.Solution, out var resultSoln, out var resultSolution);
|
||||
if (recipe.TryMergeSolutions && resultSoln is not null)
|
||||
|
||||
@@ -20,7 +20,12 @@ public sealed partial class IdCardComponent : Component
|
||||
[DataField]
|
||||
[AutoNetworkedField]
|
||||
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
|
||||
public string? JobTitle;
|
||||
public LocId? JobTitle;
|
||||
|
||||
private string? _jobTitle;
|
||||
|
||||
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWriteExecute)]
|
||||
public string? LocalizedJobTitle { set => _jobTitle = value; get => _jobTitle ?? Loc.GetString(JobTitle ?? string.Empty); }
|
||||
|
||||
/// <summary>
|
||||
/// The state of the job icon rsi.
|
||||
|
||||
@@ -67,7 +67,7 @@ public sealed class IdExaminableSystem : EntitySystem
|
||||
|
||||
private string GetNameAndJob(IdCardComponent id)
|
||||
{
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.JobTitle) ? string.Empty : $" ({id.JobTitle})";
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.LocalizedJobTitle) ? string.Empty : $" ({id.LocalizedJobTitle})";
|
||||
|
||||
var val = string.IsNullOrWhiteSpace(id.FullName)
|
||||
? Loc.GetString(id.NameLocId,
|
||||
|
||||
@@ -116,6 +116,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
|
||||
/// Actually works with the LocalizedJobTitle DataField and not with JobTitle.
|
||||
/// </remarks>
|
||||
public bool TryChangeJobTitle(EntityUid uid, string? jobTitle, IdCardComponent? id = null, EntityUid? player = null)
|
||||
{
|
||||
@@ -134,9 +135,9 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
jobTitle = null;
|
||||
}
|
||||
|
||||
if (id.JobTitle == jobTitle)
|
||||
if (id.LocalizedJobTitle == jobTitle)
|
||||
return true;
|
||||
id.JobTitle = jobTitle;
|
||||
id.LocalizedJobTitle = jobTitle;
|
||||
Dirty(uid, id);
|
||||
UpdateEntityName(uid, id);
|
||||
|
||||
@@ -238,7 +239,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
if (!Resolve(uid, ref id))
|
||||
return;
|
||||
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.JobTitle) ? string.Empty : $" ({id.JobTitle})";
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.LocalizedJobTitle) ? string.Empty : $" ({id.LocalizedJobTitle})";
|
||||
|
||||
var val = string.IsNullOrWhiteSpace(id.FullName)
|
||||
? Loc.GetString(id.NameLocId,
|
||||
@@ -251,7 +252,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
|
||||
private static string ExtractFullTitle(IdCardComponent idCardComponent)
|
||||
{
|
||||
return $"{idCardComponent.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(idCardComponent.JobTitle ?? string.Empty)})"
|
||||
return $"{idCardComponent.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(idCardComponent.LocalizedJobTitle ?? string.Empty)})"
|
||||
.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.ChangeNameInContainer;
|
||||
|
||||
/// <summary>
|
||||
/// An entity with this component will get its name and verb chaned to the container it's inside of. E.g, if your a
|
||||
/// pAI that has this component and are inside a lizard plushie, your name when talking will be "lizard plushie".
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(ChangeNameInContainerSystem))]
|
||||
public sealed partial class ChangeVoiceInContainerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A whitelist of containers that will change the name.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Shared.Containers;
|
||||
using Content.Shared.Whitelist;
|
||||
using Content.Shared.Speech;
|
||||
|
||||
namespace Content.Shared.ChangeNameInContainer;
|
||||
|
||||
public sealed partial class ChangeNameInContainerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ChangeVoiceInContainerComponent, TransformSpeakerNameEvent>(OnTransformSpeakerName);
|
||||
}
|
||||
|
||||
private void OnTransformSpeakerName(Entity<ChangeVoiceInContainerComponent> ent, ref TransformSpeakerNameEvent args)
|
||||
{
|
||||
if (!_container.TryGetContainingContainer((ent, null, null), out var container)
|
||||
|| _whitelist.IsWhitelistFail(ent.Comp.Whitelist, container.Owner))
|
||||
return;
|
||||
|
||||
args.VoiceName = Name(container.Owner);
|
||||
if (TryComp<SpeechComponent>(container.Owner, out var speechComp))
|
||||
args.SpeechVerb = speechComp.SpeechVerb;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Clothing.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Defines something as causing waddling when worn.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class WaddleWhenWornComponent : Component
|
||||
{
|
||||
///<summary>
|
||||
/// How high should they hop during the waddle? Higher hop = more energy.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Vector2 HopIntensity = new(0, 0.25f);
|
||||
|
||||
/// <summary>
|
||||
/// How far should they rock backward and forward during the waddle?
|
||||
/// Each step will alternate between this being a positive and negative rotation. More rock = more scary.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float TumbleIntensity = 20.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How long should a complete step take? Less time = more chaos.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float AnimationLength = 0.66f;
|
||||
|
||||
/// <summary>
|
||||
/// How much shorter should the animation be when running?
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float RunAnimationLengthMultiplier = 0.568f;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Inventory.Events;
|
||||
|
||||
namespace Content.Shared.Clothing.EntitySystems;
|
||||
|
||||
public sealed class WaddleClothingSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, ClothingGotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
|
||||
}
|
||||
|
||||
private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotEquippedEvent args)
|
||||
{
|
||||
var waddleAnimComp = EnsureComp<WaddleAnimationComponent>(args.Wearer);
|
||||
|
||||
waddleAnimComp.AnimationLength = comp.AnimationLength;
|
||||
waddleAnimComp.HopIntensity = comp.HopIntensity;
|
||||
waddleAnimComp.RunAnimationLengthMultiplier = comp.RunAnimationLengthMultiplier;
|
||||
waddleAnimComp.TumbleIntensity = comp.TumbleIntensity;
|
||||
}
|
||||
|
||||
private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotUnequippedEvent args)
|
||||
{
|
||||
RemComp<WaddleAnimationComponent>(args.Wearer);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ public sealed partial class MindContainerComponent : Component
|
||||
/// The mind controlling this mob. Can be null.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[Access(typeof(SharedMindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends
|
||||
public EntityUid? Mind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -35,7 +34,6 @@ public sealed partial class MindContainerComponent : Component
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("ghostOnShutdown")]
|
||||
[Access(typeof(SharedMindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends
|
||||
public bool GhostOnShutdown { get; set; } = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -87,17 +86,21 @@ public sealed partial class MindComponent : Component
|
||||
/// <summary>
|
||||
/// Prevents user from ghosting out
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("preventGhosting")]
|
||||
[DataField]
|
||||
public bool PreventGhosting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Prevents user from suiciding
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("preventSuicide")]
|
||||
[DataField]
|
||||
public bool PreventSuicide { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mind Role Entities belonging to this Mind
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<EntityUid> MindRoles = new List<EntityUid>();
|
||||
|
||||
/// <summary>
|
||||
/// The session of the player owning this mind.
|
||||
/// Can be null, in which case the player is currently not logged in.
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Movement.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Declares that an entity has started to waddle like a duck/clown.
|
||||
/// </summary>
|
||||
/// <param name="entity">The newly be-waddled.</param>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StartedWaddlingEvent(NetEntity entity) : EntityEventArgs
|
||||
{
|
||||
public NetEntity Entity = entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declares that an entity has stopped waddling like a duck/clown.
|
||||
/// </summary>
|
||||
/// <param name="entity">The former waddle-er.</param>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StoppedWaddlingEvent(NetEntity entity) : EntityEventArgs
|
||||
{
|
||||
public NetEntity Entity = entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines something as having a waddle animation when it moves.
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentState]
|
||||
public sealed partial class WaddleAnimationComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// What's the name of this animation? Make sure it's unique so it can play along side other animations.
|
||||
/// This prevents someone accidentally causing two identical waddling effects to play on someone at the same time.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string KeyName = "Waddle";
|
||||
|
||||
///<summary>
|
||||
/// How high should they hop during the waddle? Higher hop = more energy.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Vector2 HopIntensity = new(0, 0.25f);
|
||||
|
||||
/// <summary>
|
||||
/// How far should they rock backward and forward during the waddle?
|
||||
/// Each step will alternate between this being a positive and negative rotation. More rock = more scary.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float TumbleIntensity = 20.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How long should a complete step take? Less time = more chaos.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float AnimationLength = 0.66f;
|
||||
|
||||
/// <summary>
|
||||
/// How much shorter should the animation be when running?
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float RunAnimationLengthMultiplier = 0.568f;
|
||||
|
||||
/// <summary>
|
||||
/// Stores which step we made last, so if someone cancels out of the animation mid-step then restarts it looks more natural.
|
||||
/// </summary>
|
||||
public bool LastStep;
|
||||
|
||||
/// <summary>
|
||||
/// Stores if we're currently waddling so we can start/stop as appropriate and can tell other systems our state.
|
||||
/// </summary>
|
||||
[AutoNetworkedField]
|
||||
public bool IsCurrentlyWaddling;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Movement.Systems;
|
||||
|
||||
public abstract class SharedWaddleAnimationSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// Startup
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, ComponentStartup>(OnComponentStartup);
|
||||
|
||||
// Start moving possibilities
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, MoveInputEvent>(OnMovementInput);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StoodEvent>(OnStood);
|
||||
|
||||
// Stop moving possibilities
|
||||
SubscribeLocalEvent((Entity<WaddleAnimationComponent> ent, ref StunnedEvent _) => StopWaddling(ent));
|
||||
SubscribeLocalEvent((Entity<WaddleAnimationComponent> ent, ref DownedEvent _) => StopWaddling(ent));
|
||||
SubscribeLocalEvent((Entity<WaddleAnimationComponent> ent, ref BuckledEvent _) => StopWaddling(ent));
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, GravityChangedEvent>(OnGravityChanged);
|
||||
}
|
||||
|
||||
private void OnGravityChanged(Entity<WaddleAnimationComponent> ent, ref GravityChangedEvent args)
|
||||
{
|
||||
if (!args.HasGravity && ent.Comp.IsCurrentlyWaddling)
|
||||
StopWaddling(ent);
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<WaddleAnimationComponent> entity, ref ComponentStartup args)
|
||||
{
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var moverComponent))
|
||||
return;
|
||||
|
||||
// If the waddler is currently moving, make them start waddling
|
||||
if ((moverComponent.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.AnyDirection)
|
||||
{
|
||||
RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMovementInput(Entity<WaddleAnimationComponent> entity, ref MoveInputEvent args)
|
||||
{
|
||||
// Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume
|
||||
// they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.HasDirectionalMovement && entity.Comp.IsCurrentlyWaddling)
|
||||
{
|
||||
StopWaddling(entity);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Only start waddling if we're not currently AND we're actually moving.
|
||||
if (entity.Comp.IsCurrentlyWaddling || !args.HasDirectionalMovement)
|
||||
return;
|
||||
|
||||
entity.Comp.IsCurrentlyWaddling = true;
|
||||
|
||||
RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
|
||||
private void OnStood(Entity<WaddleAnimationComponent> entity, ref StoodEvent args)
|
||||
{
|
||||
// Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume
|
||||
// they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mover.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.None)
|
||||
return;
|
||||
|
||||
if (entity.Comp.IsCurrentlyWaddling)
|
||||
return;
|
||||
|
||||
entity.Comp.IsCurrentlyWaddling = true;
|
||||
|
||||
RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
|
||||
private void StopWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
entity.Comp.IsCurrentlyWaddling = false;
|
||||
|
||||
RaiseNetworkEvent(new StoppedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
public abstract partial class AntagonistRoleComponent : Component
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public string? PrototypeId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the antagonist role component as being exclusive
|
||||
/// IE by default other antagonists should refuse to select the same entity for a different antag role
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[BaseTypeRequired(typeof(AntagonistRoleComponent))]
|
||||
public sealed partial class ExclusiveAntagonistAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Roles.Jobs;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind entities to hold the data for the player's current job.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class JobComponent : Component
|
||||
{
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public ProtoId<JobPrototype>? Prototype;
|
||||
}
|
||||
12
Content.Shared/Roles/Jobs/JobRoleComponent.cs
Normal file
12
Content.Shared/Roles/Jobs/JobRoleComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Roles.Jobs;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind role entities to mark them as a job role entity.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class JobRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
|
||||
}
|
||||
@@ -13,8 +13,10 @@ namespace Content.Shared.Roles.Jobs;
|
||||
/// </summary>
|
||||
public abstract class SharedJobSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly SharedPlayerSystem _playerSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
|
||||
private readonly Dictionary<string, string> _inverseTrackerLookup = new();
|
||||
|
||||
public override void Initialize()
|
||||
@@ -100,32 +102,44 @@ public abstract class SharedJobSystem : EntitySystem
|
||||
|
||||
public bool MindHasJobWithId(EntityUid? mindId, string prototypeId)
|
||||
{
|
||||
return CompOrNull<JobComponent>(mindId)?.Prototype == prototypeId;
|
||||
|
||||
MindRoleComponent? comp = null;
|
||||
if (mindId is null)
|
||||
return false;
|
||||
|
||||
_roles.MindHasRole<JobRoleComponent>(mindId.Value, out var role);
|
||||
|
||||
if (role is null)
|
||||
return false;
|
||||
|
||||
comp = role.Value.Comp;
|
||||
|
||||
return (comp.JobPrototype == prototypeId);
|
||||
}
|
||||
|
||||
public bool MindTryGetJob(
|
||||
[NotNullWhen(true)] EntityUid? mindId,
|
||||
[NotNullWhen(true)] out JobComponent? comp,
|
||||
[NotNullWhen(true)] out JobPrototype? prototype)
|
||||
{
|
||||
comp = null;
|
||||
prototype = null;
|
||||
MindTryGetJobId(mindId, out var protoId);
|
||||
|
||||
return TryComp(mindId, out comp) &&
|
||||
comp.Prototype != null &&
|
||||
_prototypes.TryIndex(comp.Prototype, out prototype);
|
||||
return (_prototypes.TryIndex<JobPrototype>(protoId, out prototype) || prototype is not null);
|
||||
}
|
||||
|
||||
public bool MindTryGetJobId([NotNullWhen(true)] EntityUid? mindId, out ProtoId<JobPrototype>? job)
|
||||
public bool MindTryGetJobId(
|
||||
[NotNullWhen(true)] EntityUid? mindId,
|
||||
out ProtoId<JobPrototype>? job)
|
||||
{
|
||||
if (!TryComp(mindId, out JobComponent? comp))
|
||||
{
|
||||
job = null;
|
||||
return false;
|
||||
}
|
||||
job = null;
|
||||
|
||||
job = comp.Prototype;
|
||||
return true;
|
||||
if (mindId is null)
|
||||
return false;
|
||||
|
||||
if (_roles.MindHasRole<JobRoleComponent>(mindId.Value, out var role))
|
||||
job = role.Value.Comp.JobPrototype;
|
||||
|
||||
return (job is not null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -134,7 +148,7 @@ public abstract class SharedJobSystem : EntitySystem
|
||||
/// </summary>
|
||||
public bool MindTryGetJobName([NotNullWhen(true)] EntityUid? mindId, out string name)
|
||||
{
|
||||
if (MindTryGetJob(mindId, out _, out var prototype))
|
||||
if (MindTryGetJob(mindId, out var prototype))
|
||||
{
|
||||
name = prototype.LocalizedName;
|
||||
return true;
|
||||
@@ -161,7 +175,7 @@ public abstract class SharedJobSystem : EntitySystem
|
||||
if (_playerSystem.ContentData(player) is not { Mind: { } mindId })
|
||||
return true;
|
||||
|
||||
if (!MindTryGetJob(mindId, out _, out var prototype))
|
||||
if (!MindTryGetJob(mindId, out var prototype))
|
||||
return true;
|
||||
|
||||
return prototype.CanBeAntag;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Shared.Roles;
|
||||
/// </summary>
|
||||
/// <param name="Roles">The list of roles on the player.</param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct MindGetAllRolesEvent(List<RoleInfo> Roles);
|
||||
public readonly record struct MindGetAllRoleInfoEvent(List<RoleInfo> Roles);
|
||||
|
||||
/// <summary>
|
||||
/// Returned by <see cref="MindGetAllRolesEvent"/> to give some information about a player's role.
|
||||
@@ -17,4 +17,4 @@ public readonly record struct MindGetAllRolesEvent(List<RoleInfo> Roles);
|
||||
/// <param name="Antagonist">Whether or not this role makes this player an antagonist.</param>
|
||||
/// <param name="PlayTimeTrackerId">The <see cref="PlayTimeTrackerPrototype"/> id associated with the role.</param>
|
||||
/// <param name="Prototype">The prototype ID of the role</param>
|
||||
public readonly record struct RoleInfo(Component Component, string Name, bool Antagonist, string? PlayTimeTrackerId, string Prototype);
|
||||
public readonly record struct RoleInfo(string Name, bool Antagonist, string? PlayTimeTrackerId, string Prototype);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user