From 4cacb7b9e3220e44e94d27c5d8c3b83c4d93b1a0 Mon Sep 17 00:00:00 2001 From: KISS <59531932+YuriyKiss@users.noreply.github.com> Date: Mon, 6 Nov 2023 04:41:42 +0200 Subject: [PATCH] Ice anomaly spawns ice underneath it (#21227) * added TileAnomalySystem to AnomalyIce * added FloorIce for station * created ice crust entity to spawn under ice anomaly * update draw depth for ice crust * uh oh, added ice-sliding but at what cost * resolved mispredicts * updated sprite alpha, removed appearance component (not used) * fixed function not reflecting event name, left datafield attributes blank, added one comment about saving data (?) --------- Co-authored-by: Yurii Kis --- .../Anomaly/Effects/EntityAnomalySystem.cs | 29 ++++- .../Components/EntitySpawnAnomalyComponent.cs | 22 ++-- .../Components/FrictionContactsComponent.cs | 31 ++++++ .../MovementSpeedModifierComponent.cs | 6 +- .../Systems/FrictionContactsSystem.cs | 100 ++++++++++++++++++ .../Systems/MovementSpeedModifierSystem.cs | 16 ++- .../Entities/Objects/Misc/ice_crust.yml | 52 +++++++++ .../Structures/Specific/Anomaly/anomalies.yml | 7 ++ .../Objects/Misc/ice_crust.rsi/ice.png | Bin 0 -> 293 bytes .../Objects/Misc/ice_crust.rsi/meta.json | 14 +++ 10 files changed, 261 insertions(+), 16 deletions(-) create mode 100644 Content.Shared/Movement/Components/FrictionContactsComponent.cs create mode 100644 Content.Shared/Movement/Systems/FrictionContactsSystem.cs create mode 100644 Resources/Prototypes/Entities/Objects/Misc/ice_crust.yml create mode 100644 Resources/Textures/Objects/Misc/ice_crust.rsi/ice.png create mode 100644 Resources/Textures/Objects/Misc/ice_crust.rsi/meta.json diff --git a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs index 5f20183314..ee4e2ac115 100644 --- a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs @@ -21,27 +21,46 @@ public sealed class EntityAnomalySystem : EntitySystem { SubscribeLocalEvent(OnPulse); SubscribeLocalEvent(OnSupercritical); + SubscribeLocalEvent(OnStabilityChanged); } private void OnPulse(EntityUid uid, EntitySpawnAnomalyComponent component, ref AnomalyPulseEvent args) { + if (!component.SpawnOnPulse) + return; + var range = component.SpawnRange * args.Stability; var amount = (int) (component.MaxSpawnAmount * args.Severity + 0.5f); var xform = Transform(uid); - SpawnMonstersOnOpenTiles(component, xform, amount, range, component.Spawns); + SpawnEntitesOnOpenTiles(component, xform, amount, range, component.Spawns); } private void OnSupercritical(EntityUid uid, EntitySpawnAnomalyComponent component, ref AnomalySupercriticalEvent args) { + if (!component.SpawnOnSuperCritical) + return; + var xform = Transform(uid); - // A cluster of monsters - SpawnMonstersOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange, component.Spawns); + // A cluster of entities + SpawnEntitesOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange, component.Spawns); // And so much meat (for the meat anomaly at least) - SpawnMonstersOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange, component.SuperCriticalSpawns); + SpawnEntitesOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange, component.SuperCriticalSpawns); } - private void SpawnMonstersOnOpenTiles(EntitySpawnAnomalyComponent component, TransformComponent xform, int amount, float radius, List spawns) + private void OnStabilityChanged(EntityUid uid, EntitySpawnAnomalyComponent component, ref AnomalyStabilityChangedEvent args) + { + if (!component.SpawnOnStabilityChanged) + return; + + var range = component.SpawnRange * args.Stability; + var amount = (int) (component.MaxSpawnAmount * args.Stability + 0.5f); + + var xform = Transform(uid); + SpawnEntitesOnOpenTiles(component, xform, amount, range, component.Spawns); + } + + private void SpawnEntitesOnOpenTiles(EntitySpawnAnomalyComponent component, TransformComponent xform, int amount, float radius, List spawns) { if (!component.Spawns.Any()) return; diff --git a/Content.Shared/Anomaly/Effects/Components/EntitySpawnAnomalyComponent.cs b/Content.Shared/Anomaly/Effects/Components/EntitySpawnAnomalyComponent.cs index 7083c91040..7a816e4312 100644 --- a/Content.Shared/Anomaly/Effects/Components/EntitySpawnAnomalyComponent.cs +++ b/Content.Shared/Anomaly/Effects/Components/EntitySpawnAnomalyComponent.cs @@ -1,7 +1,4 @@ -using Content.Shared.Maps; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Anomaly.Effects.Components; @@ -36,8 +33,21 @@ public sealed partial class EntitySpawnAnomalyComponent : Component public float SpawnRange = 5f; /// - /// The tile that is spawned by the anomaly's effect + /// Whether or not anomaly spawns entities on Pulse /// - [DataField("floorTileId", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] - public string FloorTileId = "FloorFlesh"; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool SpawnOnPulse = true; + + /// + /// Whether or not anomaly spawns entities on SuperCritical + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool SpawnOnSuperCritical = true; + + /// + /// Whether or not anomaly spawns entities on StabilityChanged + /// The idea was to spawn entities either on Pulse/Supercritical OR StabilityChanged + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool SpawnOnStabilityChanged = false; } diff --git a/Content.Shared/Movement/Components/FrictionContactsComponent.cs b/Content.Shared/Movement/Components/FrictionContactsComponent.cs new file mode 100644 index 0000000000..693ada49f7 --- /dev/null +++ b/Content.Shared/Movement/Components/FrictionContactsComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.Movement.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +[NetworkedComponent, RegisterComponent] +[AutoGenerateComponentState] +[Access(typeof(FrictionContactsSystem))] +public sealed partial class FrictionContactsComponent : Component +{ + /// + /// Modified mob friction while on FrictionContactsComponent + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public float MobFriction = 0.5f; + + /// + /// Modified mob friction without input while on FrictionContactsComponent + /// + [AutoNetworkedField] + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MobFrictionNoInput = 0.05f; + + /// + /// Modified mob acceleration while on FrictionContactsComponent + /// + [AutoNetworkedField] + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MobAcceleration = 2.0f; +} diff --git a/Content.Shared/Movement/Components/MovementSpeedModifierComponent.cs b/Content.Shared/Movement/Components/MovementSpeedModifierComponent.cs index 7efc12a731..a0feab7052 100644 --- a/Content.Shared/Movement/Components/MovementSpeedModifierComponent.cs +++ b/Content.Shared/Movement/Components/MovementSpeedModifierComponent.cs @@ -87,19 +87,19 @@ namespace Content.Shared.Movement.Components /// /// The acceleration applied to mobs when moving. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite), DataField] public float Acceleration = DefaultAcceleration; /// /// The negative velocity applied for friction. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite), DataField] public float Friction = DefaultFriction; /// /// The negative velocity applied for friction. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite), DataField] public float? FrictionNoInput; [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] diff --git a/Content.Shared/Movement/Systems/FrictionContactsSystem.cs b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs new file mode 100644 index 0000000000..b104c549e6 --- /dev/null +++ b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs @@ -0,0 +1,100 @@ +using Content.Shared.Movement.Components; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared.Movement.Systems; + +public sealed class FrictionContactsSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; + + // Comment copied from "original" SlowContactsSystem.cs + // TODO full-game-save + // Either these need to be processed before a map is saved, or slowed/slowing entities need to update on init. + private HashSet _toUpdate = new(); + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnEntityEnter); + SubscribeLocalEvent(OnEntityExit); + SubscribeLocalEvent(OnShutdown); + + UpdatesAfter.Add(typeof(SharedPhysicsSystem)); + } + + private void OnEntityEnter(EntityUid uid, FrictionContactsComponent component, ref StartCollideEvent args) + { + var otherUid = args.OtherEntity; + + if (!HasComp(otherUid, typeof(MovementSpeedModifierComponent))) + return; + + _toUpdate.Add(otherUid); + } + + private void OnEntityExit(EntityUid uid, FrictionContactsComponent component, ref EndCollideEvent args) + { + var otherUid = args.OtherEntity; + + if (!HasComp(otherUid, typeof(MovementSpeedModifierComponent))) + return; + + _toUpdate.Add(otherUid); + } + + private void OnShutdown(EntityUid uid, FrictionContactsComponent component, ComponentShutdown args) + { + if (!TryComp(uid, out PhysicsComponent? phys)) + return; + + _toUpdate.UnionWith(_physics.GetContactingEntities(uid, phys)); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var uid in _toUpdate) + { + ApplyFrictionChange(uid); + } + + _toUpdate.Clear(); + } + + private void ApplyFrictionChange(EntityUid uid) + { + if (!EntityManager.TryGetComponent(uid, out var physicsComponent)) + return; + + if (!TryComp(uid, out MovementSpeedModifierComponent? speedModifier)) + return; + + FrictionContactsComponent? frictionComponent = TouchesFrictionContactsComponent(uid, physicsComponent); + + if (frictionComponent == null) + { + _speedModifierSystem.ChangeFriction(uid, MovementSpeedModifierComponent.DefaultFriction, null, MovementSpeedModifierComponent.DefaultAcceleration, speedModifier); + } + else + { + _speedModifierSystem.ChangeFriction(uid, frictionComponent.MobFriction, frictionComponent.MobFrictionNoInput, frictionComponent.MobAcceleration, speedModifier); + } + } + + private FrictionContactsComponent? TouchesFrictionContactsComponent(EntityUid uid, PhysicsComponent physicsComponent) + { + foreach (var ent in _physics.GetContactingEntities(uid, physicsComponent)) + { + if (!TryComp(ent, out FrictionContactsComponent? frictionContacts)) + continue; + + return frictionContacts; + } + + return null; + } +} diff --git a/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs b/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs index c95ab379c6..7c793d5eb8 100644 --- a/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs +++ b/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs @@ -25,7 +25,7 @@ namespace Content.Shared.Movement.Systems move.WalkSpeedModifier = ev.WalkSpeedModifier; move.SprintSpeedModifier = ev.SprintSpeedModifier; - Dirty(move); + Dirty(uid, move); } public void ChangeBaseSpeed(EntityUid uid, float baseWalkSpeed, float baseSprintSpeed, float acceleration, MovementSpeedModifierComponent? move = null) @@ -36,7 +36,19 @@ namespace Content.Shared.Movement.Systems move.BaseWalkSpeed = baseWalkSpeed; move.BaseSprintSpeed = baseSprintSpeed; move.Acceleration = acceleration; - Dirty(move); + Dirty(uid, move); + } + + // We might want to create separate RefreshMovementFrictionModifiersEvent and RefreshMovementFrictionModifiers function that will call it + public void ChangeFriction(EntityUid uid, float friction, float? frictionNoInput, float acceleration, MovementSpeedModifierComponent? move = null) + { + if (!Resolve(uid, ref move, false)) + return; + + move.Friction = friction; + move.FrictionNoInput = frictionNoInput; + move.Acceleration = acceleration; + Dirty(uid, move); } } diff --git a/Resources/Prototypes/Entities/Objects/Misc/ice_crust.yml b/Resources/Prototypes/Entities/Objects/Misc/ice_crust.yml new file mode 100644 index 0000000000..2f8da6dada --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Misc/ice_crust.yml @@ -0,0 +1,52 @@ +- type: entity + id: IceCrust + name: ice crust + description: It's cold and slippery. + placement: + mode: SnapgridCenter + snap: + - Wall + components: + - type: MeleeSound + soundGroups: + Brute: + path: + "/Audio/Weapons/slash.ogg" + - type: Sprite + sprite: Objects/Misc/ice_crust.rsi + layers: + - state: ice + drawdepth: FloorObjects + color: "#ffffff44" + - type: Clickable + - type: Transform + anchored: true + - type: Physics + - type: Fixtures + fixtures: + fix1: + hard: false + density: 7 + shape: + !type:PhysShapeAabb + bounds: "-0.4,-0.4,0.4,0.4" + layer: + - MidImpassable + - type: Damageable + damageModifierSet: Wood + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Temperature + heatDamage: + types: + Heat: 5 + coldDamage: {} + ColdDamageThreshold: 0 + - type: FrictionContacts + \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 3526216c13..234dd52359 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -241,6 +241,13 @@ - type: ProjectileAnomaly projectilePrototype: ProjectileIcicle targetNonSentientChance: 0.1 + - type: EntitySpawnAnomaly + spawns: + - IceCrust + maxSpawnAmount: 17 + spawnOnPulse: false + spawnOnSuperCritical: false + spawnOnStabilityChanged: true - type: TempAffectingAnomaly tempChangePerSecond: -25 hotspotExposeTemperature: -1000 diff --git a/Resources/Textures/Objects/Misc/ice_crust.rsi/ice.png b/Resources/Textures/Objects/Misc/ice_crust.rsi/ice.png new file mode 100644 index 0000000000000000000000000000000000000000..0f460febe86f871d893fa622f12c5042e5ddaf05 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D0tn| z#WAE}&f9And7BMHSOUb&8I)6G+&oTZXa+A!nKNa|loC%FW~<0)PwVwE_Nk^Eu;Ek8 zv5PzXjkCIX&C2<0TjZDL3|H>2(2T1