Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fugitives redux #1413

Merged
merged 18 commits into from
Jun 30, 2024
2 changes: 1 addition & 1 deletion Content.Client/Thief/ThiefBackpackMenu.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
MinSize="700 700">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
<!-- First Informational panel -->
<Label Text="{Loc 'thief-backpack-window-description'}" Margin="5 5"/>
<Label Name="Description" Margin="5 5"/>
<controls:HLine Color="#404040" Thickness="2" Margin="0 5"/>
<Label Name="SelectedSets" Text="{Loc 'thief-backpack-window-selected'}" Margin="5 5"/>

Expand Down
1 change: 1 addition & 0 deletions Content.Client/Thief/ThiefBackpackMenu.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public void UpdateState(ThiefBackpackBoundUserInterfaceState state)
selectedNumber++;
}

Description.Text = Loc.GetString("thief-backpack-window-description", ("maxCount", state.MaxSelectedSets));
SelectedSets.Text = Loc.GetString("thief-backpack-window-selected", ("selectedCount", selectedNumber), ("maxCount", state.MaxSelectedSets));
ApproveButton.Disabled = selectedNumber == state.MaxSelectedSets ? false : true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Content.Shared.Dataset;
using Content.Server.StationEvents.Events;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Utility;

namespace Content.Server.StationEvents.Components;

/// <summary>
/// Makes a GALPOL announcement and creates a report some time after an antag spawns.
/// Removed after this is done.
/// </summary>
[RegisterComponent, Access(typeof(FugitiveRule))]
[AutoGenerateComponentPause]
public sealed partial class FugitiveRuleComponent : Component
{
[DataField]
public LocId Announcement = "station-event-fugitive-hunt-announcement";

[DataField]
public LocId Sender = "fugitive-announcement-GALPOL";

[DataField]
public Color Color = Color.Yellow;

/// <summary>
/// Report paper to spawn. Its content is generated from the fugitive.
/// </summary>
[DataField]
public EntProtoId ReportPaper = "PaperFugitiveReport";

/// <summary>
/// How long to wait after the antag spawns before announcing it.
/// </summary>
[DataField]
public TimeSpan AnnounceDelay = TimeSpan.FromMinutes(5);

/// <summary>
/// Station to give the report to.
/// </summary>
[DataField]
public EntityUid? Station;

/// <summary>
/// The report generated for the spawned fugitive.
/// </summary>
[DataField]
public string Report = string.Empty;

/// <summary>
/// When the announcement will be made, if an antag has spawned yet.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan? NextAnnounce;

/// <summary>
/// Dataset to pick crimes on the report from.
/// </summary>
[DataField]
public ProtoId<LocalizedDatasetPrototype> CrimesDataset = "FugitiveCrimes";

/// <summary>
/// Max number of unique crimes they can be charged with.
/// Does not affect the counts of each crime.
/// </summary>
[DataField]
public int MinCrimes = 2;

/// <summary>
/// Min number of unique crimes they can be charged with.
/// Does not affect the counts of each crime.
/// </summary>
[DataField]
public int MaxCrimes = 7;

/// <summary>
/// Min counts of each crime that can be rolled.
/// </summary>
[DataField]
public int MinCounts = 1;

/// <summary>
/// Max counts of each crime that can be rolled.
/// </summary>
[DataField]
public int MaxCounts = 4;
}
129 changes: 129 additions & 0 deletions Content.Server/DeltaV/StationEvents/Events/FugitiveRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using Content.Server.Antag;
using Content.Server.Communications;
using Content.Server.GameTicking.Components; // TODO: Shared when upstream merged
using Content.Server.Paper;
using Content.Server.StationEvents.Components;
using Content.Shared.Ghost;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Popups;
using Content.Shared.Random.Helpers;
using Robust.Shared.Physics.Components;
using Robust.Shared.Utility;

namespace Content.Server.StationEvents.Events;

public sealed class FugitiveRule : StationEventSystem<FugitiveRuleComponent>
{
[Dependency] private readonly PaperSystem _paper = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<FugitiveRuleComponent, AfterAntagEntitySelectedEvent>(OnEntitySelected);
}

protected override void ActiveTick(EntityUid uid, FugitiveRuleComponent comp, GameRuleComponent rule, float frameTime)
{
if (comp.NextAnnounce is not {} next || next > Timing.CurTime)
return;

var announcement = Loc.GetString(comp.Announcement);
var sender = Loc.GetString(comp.Sender);
ChatSystem.DispatchGlobalAnnouncement(announcement, sender: sender, colorOverride: comp.Color);

// send the report to every comms console on the station
var query = EntityQueryEnumerator<TransformComponent, CommunicationsConsoleComponent>();
var consoles = new List<TransformComponent>();
while (query.MoveNext(out var console, out var xform, out _))
{
if (StationSystem.GetOwningStation(console, xform) != comp.Station || HasComp<GhostComponent>(console))
continue;

consoles.Add(xform);
}

foreach (var xform in consoles)
{
var report = Spawn(comp.ReportPaper, xform.Coordinates);
_paper.SetContent(report, comp.Report);
}

// prevent any possible funnies
comp.NextAnnounce = null;

RemCompDeferred(uid, comp);
}

private void OnEntitySelected(Entity<FugitiveRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
if (ent.Comp.NextAnnounce != null)
{
Log.Error("Fugitive rule spawning multiple fugitives isn't supported, sorry.");
return;
}

var fugi = args.EntityUid;
ent.Comp.Report = GenerateReport(fugi, ent.Comp).ToMarkup();
ent.Comp.Station = StationSystem.GetOwningStation(fugi);
ent.Comp.NextAnnounce = Timing.CurTime + ent.Comp.AnnounceDelay;

_popup.PopupEntity(Loc.GetString("fugitive-spawn"), fugi, fugi);
}

private FormattedMessage GenerateReport(EntityUid uid, FugitiveRuleComponent rule)
{
var report = new FormattedMessage();
report.PushMarkup(Loc.GetString("fugitive-report-title", ("name", uid)));
report.PushNewline();
report.PushMarkup(Loc.GetString("fugitive-report-first-line", ("name", uid)));
report.PushNewline();

if (!TryComp<HumanoidAppearanceComponent>(uid, out var humanoid))
{
report.AddMarkup(Loc.GetString("fugitive-report-inhuman", ("name", uid)));
return report;
}

var species = PrototypeManager.Index(humanoid.Species);

report.PushMarkup(Loc.GetString("fugitive-report-morphotype", ("species", Loc.GetString(species.Name))));
report.PushMarkup(Loc.GetString("fugitive-report-age", ("age", humanoid.Age)));
report.PushMarkup(Loc.GetString("fugitive-report-sex", ("sex", humanoid.Sex.ToString())));

if (TryComp<PhysicsComponent>(uid, out var physics))
report.PushMarkup(Loc.GetString("fugitive-report-weight", ("weight", Math.Round(physics.FixturesMass))));

report.PushNewline();
report.PushMarkup(Loc.GetString("fugitive-report-crimes-header"));

// generate some random crimes to avoid this situation
// "officer what are my charges?"
// "uh i dunno a piece of paper said to arrest you thats it"
AddCharges(report, rule);

report.PushNewline();
report.AddMarkup(Loc.GetString("fugitive-report-last-line"));

return report;
}

private void AddCharges(FormattedMessage report, FugitiveRuleComponent rule)
{
var crimeTypes = PrototypeManager.Index(rule.CrimesDataset);
var crimes = new HashSet<LocId>();
var total = RobustRandom.Next(rule.MinCrimes, rule.MaxCrimes + 1);
while (crimes.Count < total)
{
crimes.Add(RobustRandom.Pick(crimeTypes));
}

foreach (var crime in crimes)
{
var count = RobustRandom.Next(rule.MinCounts, rule.MaxCounts + 1);
report.PushMarkup(Loc.GetString("fugitive-report-crime", ("crime", Loc.GetString(crime)), ("count", count)));
}
}
}
9 changes: 9 additions & 0 deletions Content.Server/Roles/FugitiveRoleComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Content.Shared.Roles;

namespace Content.Server.Roles;

/// <summary>
/// DeltaV - fugitive antag role
/// </summary>
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class FugitiveRoleComponent : AntagonistRoleComponent;
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ public sealed partial class ThiefUndeterminedBackpackComponent : Component

[DataField]
public SoundSpecifier ApproveSound = new SoundPathSpecifier("/Audio/Effects/rustle1.ogg");

/// <summary>
/// Max number of sets you can select.
/// </summary>
[DataField]
public int MaxSelectedSets = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public sealed class ThiefUndeterminedBackpackSystem : EntitySystem
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;

private const int MaxSelectedSets = 2;
public override void Initialize()
{
base.Initialize();
Expand All @@ -35,7 +34,7 @@ private void OnUIOpened(Entity<ThiefUndeterminedBackpackComponent> backpack, ref

private void OnApprove(Entity<ThiefUndeterminedBackpackComponent> backpack, ref ThiefBackpackApproveMessage args)
{
if (backpack.Comp.SelectedSets.Count != MaxSelectedSets)
if (backpack.Comp.SelectedSets.Count != backpack.Comp.MaxSelectedSets)
return;

foreach (var i in backpack.Comp.SelectedSets)
Expand Down Expand Up @@ -79,6 +78,6 @@ private void UpdateUI(EntityUid uid, ThiefUndeterminedBackpackComponent? compone
data.Add(i, info);
}

_ui.SetUiState(uid, ThiefBackpackUIKey.Key, new ThiefBackpackBoundUserInterfaceState(data, MaxSelectedSets));
_ui.SetUiState(uid, ThiefBackpackUIKey.Key, new ThiefBackpackBoundUserInterfaceState(data, component.MaxSelectedSets));
}
}
24 changes: 24 additions & 0 deletions Resources/Locale/en-US/deltav/fugitive/sets.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
fugitive-set-hitman-name = hitman's kit
fugitive-set-hitman-description =
You've taken lives before and are prepared to do so again if necessary.
Comes with a loaded viper and a spare mag.

fugitive-set-saboteur-name = saboteur's kit
fugitive-set-saboteur-description =
Making engineers miserable is your life's mission.
Comes with EMP grenades and a brick of C4.

fugitive-set-ghost-name = ghost's kit
fugitive-set-ghost-description =
Disappear in the middle of a chase to secure your freedom!
Comes with 2 smoke grenades and a scram implanter that can teleport you.

fugitive-set-leverage-name = leverage kit
fugitive-set-leverage-description =
Your years in the clown college taught you to slip security very well.
Use a death adicifier on you or a "friend" to get what you want!

fugitive-set-infiltrator-name = infiltrator's kit
fugitive-set-infiltrator-description =
Use an Agent ID to steal access from others and go anywhere.
Your freedom implanter can be used as a plan B if all else fails.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
fugitive-round-end-agent-name = Fugitive

fugitive-spawn = You fall from the ceiling!

station-event-fugitive-hunt-announcement = Please check communications consoles for a sensitive message.
fugitive-announcement-GALPOL = GALPOL

fugitive-report-title = WANTED: {$name}
fugitive-report-first-line = Escaped fugitive {$name} has been spotted in the sector. They may be a stowaway on a station somewhere.
fugitive-report-inhuman = {CAPITALIZE(THE($name))} {CONJUGATE-BE($name)} inhuman. We have no further details.
fugitive-report-morphotype = MORPHOTYPE: {$species}
fugitive-report-age = AGE: {$age}
fugitive-report-sex = SEX: {$sex ->
[Male] M
[Female] F
*[none] N/A
}
fugitive-report-weight = WEIGHT: {$weight} kg
fugitive-report-crimes-header = The above individual is wanted across the sector for the following:
fugitive-report-crime = - {$count ->
[1] One count
*[other] {$count} counts
} of {$crime}
fugitive-report-last-line = GALPOL prefers the fugitive to be returned alive so they may face trial at Central Command.

# All (non erp) felonies and capital crimes in Space Law as of June 2024
fugitive-crime-1 = Murder
fugitive-crime-2 = Terrorism
fugitive-crime-3 = Grand Sabotage
fugitive-crime-4 = Decorporealisation
fugitive-crime-5 = Kidnapping
fugitive-crime-6 = Sedition

fugitive-crime-7 = Manslaughter
fugitive-crime-8 = Grand Theft
fugitive-crime-9 = Black Marketeering
fugitive-crime-10 = Sabotage
fugitive-crime-11 = Mindbreaking
fugitive-crime-12 = Assault
fugitive-crime-13 = Abuse of Power
fugitive-crime-14 = Possession
fugitive-crime-15 = Endangerment
fugitive-crime-16 = Breaking and Entering
fugitive-crime-17 = Rioting
fugitive-crime-18 = Contempt of Court
fugitive-crime-19 = Perjury
fugitive-crime-20 = False Report
fugitive-crime-21 = Obstruction of Justice
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ ghost-role-information-listeningop-rules = You are a Syndicate Operative tasked
ghost-role-information-paradox-anomaly-name = Paradox Anomaly
ghost-role-information-paradox-anomaly-description = Replace your double, or befriend them.
ghost-role-information-paradox-anomaly-rules = Try and replace your twin with this funny roleplay antag rather than plasma flooding the station or something. You can also just befriend them.

ghost-role-information-fugitive-name = Fugitive
ghost-role-information-fugitive-description = You're an escaped prisoner. Make it out alive.
ghost-role-information-fugitive-rules = You are the lightest of antags, focus on laying low rather than engaging security directly. Don't murderbone.
5 changes: 4 additions & 1 deletion Resources/Locale/en-US/thief/backpack.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ thief-backpack-window-title = thief toolbox
thief-backpack-window-description =
This toolbox is filled with unspecified contents.
Now you need to remember what you put in it.
Choose 2 different sets from the list.
Choose {$maxCount} different {$maxCount ->
[1] set
*[other] sets
} from the list.

thief-backpack-window-selected = Kits selected: ({$selectedCount}/{$maxCount})

Expand Down
Loading
Loading