diff --git a/Content.Server/SS220/CriminalRecords/CriminalRecordSystem.cs b/Content.Server/SS220/CriminalRecords/CriminalRecordSystem.cs index f85f00565809b6..a3c421952c4398 100644 --- a/Content.Server/SS220/CriminalRecords/CriminalRecordSystem.cs +++ b/Content.Server/SS220/CriminalRecords/CriminalRecordSystem.cs @@ -15,7 +15,6 @@ using Content.Shared.SS220.Ghost; using Content.Shared.StationRecords; using Content.Shared.StatusIcon.Components; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -141,7 +140,9 @@ public void UpdateLastRecordTime(CriminalRecordCatalog catalog) catalog.LastRecordTime = biggest == -1 ? null : biggest; } - public void UpdateIdCards(StationRecordKey key, GeneralStationRecord generalRecord) + + /// Null if no IDCard was updated or Uid of updated IDCard + public EntityUid? UpdateIdCards(StationRecordKey key, GeneralStationRecord generalRecord) { CriminalRecord? criminalRecord = null; if (generalRecord.CriminalRecords != null) @@ -167,7 +168,9 @@ public void UpdateIdCards(StationRecordKey key, GeneralStationRecord generalReco idCard.CurrentSecurityRecord = criminalRecord; EntityManager.Dirty(uid, idCard); + return uid; } + return null; } public bool RemoveCriminalRecordStatus(StationRecordKey key, int time, EntityUid? sender = null) @@ -185,7 +188,9 @@ public bool RemoveCriminalRecordStatus(StationRecordKey key, int time, EntityUid UpdateLastRecordTime(catalog); _stationRecords.Synchronize(key.OriginStation); - UpdateIdCards(key, selectedRecord); + var cardId = UpdateIdCards(key, selectedRecord); + if (cardId.HasValue && TryGetLastRecord(key, out _, out var currentCriminalRecord)) + RaiseLocalEvent(cardId.Value, new CriminalStatusDeleted(sender, key, ref currentCriminalRecord)); if (sender != null) { @@ -255,7 +260,10 @@ public bool AddCriminalRecordStatus(StationRecordKey key, string message, string catalog.LastRecordTime = currentRoundTime; _stationRecords.Synchronize(key.OriginStation); - UpdateIdCards(key, selectedRecord); + var cardId = UpdateIdCards(key, selectedRecord); + if (cardId.HasValue) + RaiseLocalEvent(cardId.Value, new CriminalStatusAdded(sender, key, ref criminalRecord)); + _sawmill.Debug("Added new criminal record, synchonizing"); if (sender != null) diff --git a/Content.Server/SS220/CriminalRecords/CriminalRecordsEvent.cs b/Content.Server/SS220/CriminalRecords/CriminalRecordsEvent.cs new file mode 100644 index 00000000000000..7c7b00d2b22ca8 --- /dev/null +++ b/Content.Server/SS220/CriminalRecords/CriminalRecordsEvent.cs @@ -0,0 +1,27 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.SS220.CriminalRecords; +using Content.Shared.StationRecords; + +namespace Content.Server.SS220.CriminalRecords; + +public abstract class CriminalStatusEvent(EntityUid? sender, StationRecordKey key, ref CriminalRecord currentCriminalRecord) +{ + public EntityUid? Sender { get; } = sender; + public StationRecordKey Key { get; } = key; + public CriminalRecord CurrentCriminalRecord { get; } = currentCriminalRecord; +} + +/// +/// Just Look to its name +/// +public sealed class CriminalStatusAdded(EntityUid? sender, StationRecordKey key, ref CriminalRecord criminalRecord) + : CriminalStatusEvent(sender, key, ref criminalRecord) +{ } + +/// +/// Just Look to its name +/// +public sealed class CriminalStatusDeleted(EntityUid? sender, StationRecordKey key, ref CriminalRecord criminalRecord) + : CriminalStatusEvent(sender, key, ref criminalRecord) +{ } diff --git a/Content.Server/SS220/Objectives/Components/FramePersonConditionComponent.cs b/Content.Server/SS220/Objectives/Components/FramePersonConditionComponent.cs new file mode 100644 index 00000000000000..1bfbe2e0b3535e --- /dev/null +++ b/Content.Server/SS220/Objectives/Components/FramePersonConditionComponent.cs @@ -0,0 +1,14 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Server.SS220.Trackers.Components; + +namespace Content.Server.SS220.Objectives.Components; + +[RegisterComponent] +public sealed partial class FramePersonConditionComponent : Component +{ + [DataField(required: true)] + public CriminalStatusTrackerSpecifier CriminalStatusSpecifier = new(); + + public bool ObjectiveIsDone = false; +} diff --git a/Content.Server/SS220/Objectives/Components/IntimidatePersonConditionComponent.cs b/Content.Server/SS220/Objectives/Components/IntimidatePersonConditionComponent.cs new file mode 100644 index 00000000000000..4ee34d3c621dc5 --- /dev/null +++ b/Content.Server/SS220/Objectives/Components/IntimidatePersonConditionComponent.cs @@ -0,0 +1,46 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Server.SS220.Trackers.Components; + +namespace Content.Server.SS220.Objectives.Components; + +[RegisterComponent] +public sealed partial class IntimidatePersonConditionComponent : Component +{ + [DataField(required: true)] + public DamageTrackerSpecifier DamageTrackerSpecifier = new(); + + [ViewVariables(VVAccess.ReadWrite)] + public EntityUid TargetMob; + + [ViewVariables(VVAccess.ReadWrite)] + public bool ObjectiveIsDone = false; + + public IntimidatePersonDescriptionType DescriptionType; + // Descriptions comes to help player differ done object from one which isn't. + + /// + /// Description will be applied at start. No params in it + /// + [DataField(required: true)] + public string? StartDescription; + + /// + /// Description will be applied when objective is done. No params in it + /// + [DataField(required: true)] + public string? SuccessDescription; + + /// + /// Description will be applied when objective is done. No params in it + /// + [DataField(required: true)] + public string? SSDDescription; +} + +public enum IntimidatePersonDescriptionType +{ + Start = 0, + Success, + SSD +} diff --git a/Content.Server/SS220/Objectives/Systems/FramePersonConditionSystem.cs b/Content.Server/SS220/Objectives/Systems/FramePersonConditionSystem.cs new file mode 100644 index 00000000000000..49a2dfca3e16e4 --- /dev/null +++ b/Content.Server/SS220/Objectives/Systems/FramePersonConditionSystem.cs @@ -0,0 +1,140 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Server.Access.Systems; +using Content.Server.Mind; +using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; +using Content.Server.SS220.Objectives.Components; +using Content.Server.SS220.Trackers.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.SS220.Objectives.Systems; + +public sealed class FramePersonConditionSystem : EntitySystem +{ + [Dependency] private readonly IdCardSystem _idCard = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; + [Dependency] private readonly TargetObjectiveSystem _targetObjective = default!; + + /// + /// We use this to determine which jobs have legalImmunity... Wait for MRP PR for a special flag. + /// + private readonly string _legalImmunitySupervisors = "job-supervisors-centcom"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetProgress); + + SubscribeLocalEvent(OnPersonAssigned); + } + + private void OnGetProgress(Entity entity, ref ObjectiveGetProgressEvent args) + { + if (!_targetObjective.GetTarget(entity.Owner, out var target)) + return; + + if (entity.Comp.ObjectiveIsDone) + { + args.Progress = 1f; + return; + } + + if (!TryComp(target, out var mindComponent) + || mindComponent.OriginalOwnedEntity == null) + { + Log.Error("while getting progress: target dont have a mindComponent or originalEntity is null"); + args.Progress = 1f; + return; + } + + args.Progress = GetProgress(GetEntity(mindComponent.OriginalOwnedEntity.Value)); + if (args.Progress >= 1f) + entity.Comp.ObjectiveIsDone = true; + } + + private void OnPersonAssigned(Entity entity, ref ObjectiveAssignedEvent args) + { + args.Cancelled = !(TryPickRandomPerson(entity.Owner, args.MindId, out var target) + && TryComp(target, out var mindComponent) + && mindComponent.OriginalOwnedEntity != null + && TryTrackIdCardOwner(GetEntity(mindComponent.OriginalOwnedEntity.Value), args.MindId, entity.Comp)); + } + + private bool TryTrackIdCardOwner(EntityUid idCardOwner, EntityUid trackedByMind, FramePersonConditionComponent objective) + { + if (!_idCard.TryFindIdCard(idCardOwner, out var idCard)) + return false; + + var criminalStatusTracker = AddComp(idCard); + criminalStatusTracker.CriminalStatusSpecifier = objective.CriminalStatusSpecifier; + criminalStatusTracker.TrackedByMind = trackedByMind; + return true; + } + + private bool TryPickRandomPerson(EntityUid objective, EntityUid objectiveOwnerMind, [NotNullWhen(true)] out EntityUid? picked, List? blacklist = null) + { + picked = null; + if (!TryComp(objective, out var targetObjective)) + return false; + + if (targetObjective.Target != null) + return false; + + var whitelistedPlayers = _mind.GetAliveHumans(objectiveOwnerMind) + .Where(x => CorrectJob(x) && (blacklist == null || !EntityHasAnyComponent(x, blacklist))) + .ToList(); + + if (whitelistedPlayers.Count == 0) + return false; + + picked = _random.Pick(whitelistedPlayers); + _targetObjective.SetTarget(objective, picked.Value, targetObjective); + return true; + } + + private bool EntityHasAnyComponent(EntityUid uid, List whitelist) + { + foreach (var type in whitelist) + { + if (HasComp(uid, type)) + return true; + } + return false; + } + + /// + /// Checks if that job can be framed. Relays on supervisor cause... no RP in code sry. + /// + private bool CorrectJob(EntityUid mindUid) + { + + if (!_roleSystem.MindHasRole(mindUid, out var role) + || !role.Value.Comp1.JobPrototype.HasValue) + return false; + + if (_prototype.Index(role!.Value.Comp1.JobPrototype!.Value).Supervisors == _legalImmunitySupervisors) + return false; + + return true; + } + + private float GetProgress(EntityUid target) + { + if (!_idCard.TryFindIdCard(target, out var idCard) + || !TryComp(idCard, out var statusTrackerComponent)) + return 1f; // Uh... hmmm... fuck.... <- maybe we need to change target at that point. Anyways player have no fault of that. + + return statusTrackerComponent.GetProgress(); + } +} diff --git a/Content.Server/SS220/Objectives/Systems/IntimidatePersonConditionSystem.cs b/Content.Server/SS220/Objectives/Systems/IntimidatePersonConditionSystem.cs new file mode 100644 index 00000000000000..6da388cb2f8b97 --- /dev/null +++ b/Content.Server/SS220/Objectives/Systems/IntimidatePersonConditionSystem.cs @@ -0,0 +1,144 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using System.Linq; +using Content.Server.Mind; +using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; +using Content.Server.SS220.Objectives.Components; +using Content.Server.SS220.Trackers.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using Content.Shared.SSDIndicator; +using Robust.Shared.Random; + +namespace Content.Server.SS220.Objectives.Systems; + +public sealed class IntimidatePersonConditionSystem : EntitySystem +{ + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly TargetObjectiveSystem _target = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetProgress); + + SubscribeLocalEvent(OnPersonAssigned); + SubscribeLocalEvent(OnAfterAssign); + } + + private void OnGetProgress(Entity entity, ref ObjectiveGetProgressEvent args) + { + if (!_target.GetTarget(entity.Owner, out var target)) + return; + + if (entity.Comp.ObjectiveIsDone) + { + args.Progress = 1f; + return; + } + + //HandleSSDMoment + if (!TryComp(entity.Comp.TargetMob, out var ssdIndicator) + || ssdIndicator.IsSSD) + { + args.Progress = 1f; + SetDescription(entity, IntimidatePersonDescriptionType.SSD); + return; + } + + SetDescription(entity, IntimidatePersonDescriptionType.Start); + args.Progress = GetProgress(entity.Comp.TargetMob); + if (args.Progress >= 1f) + { + entity.Comp.ObjectiveIsDone = true; + SetDescription(entity, IntimidatePersonDescriptionType.Success); + } + } + + private void OnPersonAssigned(Entity entity, ref ObjectiveAssignedEvent args) + { + var (uid, _) = entity; + + if (!TryComp(uid, out var targetObjectiveComponent)) + { + args.Cancelled = true; + return; + } + + if (targetObjectiveComponent.Target != null) + return; + + var targetableMinds = _mind.GetAliveHumans(args.MindId) + .Where(x => TryComp(x, out var mindComponent) + && !HasComp(GetEntity(mindComponent.OriginalOwnedEntity))) + .ToList(); + + if (targetableMinds.Count == 0) + { + args.Cancelled = true; + return; + } + + var targetMindUid = _random.Pick(targetableMinds); + var target = GetMindsOriginalEntity(targetMindUid); + + if (args.Mind.CurrentEntity == null + || target == null) + { + args.Cancelled = true; + return; + } + + _target.SetTarget(uid, targetMindUid, targetObjectiveComponent); + var damageReceivedTracker = AddComp(target.Value); + entity.Comp.TargetMob = target.Value; + damageReceivedTracker.WhomDamageTrack = args.Mind.CurrentEntity.Value; + damageReceivedTracker.DamageTracker = entity.Comp.DamageTrackerSpecifier; + } + + private void OnAfterAssign(Entity entity, ref ObjectiveAfterAssignEvent args) + { + if (entity.Comp.StartDescription != null) + _metaData.SetEntityDescription(entity.Owner, Loc.GetString(entity.Comp.StartDescription)); + } + + private float GetProgress(EntityUid target, DamageReceivedTrackerComponent? tracker = null) + { + if (!Resolve(target, ref tracker)) + return 0f; + + return tracker.GetProgress(); + } + + private EntityUid? GetMindsOriginalEntity(EntityUid mindUid) + { + return GetEntity(Comp(mindUid).OriginalOwnedEntity); + } + + /// + /// A way to change description mindlessly + /// + private void SetDescription(Entity entity, IntimidatePersonDescriptionType type) + { + var (uid, component) = entity; + if (component.DescriptionType == type) + return; + + var newDescription = type switch + { + IntimidatePersonDescriptionType.Start => component.StartDescription, + IntimidatePersonDescriptionType.Success => component.SuccessDescription, + IntimidatePersonDescriptionType.SSD => component.SSDDescription, + _ => null + }; + + if (newDescription == null) + return; + + _metaData.SetEntityDescription(uid, Loc.GetString(newDescription)); + } +} diff --git a/Content.Server/SS220/Trackers/Components/CriminalStatusTrackerComponent.cs b/Content.Server/SS220/Trackers/Components/CriminalStatusTrackerComponent.cs new file mode 100644 index 00000000000000..e3ac74b813321b --- /dev/null +++ b/Content.Server/SS220/Trackers/Components/CriminalStatusTrackerComponent.cs @@ -0,0 +1,103 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.SS220.CriminalRecords; +using Robust.Shared.Prototypes; +using Serilog; + +namespace Content.Server.SS220.Trackers.Components; + +[RegisterComponent] +public sealed partial class CriminalStatusTrackerComponent : Component +{ + /// + /// Mainly used to prevent starting sequence without anyone notice + /// + [ViewVariables(VVAccess.ReadWrite)] + public EntityUid TrackedByMind; + + [ViewVariables(VVAccess.ReadOnly)] + public CriminalStatusTrackerSpecifier CriminalStatusSpecifier = new(); + + [ViewVariables(VVAccess.ReadWrite)] + private int _currentNode = InitCurrentNode; + + private const int InitCurrentNode = -1; + + public void ForceFirstNode() => _currentNode = InitCurrentNode; + public void ForceLastNode() => _currentNode = CriminalStatusSpecifier.CriminalStatusNodes.Count; + public float GetProgress() => (float)(_currentNode + 1) / CriminalStatusSpecifier.CriminalStatusNodes.Count; + + + /// + /// Hide logic for deciding if we can move further with new status. Moving to Last entry of the list is prioritized. + /// + /// True if node changed, else - false + public bool TryMove(ProtoId newStatus, EntityUid? mindUid) + { + if (CriminalStatusSpecifier.CriminalStatusNodes.Count == 0) + { + Log.Warning("Tried to move into empty list of criminal status nodes! This may occur to admins adding component"); + return false; + } + + var newNode = _currentNode + 1; // nope no ++, -- playing + if (TryChangeNode(newNode, newStatus, mindUid, ref _currentNode)) + return true; + + newNode = _currentNode - 1; + if (TryChangeNode(newNode, newStatus, mindUid, ref _currentNode)) + return true; + + return false; + } + + /// + /// Checks if current step is changeable by this mind + /// + /// True if current step allowed by CriminalStatusSpecifier + public bool CanBeChangedByMind(EntityUid? mindUid) + { + return TrackedByMind != mindUid; + } + + /// False if current step can be done by anyone, otherwise true. + public bool NeedToCheckMind(int node) + { + return !CriminalStatusSpecifier.CriminalStatusNodes[node].CanBeSetByTracker; + } + + private bool TryChangeNode(int newNode, ProtoId newStatus, EntityUid? mindUid, ref int currentNode) + { + if (newNode <= InitCurrentNode) + return false; + + if (newNode >= CriminalStatusSpecifier.CriminalStatusNodes.Count) + return false; + + if (CriminalStatusSpecifier.CriminalStatusNodes[newNode].AllowedStatuses.Contains(newStatus) + && (!NeedToCheckMind(newNode) || CanBeChangedByMind(mindUid))) + { + currentNode = newNode; + return true; + } + + return false; + } +} + +[DataDefinition] +public sealed partial class CriminalStatusTrackerSpecifier +{ + [DataField(required: true)] + public List CriminalStatusNodes = new(); +} + +[DataDefinition] +public sealed partial class CriminalStatusTrackerSpecifierNode +{ + [DataField(required: true)] + public List> AllowedStatuses = new(); + + [DataField] + public bool CanBeSetByTracker; +} diff --git a/Content.Server/SS220/Trackers/Components/DamageReceivedTrackerComponent.cs b/Content.Server/SS220/Trackers/Components/DamageReceivedTrackerComponent.cs new file mode 100644 index 00000000000000..901b9d1cb38a8e --- /dev/null +++ b/Content.Server/SS220/Trackers/Components/DamageReceivedTrackerComponent.cs @@ -0,0 +1,61 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Damage.Prototypes; +using Content.Shared.FixedPoint; +using Content.Shared.Mobs; +using Robust.Shared.Prototypes; +using Serilog; + +namespace Content.Server.SS220.Trackers.Components; + +[RegisterComponent] +public sealed partial class DamageReceivedTrackerComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite)] + public EntityUid WhomDamageTrack; + + // We use this to make damaging more situational like burning after igniting by performer will also counted + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan ResetTimeDamageOwnerTracked = TimeSpan.Zero; + + [ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 CurrentAmount = 0; + + [ViewVariables(VVAccess.ReadOnly)] + [DataField(required: true)] + public DamageTrackerSpecifier DamageTracker = new(); + + public float GetProgress() + { + if (CurrentAmount > DamageTracker.TargetAmount) + return 1f; + + if (DamageTracker.TargetAmount == FixedPoint2.Zero) + { + Log.Warning("Damage tracker target amount is zero! This may occur due to admins adding it."); + return 1f; + } + + return (float)CurrentAmount.Value / DamageTracker.TargetAmount.Value; + } + +} + +[DataDefinition] +public sealed partial class DamageTrackerSpecifier +{ + [DataField(required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public ProtoId DamageGroup; + + /// + /// if null will count damage in all owner's mob state. + /// + [DataField] + [ViewVariables(VVAccess.ReadWrite)] + public List? AllowedState; + + [DataField(required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 TargetAmount = FixedPoint2.Zero; +} diff --git a/Content.Server/SS220/Trackers/Systems/CriminalStatusTrackerSystem.cs b/Content.Server/SS220/Trackers/Systems/CriminalStatusTrackerSystem.cs new file mode 100644 index 00000000000000..96a68ac98eb33c --- /dev/null +++ b/Content.Server/SS220/Trackers/Systems/CriminalStatusTrackerSystem.cs @@ -0,0 +1,33 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Server.SS220.CriminalRecords; +using Content.Server.SS220.Trackers.Components; +using Content.Shared.Mind.Components; + + +public sealed class CriminalStatusTrackerSystem : EntitySystem +{ + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStatusChanged); + } + + private void OnStatusChanged(Entity entity, ref CriminalStatusEvent args) + { + var (_, comp) = entity; + + if (args.CurrentCriminalRecord.RecordType == null) + return; + + EntityUid? mindUid = null; + // we check if sender is able to move the progress + if (TryComp(args.Sender, out var mindContainer)) + mindUid = mindContainer.Mind; + + comp.TryMove(args.CurrentCriminalRecord.RecordType.Value, mindUid); + } +} + diff --git a/Content.Server/SS220/Trackers/Systems/DamageReceivedTrackerSystem.cs b/Content.Server/SS220/Trackers/Systems/DamageReceivedTrackerSystem.cs new file mode 100644 index 00000000000000..61bbab78303045 --- /dev/null +++ b/Content.Server/SS220/Trackers/Systems/DamageReceivedTrackerSystem.cs @@ -0,0 +1,47 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Server.SS220.Trackers.Components; +using Content.Shared.Damage; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Server.SS220.Trackers.Systems; + +public sealed class DamageReceivedTrackerSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + + private const float ResetDamageOwnerDelaySeconds = 0.5f; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDamageChanged, before: [typeof(MobThresholdSystem)]); + } + + private void OnDamageChanged(Entity entity, ref DamageChangedEvent args) + { + + if (args.DamageDelta == null || !args.DamageIncreased) + return; + + if (args.Origin != entity.Comp.WhomDamageTrack + && entity.Comp.ResetTimeDamageOwnerTracked < _gameTiming.CurTime) + return; + + if (entity.Comp.DamageTracker.AllowedState != null + && (!TryComp(entity.Owner, out var mobState) + || entity.Comp.DamageTracker.AllowedState!.Contains(mobState.CurrentState))) + return; + + var damageGroup = _prototype.Index(entity.Comp.DamageTracker.DamageGroup); + args.DamageDelta.TryGetDamageInGroup(damageGroup, out var trackableDamage); + entity.Comp.CurrentAmount += trackableDamage; + + entity.Comp.ResetTimeDamageOwnerTracked = _gameTiming.CurTime + TimeSpan.FromSeconds(ResetDamageOwnerDelaySeconds); + } +} diff --git a/Resources/Locale/ru-RU/ss220/objectives/ninja.ftl b/Resources/Locale/ru-RU/ss220/objectives/ninja.ftl new file mode 100644 index 00000000000000..86d6b1afbb00c1 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/objectives/ninja.ftl @@ -0,0 +1,18 @@ +objective-condition-frame-target-title = Подставьте { $targetName }, в должности { CAPITALIZE($job) }. +ent-NinjaFrameTargetObjective = Подставить цель + .desc = Этот сотрудник избегает правосудия, исправьте это. Подставьте цель так, что дойдёт до заключения в бриге станции. Следите чтобы пометки оставались и в криминальных записях. + +objective-condition-intimidate-target-brute-title = Заказ на устрашение { $targetName }, в должности { CAPITALIZE($job) }. +objective-condition-intimidate-target-brute-desc = Вам заказали преподать урок члену экипажа станции. Избейте вашу цель несколько раз, пока она в ясном сознании. +objective-condition-intimidate-target-brute-desc-ssd= Цель впала в ССД, такой исход тоже возможен. +objective-condition-intimidate-target-brute-desc-success = Вы успешно преподали урок. + +objective-condition-intimidate-target-burn-title = Заказ на устрашение { $targetName }, в должности { CAPITALIZE($job) }. +objective-condition-intimidate-target-burn-desc = Вам заказали преподать урок члену экипажа станции. Нанесите вашей цели достаточно ожогов, пока она в ясном сознании. +objective-condition-intimidate-target-burn-desc-ssd= Цель впала в ССД, такой исход тоже возможен. +objective-condition-intimidate-target-burn-desc-success = Вы успешно преподали урок. + +objective-condition-intimidate-target-toxin-title = Заказ на устрашение { $targetName }, в должности { CAPITALIZE($job) }. +objective-condition-intimidate-target-toxin-desc = Вам заказали преподать урок члену экипажа станции. Отравите вашу цель несколько раз, пока она в ясном сознании. +objective-condition-intimidate-target-toxin-desc-ssd= Цель впала в ССД, такой исход тоже возможен. +objective-condition-intimidate-target-toxin-desc-success = Вы успешно преподали урок. diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 8e6f516fd02b53..d7a87e8f3a6193 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -204,12 +204,19 @@ - type: AntagLoadProfileRule - type: AntagObjectives objectives: + - NinjaFrameTargetObjective # SS220 Ninja-target-update | Move it upper - StealResearchObjective - DoorjackObjective - - SpiderChargeObjective + # - SpiderChargeObjective # SS220 Ninja-target-update - TerrorObjective - - MassArrestObjective + # - MassArrestObjective # SS220 Ninja-target-update - NinjaSurviveObjective + # SS220 Ninja-target-update begin + - type: AntagRandomObjectives + sets: + - groups: NinjaObjectiveGroups + maxDifficulty: 1.5 # be careful 1.5 is here because one ninjaTarget cost 1.5 and we need only one + # SS220 Ninja-target-update end - type: AntagSelection agentName: ninja-round-end-agent-name definitions: diff --git a/Resources/Prototypes/Roles/Antags/ninja.yml b/Resources/Prototypes/Roles/Antags/ninja.yml index 6a9a65bb13ec87..ab26d5a7437d52 100644 --- a/Resources/Prototypes/Roles/Antags/ninja.yml +++ b/Resources/Prototypes/Roles/Antags/ninja.yml @@ -20,7 +20,7 @@ shoes: ClothingShoesSpaceNinja id: AgentIDCard ears: ClothingHeadsetGrey - pocket1: SpiderCharge + # pocket1: SpiderCharge # SS220 Ninja-target-update pocket2: PinpointerStation belt: EnergyKatana inhand: diff --git a/Resources/Prototypes/SS220/Objectives/ninja.yml b/Resources/Prototypes/SS220/Objectives/ninja.yml new file mode 100644 index 00000000000000..d7f67c29875d8d --- /dev/null +++ b/Resources/Prototypes/SS220/Objectives/ninja.yml @@ -0,0 +1,82 @@ +- type: entity + parent: BaseNinjaObjective + id: NinjaFrameTargetObjective + description: This person fall out of NanoTrasen attention, correct it. Security must imprison them. + components: + - type: Objective + icon: + sprite: SS220/Objectives/frame.rsi + state: snake_white + - type: TargetObjective + title: objective-condition-frame-target-title + - type: FramePersonCondition + criminalStatusSpecifier: + criminalStatusNodes: + - !type:CriminalStatusTrackerSpecifierNode + allowedStatuses: [wanted, search, execute, suspected] + canBeSetByTracker: true + - !type:CriminalStatusTrackerSpecifierNode + allowedStatuses: [incarcerated] + canBeSetByTracker: false # it will be very sad if ninja can just hack its target + - !type:CriminalStatusTrackerSpecifierNode + allowedStatuses: [discharged] + canBeSetByTracker: true + +- type: entity + parent: BaseNinjaObjective + id: NinjaIntimidateBruteTargetObjective + components: + - type: Objective + icon: + sprite: SS220/Objectives/intimidate.rsi + state: brute + - type: TargetObjective + title: objective-condition-intimidate-target-brute-title + - type: IntimidatePersonCondition + startDescription: objective-condition-intimidate-target-brute-desc + successDescription: objective-condition-intimidate-target-brute-desc-success + sSDDescription: objective-condition-intimidate-target-brute-desc-ssd + damageTrackerSpecifier: + damageGroup: Brute + allowedState: [Alive] + targetAmount: 280 + +- type: entity + parent: BaseNinjaObjective + id: NinjaIntimidateBurnTargetObjective + components: + - type: Objective + icon: + sprite: SS220/Objectives/intimidate.rsi + state: burn + - type: TargetObjective + title: objective-condition-intimidate-target-burn-title + - type: IntimidatePersonCondition + startDescription: objective-condition-intimidate-target-burn-desc + successDescription: objective-condition-intimidate-target-burn-desc-success + sSDDescription: objective-condition-intimidate-target-burn-desc-ssd + damageTrackerSpecifier: + damageGroup: Burn + allowedState: [Alive] + targetAmount: 160 + +- type: entity + parent: BaseNinjaObjective + id: NinjaIntimidateToxinTargetObjective + components: + - type: Objective + icon: + sprite: SS220/Objectives/intimidate.rsi + state: toxin + - type: TargetObjective + title: objective-condition-intimidate-target-toxin-title + - type: IntimidatePersonCondition + startDescription: objective-condition-intimidate-target-toxin-desc + successDescription: objective-condition-intimidate-target-toxin-desc-success + sSDDescription: objective-condition-intimidate-target-toxin-desc-ssd + damageTrackerSpecifier: + damageGroup: Toxin + allowedState: [Alive] + targetAmount: 120 + + diff --git a/Resources/Prototypes/SS220/Objectives/ninjaGroup.yml b/Resources/Prototypes/SS220/Objectives/ninjaGroup.yml new file mode 100644 index 00000000000000..7ce3ebf8baa967 --- /dev/null +++ b/Resources/Prototypes/SS220/Objectives/ninjaGroup.yml @@ -0,0 +1,11 @@ +- type: weightedRandom + id: NinjaObjectiveGroups + weights: + NinjaIntimidateTargetGroup: 1 + +- type: weightedRandom + id: NinjaIntimidateTargetGroup + weights: + NinjaIntimidateBruteTargetObjective: 5 # easiest + NinjaIntimidateBurnTargetObjective: 3 + NinjaIntimidateToxinTargetObjective: 1 # hardest diff --git a/Resources/Textures/SS220/Objectives/frame.rsi/meta.json b/Resources/Textures/SS220/Objectives/frame.rsi/meta.json new file mode 100644 index 00000000000000..989459a5ead9bd --- /dev/null +++ b/Resources/Textures/SS220/Objectives/frame.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "license": "EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt", + "copyright": "By Estkemran (Github) for SS220 for SS220", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "snake_cian" + }, + { + "name": "snake_green" + }, + { + "name": "snake_white" + } + ] +} diff --git a/Resources/Textures/SS220/Objectives/frame.rsi/snake_cian.png b/Resources/Textures/SS220/Objectives/frame.rsi/snake_cian.png new file mode 100644 index 00000000000000..7a0e8df7777734 Binary files /dev/null and b/Resources/Textures/SS220/Objectives/frame.rsi/snake_cian.png differ diff --git a/Resources/Textures/SS220/Objectives/frame.rsi/snake_green.png b/Resources/Textures/SS220/Objectives/frame.rsi/snake_green.png new file mode 100644 index 00000000000000..5af12744261d82 Binary files /dev/null and b/Resources/Textures/SS220/Objectives/frame.rsi/snake_green.png differ diff --git a/Resources/Textures/SS220/Objectives/frame.rsi/snake_white.png b/Resources/Textures/SS220/Objectives/frame.rsi/snake_white.png new file mode 100644 index 00000000000000..905ff0405250d8 Binary files /dev/null and b/Resources/Textures/SS220/Objectives/frame.rsi/snake_white.png differ diff --git a/Resources/Textures/SS220/Objectives/intimidate.rsi/brute.png b/Resources/Textures/SS220/Objectives/intimidate.rsi/brute.png new file mode 100644 index 00000000000000..64800643b078fe Binary files /dev/null and b/Resources/Textures/SS220/Objectives/intimidate.rsi/brute.png differ diff --git a/Resources/Textures/SS220/Objectives/intimidate.rsi/burn.png b/Resources/Textures/SS220/Objectives/intimidate.rsi/burn.png new file mode 100644 index 00000000000000..e0b8744ea4632f Binary files /dev/null and b/Resources/Textures/SS220/Objectives/intimidate.rsi/burn.png differ diff --git a/Resources/Textures/SS220/Objectives/intimidate.rsi/meta.json b/Resources/Textures/SS220/Objectives/intimidate.rsi/meta.json new file mode 100644 index 00000000000000..91c836062dc554 --- /dev/null +++ b/Resources/Textures/SS220/Objectives/intimidate.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "license": "EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt", + "copyright": "By Estkemran (Github) for SS220 for SS220", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "brute" + }, + { + "name": "burn" + }, + { + "name": "toxin" + } + ] +} diff --git a/Resources/Textures/SS220/Objectives/intimidate.rsi/toxin.png b/Resources/Textures/SS220/Objectives/intimidate.rsi/toxin.png new file mode 100644 index 00000000000000..5524c78d239d70 Binary files /dev/null and b/Resources/Textures/SS220/Objectives/intimidate.rsi/toxin.png differ