ninja criminal records hacking (#24982)
* more humour * spotted a troll * add TryFindObjective to MindSystem * replace copypaste bool conditions with CodeCondition * use CodeConditionSystem in ninja + add handling for criminal hack * add criminal records hacking * update objectives * :trollface: --------- Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
using Content.Shared.CriminalRecords.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.CriminalRecords.Systems;
|
||||||
|
|
||||||
|
public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Content.Server.Chat.Systems;
|
||||||
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Server.StationRecords.Systems;
|
||||||
|
using Content.Shared.CriminalRecords;
|
||||||
|
using Content.Shared.CriminalRecords.Components;
|
||||||
|
using Content.Shared.CriminalRecords.Systems;
|
||||||
|
using Content.Shared.Dataset;
|
||||||
|
using Content.Shared.Security;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.CriminalRecords.Systems;
|
||||||
|
|
||||||
|
public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly StationSystem _station = default!;
|
||||||
|
[Dependency] private readonly StationRecordsSystem _records = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<CriminalRecordsHackerComponent, CriminalRecordsHackDoAfterEvent>(OnDoAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDoAfter(Entity<CriminalRecordsHackerComponent> ent, ref CriminalRecordsHackDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled || args.Handled || args.Target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_station.GetOwningStation(ent) is not {} station)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var reasons = _proto.Index<DatasetPrototype>(ent.Comp.Reasons);
|
||||||
|
foreach (var (key, record) in _records.GetRecordsOfType<CriminalRecord>(station))
|
||||||
|
{
|
||||||
|
var reason = _random.Pick(reasons.Values);
|
||||||
|
record.Status = SecurityStatus.Wanted;
|
||||||
|
record.Reason = reason;
|
||||||
|
// no radio message since spam
|
||||||
|
// no history since lazy and its easy to remove anyway
|
||||||
|
// main damage with this is existing arrest warrants are lost and to anger beepsky
|
||||||
|
}
|
||||||
|
|
||||||
|
_chat.DispatchGlobalAnnouncement(Loc.GetString(ent.Comp.Announcement), playSound: true, colorOverride: Color.Red);
|
||||||
|
|
||||||
|
// once is enough
|
||||||
|
RemComp<CriminalRecordsHackerComponent>(ent);
|
||||||
|
|
||||||
|
var ev = new CriminalRecordsHackedEvent(ent, args.Target.Value);
|
||||||
|
RaiseLocalEvent(args.User, ref ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the user after hacking a criminal records console.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct CriminalRecordsHackedEvent(EntityUid User, EntityUid Target);
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
using Content.Server.Communications;
|
using Content.Server.Communications;
|
||||||
using Content.Server.Mind;
|
using Content.Server.Mind;
|
||||||
using Content.Server.Ninja.Events;
|
using Content.Server.Ninja.Events;
|
||||||
using Content.Server.Objectives.Components;
|
using Content.Server.Objectives.Systems;
|
||||||
using Content.Shared.Communications;
|
using Content.Shared.Communications;
|
||||||
|
using Content.Shared.CriminalRecords.Components;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Content.Shared.Research.Components;
|
using Content.Shared.Research.Components;
|
||||||
@@ -16,6 +17,7 @@ namespace Content.Server.Ninja.Systems;
|
|||||||
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly EmagProviderSystem _emagProvider = default!;
|
[Dependency] private readonly EmagProviderSystem _emagProvider = default!;
|
||||||
|
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
||||||
[Dependency] private readonly CommsHackerSystem _commsHacker = default!;
|
[Dependency] private readonly CommsHackerSystem _commsHacker = default!;
|
||||||
[Dependency] private readonly MindSystem _mind = default!;
|
[Dependency] private readonly MindSystem _mind = default!;
|
||||||
[Dependency] private readonly SharedStunProviderSystem _stunProvider = default!;
|
[Dependency] private readonly SharedStunProviderSystem _stunProvider = default!;
|
||||||
@@ -88,12 +90,16 @@ public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
|
|||||||
|
|
||||||
EnsureComp<ResearchStealerComponent>(user);
|
EnsureComp<ResearchStealerComponent>(user);
|
||||||
// prevent calling in multiple threats by toggling gloves after
|
// prevent calling in multiple threats by toggling gloves after
|
||||||
if (_mind.TryGetObjectiveComp<TerrorConditionComponent>(user, out var obj) && !obj.CalledInThreat)
|
if (!_codeCondition.IsCompleted(user, ninja.TerrorObjective))
|
||||||
{
|
{
|
||||||
var hacker = EnsureComp<CommsHackerComponent>(user);
|
var hacker = EnsureComp<CommsHackerComponent>(user);
|
||||||
var rule = _ninja.NinjaRule(user);
|
var rule = _ninja.NinjaRule(user);
|
||||||
if (rule != null)
|
if (rule != null)
|
||||||
_commsHacker.SetThreats(user, rule.Threats, hacker);
|
_commsHacker.SetThreats(user, rule.Threats, hacker);
|
||||||
}
|
}
|
||||||
|
if (!_codeCondition.IsCompleted(user, ninja.MassArrestObjective))
|
||||||
|
{
|
||||||
|
EnsureComp<CriminalRecordsHackerComponent>(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
using Content.Server.Communications;
|
using Content.Server.Communications;
|
||||||
using Content.Server.Chat.Managers;
|
using Content.Server.Chat.Managers;
|
||||||
|
using Content.Server.CriminalRecords.Systems;
|
||||||
using Content.Server.GameTicking.Rules.Components;
|
using Content.Server.GameTicking.Rules.Components;
|
||||||
|
using Content.Server.GenericAntag;
|
||||||
|
using Content.Server.Objectives.Components;
|
||||||
|
using Content.Server.Objectives.Systems;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
using Content.Server.Research.Systems;
|
using Content.Server.Research.Systems;
|
||||||
using Content.Server.Roles;
|
using Content.Server.Roles;
|
||||||
using Content.Server.GenericAntag;
|
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
@@ -19,7 +22,6 @@ using Content.Shared.Rounding;
|
|||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Server.Objectives.Components;
|
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
|
||||||
namespace Content.Server.Ninja.Systems;
|
namespace Content.Server.Ninja.Systems;
|
||||||
@@ -28,7 +30,6 @@ namespace Content.Server.Ninja.Systems;
|
|||||||
// engi -> saboteur
|
// engi -> saboteur
|
||||||
// medi -> idk reskin it
|
// medi -> idk reskin it
|
||||||
// other -> assault
|
// other -> assault
|
||||||
// TODO: when criminal records is merged, hack it to set everyone to arrest
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use.
|
/// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use.
|
||||||
@@ -37,6 +38,7 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||||
[Dependency] private readonly BatterySystem _battery = default!;
|
[Dependency] private readonly BatterySystem _battery = default!;
|
||||||
|
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
|
||||||
[Dependency] private readonly IChatManager _chatMan = default!;
|
[Dependency] private readonly IChatManager _chatMan = default!;
|
||||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||||
[Dependency] private readonly RoleSystem _role = default!;
|
[Dependency] private readonly RoleSystem _role = default!;
|
||||||
@@ -52,6 +54,7 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|||||||
SubscribeLocalEvent<SpaceNinjaComponent, EmaggedSomethingEvent>(OnDoorjack);
|
SubscribeLocalEvent<SpaceNinjaComponent, EmaggedSomethingEvent>(OnDoorjack);
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, ResearchStolenEvent>(OnResearchStolen);
|
SubscribeLocalEvent<SpaceNinjaComponent, ResearchStolenEvent>(OnResearchStolen);
|
||||||
SubscribeLocalEvent<SpaceNinjaComponent, ThreatCalledInEvent>(OnThreatCalledIn);
|
SubscribeLocalEvent<SpaceNinjaComponent, ThreatCalledInEvent>(OnThreatCalledIn);
|
||||||
|
SubscribeLocalEvent<SpaceNinjaComponent, CriminalRecordsHackedEvent>(OnCriminalRecordsHacked);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
@@ -216,11 +219,21 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
|
|||||||
Popup.PopupEntity(str, uid, uid, PopupType.Medium);
|
Popup.PopupEntity(str, uid, uid, PopupType.Medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnThreatCalledIn(EntityUid uid, SpaceNinjaComponent comp, ref ThreatCalledInEvent args)
|
private void OnThreatCalledIn(Entity<SpaceNinjaComponent> ent, ref ThreatCalledInEvent args)
|
||||||
{
|
{
|
||||||
if (_mind.TryGetObjectiveComp<TerrorConditionComponent>(uid, out var obj))
|
_codeCondition.SetCompleted(ent.Owner, ent.Comp.TerrorObjective);
|
||||||
{
|
}
|
||||||
obj.CalledInThreat = true;
|
|
||||||
}
|
private void OnCriminalRecordsHacked(Entity<SpaceNinjaComponent> ent, ref CriminalRecordsHackedEvent args)
|
||||||
|
{
|
||||||
|
_codeCondition.SetCompleted(ent.Owner, ent.Comp.MassArrestObjective);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by <see cref="SpiderChargeSystem"/> when it detonates.
|
||||||
|
/// </summary>
|
||||||
|
public void DetonatedSpiderCharge(Entity<SpaceNinjaComponent> ent)
|
||||||
|
{
|
||||||
|
_codeCondition.SetCompleted(ent.Owner, ent.Comp.SpiderChargeObjective);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public sealed class SpiderChargeSystem : EntitySystem
|
|||||||
[Dependency] private readonly MindSystem _mind = default!;
|
[Dependency] private readonly MindSystem _mind = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly SpaceNinjaSystem _ninja = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -76,10 +77,10 @@ public sealed class SpiderChargeSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnExplode(EntityUid uid, SpiderChargeComponent comp, TriggerEvent args)
|
private void OnExplode(EntityUid uid, SpiderChargeComponent comp, TriggerEvent args)
|
||||||
{
|
{
|
||||||
if (comp.Planter == null || !_mind.TryGetObjectiveComp<SpiderChargeConditionComponent>(comp.Planter.Value, out var obj))
|
if (!TryComp<SpaceNinjaComponent>(comp.Planter, out var ninja))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// assumes the target was destroyed, that the charge wasn't moved somehow
|
// assumes the target was destroyed, that the charge wasn't moved somehow
|
||||||
obj.Detonated = true;
|
_ninja.DetonatedSpiderCharge((comp.Planter.Value, ninja));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
Content.Server/Objectives/Components/CodeConditionSystem.cs
Normal file
17
Content.Server/Objectives/Components/CodeConditionSystem.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Content.Server.Objectives.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.Objectives.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An objective that is set to complete by code in another system.
|
||||||
|
/// Use <see cref="CodeConditionSystem"/> to check and set this.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(CodeConditionSystem))]
|
||||||
|
public sealed partial class CodeConditionComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the objective is complete or not.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Completed;
|
||||||
|
}
|
||||||
@@ -9,9 +9,6 @@ namespace Content.Server.Objectives.Components;
|
|||||||
[RegisterComponent, Access(typeof(NinjaConditionsSystem), typeof(SpiderChargeSystem), typeof(SpaceNinjaSystem))]
|
[RegisterComponent, Access(typeof(NinjaConditionsSystem), typeof(SpiderChargeSystem), typeof(SpaceNinjaSystem))]
|
||||||
public sealed partial class SpiderChargeConditionComponent : Component
|
public sealed partial class SpiderChargeConditionComponent : Component
|
||||||
{
|
{
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool Detonated;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Warp point that the spider charge has to target
|
/// Warp point that the spider charge has to target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using Content.Server.Objectives.Systems;
|
|
||||||
using Content.Shared.Ninja.Systems;
|
|
||||||
|
|
||||||
namespace Content.Server.Objectives.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Requires that the player is a ninja and has called in a threat.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent, Access(typeof(NinjaConditionsSystem), typeof(SharedSpaceNinjaSystem))]
|
|
||||||
public sealed partial class TerrorConditionComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the comms console has been hacked
|
|
||||||
/// </summary>
|
|
||||||
[DataField("calledInThreat"), ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool CalledInThreat;
|
|
||||||
}
|
|
||||||
76
Content.Server/Objectives/Systems/CodeConditionSystem.cs
Normal file
76
Content.Server/Objectives/Systems/CodeConditionSystem.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using Content.Server.Objectives.Components;
|
||||||
|
using Content.Shared.Objectives.Components;
|
||||||
|
using Content.Shared.Mind;
|
||||||
|
using Content.Shared.Mind.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Objectives.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles <see cref="CodeConditionComponent"/> progress and provides API for systems to use.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CodeConditionSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<CodeConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetProgress(Entity<CodeConditionComponent> ent, ref ObjectiveGetProgressEvent args)
|
||||||
|
{
|
||||||
|
args.Progress = ent.Comp.Completed ? 1f : 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether an objective is completed.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompleted(Entity<CodeConditionComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ent.Comp.Completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if a mob's objective with a certain prototype is completed.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompleted(Entity<MindContainerComponent?> mob, string prototype)
|
||||||
|
{
|
||||||
|
if (_mind.GetMind(mob, mob.Comp) is not {} mindId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_mind.TryFindObjective(mindId, prototype, out var obj))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsCompleted(obj.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets an objective's completed field.
|
||||||
|
/// </summary>
|
||||||
|
public void SetCompleted(Entity<CodeConditionComponent?> ent, bool completed = true)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.Completed = completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a mob's objective to complete.
|
||||||
|
/// </summary>
|
||||||
|
public void SetCompleted(Entity<MindContainerComponent?> mob, string prototype, bool completed = true)
|
||||||
|
{
|
||||||
|
if (_mind.GetMind(mob, mob.Comp) is not {} mindId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_mind.TryFindObjective(mindId, prototype, out var obj))
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetCompleted(obj.Value, completed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,11 +23,8 @@ public sealed class NinjaConditionsSystem : EntitySystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<SpiderChargeConditionComponent, RequirementCheckEvent>(OnSpiderChargeRequirementCheck);
|
SubscribeLocalEvent<SpiderChargeConditionComponent, RequirementCheckEvent>(OnSpiderChargeRequirementCheck);
|
||||||
SubscribeLocalEvent<SpiderChargeConditionComponent, ObjectiveAfterAssignEvent>(OnSpiderChargeAfterAssign);
|
SubscribeLocalEvent<SpiderChargeConditionComponent, ObjectiveAfterAssignEvent>(OnSpiderChargeAfterAssign);
|
||||||
SubscribeLocalEvent<SpiderChargeConditionComponent, ObjectiveGetProgressEvent>(OnSpiderChargeGetProgress);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<StealResearchConditionComponent, ObjectiveGetProgressEvent>(OnStealResearchGetProgress);
|
SubscribeLocalEvent<StealResearchConditionComponent, ObjectiveGetProgressEvent>(OnStealResearchGetProgress);
|
||||||
|
|
||||||
SubscribeLocalEvent<TerrorConditionComponent, ObjectiveGetProgressEvent>(OnTerrorGetProgress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// doorjack
|
// doorjack
|
||||||
@@ -88,11 +85,6 @@ public sealed class NinjaConditionsSystem : EntitySystem
|
|||||||
_metaData.SetEntityName(uid, title, args.Meta);
|
_metaData.SetEntityName(uid, title, args.Meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSpiderChargeGetProgress(EntityUid uid, SpiderChargeConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
|
||||||
{
|
|
||||||
args.Progress = comp.Detonated ? 1f : 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// steal research
|
// steal research
|
||||||
|
|
||||||
private void OnStealResearchGetProgress(EntityUid uid, StealResearchConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
private void OnStealResearchGetProgress(EntityUid uid, StealResearchConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||||
@@ -108,9 +100,4 @@ public sealed class NinjaConditionsSystem : EntitySystem
|
|||||||
|
|
||||||
return MathF.Min(comp.DownloadedNodes.Count / (float) target, 1f);
|
return MathF.Min(comp.DownloadedNodes.Count / (float) target, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTerrorGetProgress(EntityUid uid, TerrorConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
|
||||||
{
|
|
||||||
args.Progress = comp.CalledInThreat ? 1f : 0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Content.Shared.CriminalRecords.Systems;
|
||||||
|
using Content.Shared.Dataset;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.CriminalRecords.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lets the user hack a criminal records console, once.
|
||||||
|
/// Everyone is set to wanted with a randomly picked reason.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedCriminalRecordsHackerSystem))]
|
||||||
|
public sealed partial class CriminalRecordsHackerComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How long the doafter is for hacking it.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan Delay = TimeSpan.FromSeconds(20);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dataset of random reasons to use.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<DatasetPrototype> Reasons = "CriminalRecordsWantedReasonPlaceholders";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Announcement made after the console is hacked.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId Announcement = "ninja-criminal-records-hack-announcement";
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using Content.Shared.CriminalRecords.Components;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Ninja.Systems;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.CriminalRecords.Systems;
|
||||||
|
|
||||||
|
public abstract class SharedCriminalRecordsHackerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||||
|
[Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<CriminalRecordsHackerComponent, BeforeInteractHandEvent>(OnBeforeInteractHand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeforeInteractHand(Entity<CriminalRecordsHackerComponent> ent, ref BeforeInteractHandEvent args)
|
||||||
|
{
|
||||||
|
// TODO: generic event
|
||||||
|
if (args.Handled || !_gloves.AbilityCheck(ent, args, out var target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HasComp<CriminalRecordsConsoleComponent>(target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var doAfterArgs = new DoAfterArgs(EntityManager, ent, ent.Comp.Delay, new CriminalRecordsHackDoAfterEvent(), target: target, used: ent, eventTarget: ent)
|
||||||
|
{
|
||||||
|
BreakOnDamage = true,
|
||||||
|
BreakOnUserMove = true,
|
||||||
|
MovementThreshold = 0.5f
|
||||||
|
};
|
||||||
|
|
||||||
|
_doAfter.TryStartDoAfter(doAfterArgs);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the user when the doafter completes.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class CriminalRecordsHackDoAfterEvent : SimpleDoAfterEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -383,6 +383,30 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find an objective that has the same prototype as the argument.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Will not work for objectives that have no prototype, or duplicate objectives with the same prototype.
|
||||||
|
/// <//remarks>
|
||||||
|
public bool TryFindObjective(Entity<MindComponent?> mind, string prototype, [NotNullWhen(true)] out EntityUid? objective)
|
||||||
|
{
|
||||||
|
objective = null;
|
||||||
|
if (!Resolve(mind, ref mind.Comp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var uid in mind.Comp.Objectives)
|
||||||
|
{
|
||||||
|
if (MetaData(uid).EntityPrototype?.ID == prototype)
|
||||||
|
{
|
||||||
|
objective = uid;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetSession(EntityUid? mindId, [NotNullWhen(true)] out ICommonSession? session)
|
public bool TryGetSession(EntityUid? mindId, [NotNullWhen(true)] out ICommonSession? session)
|
||||||
{
|
{
|
||||||
session = null;
|
session = null;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Ninja.Components;
|
namespace Content.Shared.Ninja.Components;
|
||||||
|
|
||||||
@@ -35,4 +35,22 @@ public sealed partial class SpaceNinjaComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("katana"), AutoNetworkedField]
|
[DataField("katana"), AutoNetworkedField]
|
||||||
public EntityUid? Katana;
|
public EntityUid? Katana;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Objective to complete after calling in a threat.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntProtoId TerrorObjective = "TerrorObjective";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Objective to complete after setting everyone to arrest.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntProtoId MassArrestObjective = "MassArrestObjective";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Objective to complete after the spider charge detonates.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntProtoId SpiderChargeObjective = "SpiderChargeObjective";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.Communications;
|
using Content.Shared.Communications;
|
||||||
|
using Content.Shared.CriminalRecords.Components;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
@@ -62,6 +63,7 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
|
|||||||
RemComp<StunProviderComponent>(user);
|
RemComp<StunProviderComponent>(user);
|
||||||
RemComp<ResearchStealerComponent>(user);
|
RemComp<ResearchStealerComponent>(user);
|
||||||
RemComp<CommsHackerComponent>(user);
|
RemComp<CommsHackerComponent>(user);
|
||||||
|
RemComp<CriminalRecordsHackerComponent>(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,3 +4,5 @@ ninja-suit-cooldown = The suit needs time to recuperate from the last attack.
|
|||||||
|
|
||||||
ninja-research-steal-fail = No new research nodes were stolen...
|
ninja-research-steal-fail = No new research nodes were stolen...
|
||||||
ninja-research-steal-success = Stole {$count} new nodes from {THE($server)}.
|
ninja-research-steal-success = Stole {$count} new nodes from {THE($server)}.
|
||||||
|
|
||||||
|
ninja-criminal-records-hack-announcement = ERROR: Criminal records has detected a [REDACTED] error #*;"
|
||||||
|
|||||||
@@ -2,17 +2,23 @@
|
|||||||
- type: dataset
|
- type: dataset
|
||||||
id: CriminalRecordsWantedReasonPlaceholders
|
id: CriminalRecordsWantedReasonPlaceholders
|
||||||
values:
|
values:
|
||||||
|
- Ate a delicious valid salad
|
||||||
- Ate their own shoes
|
- Ate their own shoes
|
||||||
- Being a clown
|
- Being a clown
|
||||||
- Being a mime
|
- Being a mime
|
||||||
- Breathed the wrong way
|
- Breathed the wrong way
|
||||||
- Broke into evac
|
- Broke into evac
|
||||||
- Did literally nothing
|
- Did literally nothing
|
||||||
|
- Did their job
|
||||||
- Didn't say hello to me
|
- Didn't say hello to me
|
||||||
- Drank one too many
|
- Drank one too many
|
||||||
|
- Had two toolboxes, that's too many
|
||||||
- Lied on common radio
|
- Lied on common radio
|
||||||
- Looked at me funny
|
- Looked at me funny
|
||||||
|
- Lubed up the entire way to evac
|
||||||
|
- Set AME up on time
|
||||||
- Slipped the HoS
|
- Slipped the HoS
|
||||||
- Stole the clown's mask
|
- Stole the clown's mask
|
||||||
- Told an unfunny joke
|
- Told an unfunny joke
|
||||||
- Wore a gasmask
|
- Wore a gasmask
|
||||||
|
- Wore boxing gloves
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
- DoorjackObjective
|
- DoorjackObjective
|
||||||
- SpiderChargeObjective
|
- SpiderChargeObjective
|
||||||
- TerrorObjective
|
- TerrorObjective
|
||||||
|
- MassArrestObjective
|
||||||
- NinjaSurviveObjective
|
- NinjaSurviveObjective
|
||||||
- type: NinjaRule
|
- type: NinjaRule
|
||||||
threats: NinjaThreats
|
threats: NinjaThreats
|
||||||
|
|||||||
@@ -103,3 +103,11 @@
|
|||||||
id: BaseSurviveObjective
|
id: BaseSurviveObjective
|
||||||
components:
|
components:
|
||||||
- type: SurviveCondition
|
- type: SurviveCondition
|
||||||
|
|
||||||
|
# objective progress is controlled by a system and not the objective itself
|
||||||
|
- type: entity
|
||||||
|
abstract: true
|
||||||
|
parent: BaseObjective
|
||||||
|
id: BaseCodeObjective
|
||||||
|
components:
|
||||||
|
- type: CodeCondition
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
noSpawn: true
|
noSpawn: true
|
||||||
parent: BaseNinjaObjective
|
parent: [BaseNinjaObjective, BaseCodeObjective]
|
||||||
id: SpiderChargeObjective
|
id: SpiderChargeObjective
|
||||||
description: This bomb can be detonated in a specific location. Note that the bomb will not work anywhere else!
|
description: This bomb can be detonated in a specific location. Note that the bomb will not work anywhere else!
|
||||||
components:
|
components:
|
||||||
@@ -54,7 +54,6 @@
|
|||||||
icon:
|
icon:
|
||||||
sprite: Objects/Weapons/Bombs/spidercharge.rsi
|
sprite: Objects/Weapons/Bombs/spidercharge.rsi
|
||||||
state: icon
|
state: icon
|
||||||
- type: SpiderChargeCondition
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
noSpawn: true
|
noSpawn: true
|
||||||
@@ -70,7 +69,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
noSpawn: true
|
noSpawn: true
|
||||||
parent: BaseNinjaObjective
|
parent: [BaseNinjaObjective, BaseCodeObjective]
|
||||||
id: TerrorObjective
|
id: TerrorObjective
|
||||||
name: Call in a threat
|
name: Call in a threat
|
||||||
description: Use your gloves on a communication console in order to bring another threat to the station.
|
description: Use your gloves on a communication console in order to bring another threat to the station.
|
||||||
@@ -79,4 +78,15 @@
|
|||||||
icon:
|
icon:
|
||||||
sprite: Objects/Fun/Instruments/otherinstruments.rsi
|
sprite: Objects/Fun/Instruments/otherinstruments.rsi
|
||||||
state: red_phone
|
state: red_phone
|
||||||
- type: TerrorCondition
|
|
||||||
|
- type: entity
|
||||||
|
noSpawn: true
|
||||||
|
parent: [BaseNinjaObjective, BaseCodeObjective]
|
||||||
|
id: MassArrestObjective
|
||||||
|
name: Set everyone to wanted
|
||||||
|
description: Use your gloves to hack a criminal records console, setting the entire station to be wanted!
|
||||||
|
components:
|
||||||
|
- type: Objective
|
||||||
|
icon:
|
||||||
|
sprite: Objects/Weapons/Melee/stunbaton.rsi
|
||||||
|
state: stunbaton_on
|
||||||
|
|||||||
Reference in New Issue
Block a user