From 9c5b9b3ad20755b8350007e1ef5d071d8f68b94f Mon Sep 17 00:00:00 2001 From: fox Date: Mon, 16 Dec 2024 22:34:05 +0300 Subject: [PATCH 1/9] Basic framework done --- .../GameTicking/StationEventCondition.cs | 140 ++++++++++++++++++ .../StationEventConditions.Players.cs | 49 ++++++ .../StationEventConditions.Utility.cs | 37 +++++ .../Components/StationEventComponent.cs | 11 ++ .../StationEvents/EventManagerSystem.cs | 15 ++ 5 files changed, 252 insertions(+) create mode 100644 Content.Server/FloofStation/GameTicking/StationEventCondition.cs create mode 100644 Content.Server/FloofStation/GameTicking/StationEventConditions.Players.cs create mode 100644 Content.Server/FloofStation/GameTicking/StationEventConditions.Utility.cs diff --git a/Content.Server/FloofStation/GameTicking/StationEventCondition.cs b/Content.Server/FloofStation/GameTicking/StationEventCondition.cs new file mode 100644 index 00000000000..549ada404b9 --- /dev/null +++ b/Content.Server/FloofStation/GameTicking/StationEventCondition.cs @@ -0,0 +1,140 @@ +using Content.Server.GameTicking; +using Content.Server.Mind; +using Content.Server.StationEvents; +using Content.Server.StationEvents.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Roles; +using Content.Shared.Roles.Jobs; +using Robust.Server.Player; +using Robust.Shared.Enums; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Serilog; + + +namespace Content.Server.FloofStation.GameTicking; + +/// +/// Represents an abstract condition required for a station event to be chosen from the random event pool. +/// +/// +/// Implementations should avoid performing expensive checks. +/// Any data that may be expensive to compute should instead be precomputed and stored in +/// +[Serializable, ImplicitDataDefinitionForInheritors] +public abstract partial class StationEventCondition +{ + /// + /// If true, the event will only be run if this condition is NOT met. + /// + [DataField] + public bool Inverted = false; + + public abstract bool IsMet(EntityPrototype proto, StationEventComponent component, Dependencies dependencies); + + /// + /// Entity system and other dependencies used by station event conditions. + /// GameTicker allocates an instance of this before passing it to all events. + /// + public sealed class Dependencies(IEntityManager entMan, GameTicker ticker, EventManagerSystem eventManager) + { + public ISawmill Log = Logger.GetSawmill("station-event-conditions"); + + public IEntityManager EntMan => entMan; + public GameTicker Ticker => ticker; + public EventManagerSystem EventManager => eventManager; + + public MindSystem Minds = default!; + public SharedIdCardSystem IdCard = default!; + + [Dependency] public IPrototypeManager ProtoMan = default!; + [Dependency] public IRobustRandom Random = default!; + [Dependency] public IPlayerManager PlayerManager = default!; + + /// + /// List of all players along with their jobs. The dept field may have the default value if it could not be determined. + /// + public List<(ICommonSession session, EntityUid uid, ProtoId job, ProtoId dept)> Players = new(); + public Dictionary, int> JobCounts = new(); + public Dictionary, int> DeptCounts = new(); + + // Lookups + private readonly Dictionary> _jobTitleToPrototype = new(); + private readonly Dictionary, ProtoId> _jobToDept = new(); + + /// + /// Called once after the instantiation of the class. + /// + public void Initialize() + { + IoCManager.InjectDependencies(this); + + // We cannot use entity system dependencies outside of ESC context. + IdCard = EntMan.System(); + Minds = EntMan.System(); + + // Build the lookups - SharedJobSystem contains methods that iterate over all of those lists each time, + // Resulting in an O(n^2 * m) performance cost for each update() call. + foreach (var job in ProtoMan.EnumeratePrototypes()) + { + _jobTitleToPrototype[job.LocalizedName] = job.ID; + + foreach (var dept in ProtoMan.EnumeratePrototypes()) + { + if (!dept.Primary || !dept.Roles.Contains(job.ID)) + continue; + + _jobToDept[job.ID] = dept.ID; + break; + } + } + } + + /// + /// Called once shortly before passing this object to IsMet() to collect the necessary data about the round. + /// + public void Update() + { + JobCounts.Clear(); + DeptCounts.Clear(); + + // Collect data about the jobs of the players in the round + Players.Clear(); + foreach (var session in PlayerManager.Sessions) + { + if (session.AttachedEntity is not {} player + || session.Status is SessionStatus.Zombie or SessionStatus.Disconnected + || !Minds.TryGetMind(session, out var mind, out var mindComponent)) + continue; + + ProtoId job = default; + // 1: Try to get the job from the ID the person holds + if (IdCard.TryFindIdCard(player, out var idCard) && idCard.Comp.JobTitle is {} jobTitle) + _jobTitleToPrototype.TryGetValue(jobTitle, out job); + + // 2: If failed, try to fetch it from the mind component instead + if (job == default + && EntMan.TryGetComponent(mind, out var jobComp) + && jobComp.Prototype is {} mindJobProto + ) + job = mindJobProto; + + // If both have failed, skip the player + if (job == default) + continue; + + var dept = _jobToDept.GetValueOrDefault(job); + + Players.Add((session, player, job, dept)); + JobCounts[job] = JobCounts.GetValueOrDefault(job, 0) + 1; + DeptCounts[dept] = DeptCounts.GetValueOrDefault(dept, 0) + 1; + } + + #if DEBUG + Log.Debug($"Event conditions data: Job counts: {string.Join(", ", JobCounts)}"); + Log.Debug($"Dept counts: {string.Join(", ", DeptCounts)}"); + #endif + } + } +} diff --git a/Content.Server/FloofStation/GameTicking/StationEventConditions.Players.cs b/Content.Server/FloofStation/GameTicking/StationEventConditions.Players.cs new file mode 100644 index 00000000000..ef9fdb9504b --- /dev/null +++ b/Content.Server/FloofStation/GameTicking/StationEventConditions.Players.cs @@ -0,0 +1,49 @@ +using Content.Server.StationEvents.Components; +using Content.Shared.InteractionVerbs; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; + +namespace Content.Server.FloofStation.GameTicking; + +/// +/// A condition that requires a number of players to be present in a specific department. +/// +/// +/// - !type:DepartmentCountCondition +/// department: Security +/// range: {min: 5} +/// +[Serializable] +public sealed partial class DepartmentCountCondition : StationEventCondition +{ + [DataField(required: true)] + public ProtoId Department; + + [DataField(required: true)] + public InteractionVerbPrototype.RangeSpecifier Range; + + public override bool IsMet(EntityPrototype proto, StationEventComponent component, Dependencies dependencies) + { + var count = dependencies.DeptCounts.GetValueOrDefault(Department, 0); + return Range.IsInRange(count); + } +} + +/// +/// Same as , but for specific jobs. +/// +[Serializable] +public sealed partial class JobCountCondition : StationEventCondition +{ + [DataField(required: true)] + public ProtoId Job; + + [DataField(required: true)] + public InteractionVerbPrototype.RangeSpecifier Range; + + public override bool IsMet(EntityPrototype proto, StationEventComponent component, Dependencies dependencies) + { + var count = dependencies.JobCounts.GetValueOrDefault(Job, 0); + return Range.IsInRange(count); + } +} diff --git a/Content.Server/FloofStation/GameTicking/StationEventConditions.Utility.cs b/Content.Server/FloofStation/GameTicking/StationEventConditions.Utility.cs new file mode 100644 index 00000000000..ab2b7bc7570 --- /dev/null +++ b/Content.Server/FloofStation/GameTicking/StationEventConditions.Utility.cs @@ -0,0 +1,37 @@ +using System.Linq; +using Content.Server.StationEvents.Components; +using Robust.Shared.Prototypes; + +namespace Content.Server.FloofStation.GameTicking; + +/// +/// Combines a number of other conditions in a boolean AND or a boolean OR. +/// +/// +/// +/// - !type:ComplexCondition +/// requireAll: true +/// conditions: +/// - !type:SomeCondition1 +/// ... +/// - !type:SomeCondition2 +/// ... +/// +/// +[Serializable] +public sealed partial class ComplexCondition : StationEventCondition +{ + /// + /// If true, this condition acts as a boolean AND. If false, it acts as a boolean OR. + /// + [DataField] + public bool RequireAll = false; + + [DataField(required: true)] + public List Conditions = new(); + + public override bool IsMet(EntityPrototype proto, StationEventComponent component, Dependencies dependencies) => + RequireAll + ? Conditions.All(it => it.Inverted ^ it.IsMet(proto, component, dependencies)) + : Conditions.Any(it => it.Inverted ^ it.IsMet(proto, component, dependencies)); +} diff --git a/Content.Server/StationEvents/Components/StationEventComponent.cs b/Content.Server/StationEvents/Components/StationEventComponent.cs index 54af1a59d99..6edf3d8ac5a 100644 --- a/Content.Server/StationEvents/Components/StationEventComponent.cs +++ b/Content.Server/StationEvents/Components/StationEventComponent.cs @@ -1,3 +1,4 @@ +using Content.Server.FloofStation.GameTicking; using Robust.Shared.Audio; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; @@ -82,4 +83,14 @@ public sealed partial class StationEventComponent : Component [DataField("endTime", customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] public TimeSpan? EndTime; + + // Floof section - custom conditions + + /// + /// A list of conditions that must be met for the event to run. + /// + [DataField] + public List? Conditions; + + // Floof section end } diff --git a/Content.Server/StationEvents/EventManagerSystem.cs b/Content.Server/StationEvents/EventManagerSystem.cs index 9c0005d06db..b219ce20c77 100644 --- a/Content.Server/StationEvents/EventManagerSystem.cs +++ b/Content.Server/StationEvents/EventManagerSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Server.Chat.Managers; +using Content.Server.FloofStation.GameTicking; using Content.Server.GameTicking; using Content.Server.StationEvents.Components; using Content.Shared.CCVar; @@ -24,11 +25,16 @@ public sealed class EventManagerSystem : EntitySystem public bool EventsEnabled { get; private set; } private void SetEnabled(bool value) => EventsEnabled = value; + private StationEventCondition.Dependencies _eventConditionDeps = default!; // Floof + public override void Initialize() { base.Initialize(); Subs.CVar(_configurationManager, CCVars.EventsEnabled, SetEnabled, true); + + _eventConditionDeps = new(EntityManager, GameTicker, this); + _eventConditionDeps.Initialize(); } /// @@ -113,6 +119,8 @@ private Dictionary AvailableEvents(bool var result = new Dictionary(); + _eventConditionDeps.Update(); // Floof + foreach (var (proto, stationEvent) in AllEvents()) { if (CanRun(proto, stationEvent, playerCount, currentTime)) @@ -201,6 +209,13 @@ private bool CanRun(EntityPrototype prototype, StationEventComponent stationEven } // Nyano - End modified code block. + // Floof section - custom conditions + if (stationEvent.Conditions is { } conditions + && conditions.Any(it => it.Inverted ^ !it.IsMet(prototype, stationEvent, _eventConditionDeps)) + ) + return false; + // Floof section end + return true; } } From 00114655063a95a1714011915287da642ee0650a Mon Sep 17 00:00:00 2001 From: fox Date: Mon, 16 Dec 2024 23:33:15 +0300 Subject: [PATCH 2/9] Fixes --- .../GameTicking/StationEventCondition.cs | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Content.Server/FloofStation/GameTicking/StationEventCondition.cs b/Content.Server/FloofStation/GameTicking/StationEventCondition.cs index 549ada404b9..42418c42cc2 100644 --- a/Content.Server/FloofStation/GameTicking/StationEventCondition.cs +++ b/Content.Server/FloofStation/GameTicking/StationEventCondition.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Server.GameTicking; using Content.Server.Mind; using Content.Server.StationEvents; @@ -10,7 +11,6 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Serilog; namespace Content.Server.FloofStation.GameTicking; @@ -53,15 +53,15 @@ public sealed class Dependencies(IEntityManager entMan, GameTicker ticker, Event [Dependency] public IPlayerManager PlayerManager = default!; /// - /// List of all players along with their jobs. The dept field may have the default value if it could not be determined. + /// The list of all players along with their jobs. /// - public List<(ICommonSession session, EntityUid uid, ProtoId job, ProtoId dept)> Players = new(); + public List<(ICommonSession session, EntityUid uid, ProtoId job)> Players = new(); public Dictionary, int> JobCounts = new(); public Dictionary, int> DeptCounts = new(); // Lookups private readonly Dictionary> _jobTitleToPrototype = new(); - private readonly Dictionary, ProtoId> _jobToDept = new(); + private readonly Dictionary, List>> _jobToDepts = new(); /// /// Called once after the instantiation of the class. @@ -74,20 +74,18 @@ public void Initialize() IdCard = EntMan.System(); Minds = EntMan.System(); - // Build the lookups - SharedJobSystem contains methods that iterate over all of those lists each time, + // Build the inverse lookups - SharedJobSystem contains methods that iterate over all of those lists each time, // Resulting in an O(n^2 * m) performance cost for each update() call. foreach (var job in ProtoMan.EnumeratePrototypes()) { _jobTitleToPrototype[job.LocalizedName] = job.ID; - foreach (var dept in ProtoMan.EnumeratePrototypes()) - { - if (!dept.Primary || !dept.Roles.Contains(job.ID)) - continue; + var depts = ProtoMan.EnumeratePrototypes() + .Where(it => it.Roles.Contains(job.ID)) + .Select(it => new ProtoId(it.ID)) + .ToList(); - _jobToDept[job.ID] = dept.ID; - break; - } + _jobToDepts[job.ID] = depts; } } @@ -124,11 +122,15 @@ public void Update() if (job == default) continue; - var dept = _jobToDept.GetValueOrDefault(job); - - Players.Add((session, player, job, dept)); + // Update the info + Players.Add((session, player, job)); JobCounts[job] = JobCounts.GetValueOrDefault(job, 0) + 1; - DeptCounts[dept] = DeptCounts.GetValueOrDefault(dept, 0) + 1; + // Increment the number of players in each dept this job belongs to + if (_jobToDepts.TryGetValue(job, out var depts)) + { + foreach (var dept in depts) + DeptCounts[dept] = DeptCounts.GetValueOrDefault(dept, 0) + 1; + } } #if DEBUG From b17765a909d068aa09d65776ad7f23607a2a15cb Mon Sep 17 00:00:00 2001 From: fox Date: Mon, 16 Dec 2024 23:33:25 +0300 Subject: [PATCH 3/9] Add conditions to some events --- Resources/Prototypes/Floof/Gamerules/base.yml | 11 +++++++++++ Resources/Prototypes/GameRules/events.yml | 14 +++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 Resources/Prototypes/Floof/Gamerules/base.yml diff --git a/Resources/Prototypes/Floof/Gamerules/base.yml b/Resources/Prototypes/Floof/Gamerules/base.yml new file mode 100644 index 00000000000..8bdbfa44ac7 --- /dev/null +++ b/Resources/Prototypes/Floof/Gamerules/base.yml @@ -0,0 +1,11 @@ +# A game rule base that requires at least 2 security members to be present +- type: entity + id: BaseGameRuleSecurityRequirement + parent: BaseGameRule + abstract: true + components: + - type: StationEvent + conditions: + - !type:DepartmentCountCondition + department: Security + range: {min: 2} diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 35c2fb2805d..7a742bf2de1 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -7,6 +7,10 @@ weight: 8 startDelay: 30 duration: 35 + conditions: # Floof - at least one epi member + - !type:DepartmentCountCondition + department: Epistemics + range: {min: 1} - type: AnomalySpawnRule - type: entity @@ -80,7 +84,7 @@ prototype: MobSkeletonCloset - type: entity - parent: BaseGameRule + parent: BaseGameRuleSecurityRequirement # Floof - changed parent id: DragonSpawn noSpawn: true components: @@ -94,7 +98,7 @@ prototype: SpawnPointGhostDragon - type: entity - parent: BaseGameRule + parent: BaseGameRuleSecurityRequirement # Floof - changed parent id: NinjaSpawn noSpawn: true components: @@ -168,6 +172,10 @@ endAnnouncement: true duration: null #ending is handled by MeteorSwarmRule startDelay: 30 + conditions: # Floof + - !type:DepartmentCountCondition + department: Engineering + range: {min: 2} - type: MeteorSwarmRule - type: entity @@ -371,7 +379,7 @@ - type: entity id: LoneOpsSpawn - parent: BaseGameRule + parent: BaseGameRuleSecurityRequirement # Floof - changed parent noSpawn: true components: - type: StationEvent From 9afb8425ba37bbdda853d0368fc6b09d35fc7567 Mon Sep 17 00:00:00 2001 From: fox Date: Mon, 16 Dec 2024 23:41:12 +0300 Subject: [PATCH 4/9] Minor formatting --- .../FloofStation/GameTicking/StationEventCondition.cs | 1 - Content.Server/StationEvents/EventManagerSystem.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Content.Server/FloofStation/GameTicking/StationEventCondition.cs b/Content.Server/FloofStation/GameTicking/StationEventCondition.cs index 42418c42cc2..060357ef662 100644 --- a/Content.Server/FloofStation/GameTicking/StationEventCondition.cs +++ b/Content.Server/FloofStation/GameTicking/StationEventCondition.cs @@ -12,7 +12,6 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; - namespace Content.Server.FloofStation.GameTicking; /// diff --git a/Content.Server/StationEvents/EventManagerSystem.cs b/Content.Server/StationEvents/EventManagerSystem.cs index b219ce20c77..ce62e35213a 100644 --- a/Content.Server/StationEvents/EventManagerSystem.cs +++ b/Content.Server/StationEvents/EventManagerSystem.cs @@ -33,7 +33,7 @@ public override void Initialize() Subs.CVar(_configurationManager, CCVars.EventsEnabled, SetEnabled, true); - _eventConditionDeps = new(EntityManager, GameTicker, this); + _eventConditionDeps = new(EntityManager, GameTicker, this); // Floof _eventConditionDeps.Initialize(); } From ebdcc0e81fa24711e6269330e733f07852f2f372 Mon Sep 17 00:00:00 2001 From: fox Date: Thu, 19 Dec 2024 23:19:27 +0300 Subject: [PATCH 5/9] All the fixes --- .../TileReactions/CleanTileReaction.cs | 35 ++++++++++++++++--- Content.Server/FootPrint/FootPrintsSystem.cs | 16 ++++++--- .../Footprint/FootPrintsComponent.cs | 1 + 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs index 3f5ae63c365..b7bfce9346d 100644 --- a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; @@ -7,6 +8,9 @@ using Robust.Shared.Map; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using System.Linq; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.FootPrint; + namespace Content.Server.Chemistry.TileReactions; @@ -42,11 +46,9 @@ FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, Fixe foreach (var entity in entities) { - if (!puddleQuery.TryGetComponent(entity, out var puddle) || - !solutionContainerSystem.TryGetSolution(entity, puddle.SolutionName, out var puddleSolution, out _)) - { + // Floof - separated this into a separate function to incroporate cleaning footprints + if (!TryGetCleanableSolution(entity, entMan, solutionContainerSystem, out var puddleSolution)) continue; - } var purgeable = solutionContainerSystem.SplitSolutionWithout(puddleSolution.Value, purgeAmount, ReplacementReagent, reagent.ID); @@ -60,4 +62,29 @@ FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, Fixe return (reactVolume / CleanAmountMultiplier - purgeAmount) * CleanAmountMultiplier; } + + // Floof + private bool TryGetCleanableSolution( + EntityUid entity, + IEntityManager entMan, + SharedSolutionContainerSystem solutionContainerSystem, + [NotNullWhen(true)] out Entity? solution) + { + solution = default; + if (entMan.TryGetComponent(entity, out var puddle) && + solutionContainerSystem.TryGetSolution(entity, puddle.SolutionName, out var puddleSolution, out _)) + { + solution = puddleSolution; + return true; + } + + if (entMan.TryGetComponent(entity, out var footPrint) && + solutionContainerSystem.TryGetSolution(entity, footPrint.SolutionName, out var footPrintSolution, out _)) + { + solution = footPrintSolution; + return true; + } + + return false; + } } diff --git a/Content.Server/FootPrint/FootPrintsSystem.cs b/Content.Server/FootPrint/FootPrintsSystem.cs index 8c5b7e2a3cc..a4828b43782 100644 --- a/Content.Server/FootPrint/FootPrintsSystem.cs +++ b/Content.Server/FootPrint/FootPrintsSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Server.Atmos.Components; +using Content.Server.Gravity; using Content.Shared.Inventory; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; @@ -9,6 +10,7 @@ using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Forensics; using Robust.Shared.Map; +using Robust.Shared.Physics.Components; using Robust.Shared.Random; namespace Content.Server.FootPrint; @@ -22,7 +24,7 @@ public sealed class FootPrintsSystem : EntitySystem [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; // Floof private EntityQuery _transformQuery; private EntityQuery _mobThresholdQuery; @@ -49,10 +51,12 @@ private void OnStartupComponent(EntityUid uid, FootPrintsComponent component, Co private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent args) { - if (component.PrintsColor.A <= .2f) // avoid creating footsteps that are invisible + // Floof: clear stored DNAs if footprints are now invisible + if (component.PrintsColor.A <= .2f) component.DNAs.Clear(); - if (component.PrintsColor.A <= .2f + if (component.PrintsColor.A <= .2f // avoid creating footsteps that are invisible + || TryComp(uid, out var physics) && physics.BodyStatus != BodyStatus.OnGround // Floof: do not create footprints if the entity is flying || !_transformQuery.TryComp(uid, out var transform) || !_mobThresholdQuery.TryComp(uid, out var mobThreshHolds) || !_map.TryFindGridAt(_transform.GetMapCoordinates((uid, transform)), out var gridUid, out _)) @@ -66,9 +70,10 @@ private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent if (!(distance > stepSize)) return; + // Floof section var entities = _lookup.GetEntitiesIntersecting(uid, LookupFlags.All); foreach (var entityUid in entities.Where(entityUid => HasComp(entityUid))) - return; //are we on a puddle? we exit, ideally we would exchange liquid and DNA with the puddle but meh, too lazy to do that now. + return; // are we on a puddle? we exit, ideally we would exchange liquid and DNA with the puddle but meh, too lazy to do that now. component.RightStep = !component.RightStep; @@ -76,7 +81,8 @@ private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent var footPrintComponent = EnsureComp(entity); var forensics = EntityManager.EnsureComponent(entity); - forensics.DNAs.UnionWith(component.DNAs); + if (TryComp(uid, out var ownerForensics)) // Floof edit: transfer owner DNA into the footsteps + forensics.DNAs.UnionWith(ownerForensics.DNAs); footPrintComponent.PrintOwner = uid; Dirty(entity, footPrintComponent); diff --git a/Content.Shared/Footprint/FootPrintsComponent.cs b/Content.Shared/Footprint/FootPrintsComponent.cs index 5de5d79abd5..ccdacff68cb 100644 --- a/Content.Shared/Footprint/FootPrintsComponent.cs +++ b/Content.Shared/Footprint/FootPrintsComponent.cs @@ -86,6 +86,7 @@ public sealed partial class FootPrintsComponent : Component /// public float ColorInterpolationFactor = 0.2f; + // Floof [DataField] public HashSet DNAs = new(); } From 94c759ab81de477c0cdcf7f5c9ee3f4203007bcb Mon Sep 17 00:00:00 2001 From: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> Date: Thu, 19 Dec 2024 23:25:47 +0300 Subject: [PATCH 6/9] Increase the footprint visibility threshold by 50% --- Content.Server/FootPrint/FootPrintsSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/FootPrint/FootPrintsSystem.cs b/Content.Server/FootPrint/FootPrintsSystem.cs index a4828b43782..e69e9bcf226 100644 --- a/Content.Server/FootPrint/FootPrintsSystem.cs +++ b/Content.Server/FootPrint/FootPrintsSystem.cs @@ -52,10 +52,10 @@ private void OnStartupComponent(EntityUid uid, FootPrintsComponent component, Co private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent args) { // Floof: clear stored DNAs if footprints are now invisible - if (component.PrintsColor.A <= .2f) + if (component.PrintsColor.A <= .3f) component.DNAs.Clear(); - if (component.PrintsColor.A <= .2f // avoid creating footsteps that are invisible + if (component.PrintsColor.A <= .3f // avoid creating footsteps that are invisible || TryComp(uid, out var physics) && physics.BodyStatus != BodyStatus.OnGround // Floof: do not create footprints if the entity is flying || !_transformQuery.TryComp(uid, out var transform) || !_mobThresholdQuery.TryComp(uid, out var mobThreshHolds) From 8b5c2ebbbe22ed8f047b1514ed7746d1738e73d1 Mon Sep 17 00:00:00 2001 From: fox Date: Fri, 20 Dec 2024 21:56:23 +0300 Subject: [PATCH 7/9] Added the unrevivable trait --- Content.Server/Medical/DefibrillatorSystem.cs | 6 ++++++ .../Traits/Assorted/UnrevivableComponent.cs | 10 ++++++++++ .../medical/components/defibrillator.ftl | 1 + Resources/Locale/en-US/traits/traits.ftl | 5 ++++- Resources/Prototypes/Traits/disabilities.yml | 20 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Traits/Assorted/UnrevivableComponent.cs diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 7b0b4110201..f49e7a37e28 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Ghost; using Content.Server.Popups; using Content.Server.PowerCell; +using Content.Server.Traits.Assorted; using Content.Shared.Chat; using Content.Shared.Damage; using Content.Shared.DoAfter; @@ -213,6 +214,11 @@ public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorCo _chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-rotten"), InGameICChatType.Speak, true); } + else if (HasComp(target)) + { + _chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-unrevivable"), + InGameICChatType.Speak, true); + } else { if (_mobState.IsDead(target, mob)) diff --git a/Content.Server/Traits/Assorted/UnrevivableComponent.cs b/Content.Server/Traits/Assorted/UnrevivableComponent.cs new file mode 100644 index 00000000000..b95c922d548 --- /dev/null +++ b/Content.Server/Traits/Assorted/UnrevivableComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Traits.Assorted; + +/// +/// This is used for the urevivable trait. +/// +[RegisterComponent] +public sealed partial class UnrevivableComponent : Component +{ + +} diff --git a/Resources/Locale/en-US/medical/components/defibrillator.ftl b/Resources/Locale/en-US/medical/components/defibrillator.ftl index 1c32dd801d9..dc4a03aa3b1 100644 --- a/Resources/Locale/en-US/medical/components/defibrillator.ftl +++ b/Resources/Locale/en-US/medical/components/defibrillator.ftl @@ -1,3 +1,4 @@ defibrillator-not-on = The defibrillator isn't turned on. defibrillator-no-mind = No intelligence pattern can be detected in patient's brain. Further attempts futile. defibrillator-rotten = Body decomposition detected: resuscitation failed. +defibrillator-unrevivable = This patient is unable to be revived due to a unique body composition. diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index c9db3d5056d..1f143d34cd5 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -43,6 +43,9 @@ trait-description-Hemophilia = trait-name-Paracusia = Paracusia trait-description-Paracusia = You hear sounds that aren't really there +trait-name-Unrevivable = Unrevivable +trait-description-Unrevivable = You have a unique body composition that prevents you from being revived by normal means. + trait-name-PirateAccent = Pirate Accent trait-description-PirateAccent = You can't stop speaking like a pirate! @@ -321,7 +324,7 @@ trait-description-AddictionNicotine = trait-name-AnimalFriend = Animal Friend trait-description-AnimalFriend = You have a way with animals. You will never be attacked by animals, unless you attack them first. - + trait-name-Liar = Pathological liar trait-description-Liar = You can hardly bring yourself to tell the truth. Sometimes you lie anyway. diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index efa77391013..288a8209f29 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -73,6 +73,26 @@ sounds: collection: Paracusia +# Floofstation-specific trait, for now. EE will probably port it over later, so we keep it here. +- type: trait + id: Unrevivable + category: Physical + points: 6 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Borg # Borgs cannot be revived and don't have the "dead" state, they just gib + - MedicalBorg + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC # IPCs use their own system + functions: + - !type:TraitAddComponent + components: + - type: Unrevivable + - type: trait id: Muted category: Mental From 465e5ca654b2676e4c0fb9eee71dc5a511d7c232 Mon Sep 17 00:00:00 2001 From: FloofStation Changelogs <175611579+Floof-Station-Bot@users.noreply.github.com> Date: Sat, 21 Dec 2024 20:37:56 +0000 Subject: [PATCH 8/9] Automatic Changelog Update (#423) --- Resources/Changelog/Floof.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Floof.yml b/Resources/Changelog/Floof.yml index b05458dca2b..efe93fd2e44 100644 --- a/Resources/Changelog/Floof.yml +++ b/Resources/Changelog/Floof.yml @@ -1892,3 +1892,10 @@ Entries: id: 245 time: '2024-12-21T03:36:17.0000000+00:00' url: https://github.com/Fansana/floofstation1/pull/422 +- author: Mnemotechnician + changes: + - type: Add + message: Added the "Unrevivable" trait for those who like to live on the edge. + id: 246 + time: '2024-12-21T20:36:58.0000000+00:00' + url: https://github.com/Fansana/floofstation1/pull/423 From 4894794da793fe8c72426da28b33c8ddb734ef12 Mon Sep 17 00:00:00 2001 From: FloofStation Changelogs <175611579+Floof-Station-Bot@users.noreply.github.com> Date: Sat, 21 Dec 2024 20:40:55 +0000 Subject: [PATCH 9/9] Automatic Changelog Update (#418) --- Resources/Changelog/Floof.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Resources/Changelog/Floof.yml b/Resources/Changelog/Floof.yml index efe93fd2e44..78ad12288e3 100644 --- a/Resources/Changelog/Floof.yml +++ b/Resources/Changelog/Floof.yml @@ -1899,3 +1899,13 @@ Entries: id: 246 time: '2024-12-21T20:36:58.0000000+00:00' url: https://github.com/Fansana/floofstation1/pull/423 +- author: Mnemotechnician + changes: + - type: Add + message: >- + Station events can now have customizable conditions. This means that the + station will no longer have to suffer from midround antags when there is + zero security players, and the like. + id: 247 + time: '2024-12-21T20:37:15.0000000+00:00' + url: https://github.com/Fansana/floofstation1/pull/418