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 <yurii.kis@smartteksas.com>
This commit is contained in:
KISS
2023-11-06 04:41:42 +02:00
committed by GitHub
parent b1f39ad2ad
commit 4cacb7b9e3
10 changed files with 261 additions and 16 deletions

View File

@@ -21,27 +21,46 @@ public sealed class EntityAnomalySystem : EntitySystem
{ {
SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalyPulseEvent>(OnPulse); SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalyPulseEvent>(OnPulse);
SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical); SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
SubscribeLocalEvent<EntitySpawnAnomalyComponent, AnomalyStabilityChangedEvent>(OnStabilityChanged);
} }
private void OnPulse(EntityUid uid, EntitySpawnAnomalyComponent component, ref AnomalyPulseEvent args) private void OnPulse(EntityUid uid, EntitySpawnAnomalyComponent component, ref AnomalyPulseEvent args)
{ {
if (!component.SpawnOnPulse)
return;
var range = component.SpawnRange * args.Stability; var range = component.SpawnRange * args.Stability;
var amount = (int) (component.MaxSpawnAmount * args.Severity + 0.5f); var amount = (int) (component.MaxSpawnAmount * args.Severity + 0.5f);
var xform = Transform(uid); 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) private void OnSupercritical(EntityUid uid, EntitySpawnAnomalyComponent component, ref AnomalySupercriticalEvent args)
{ {
if (!component.SpawnOnSuperCritical)
return;
var xform = Transform(uid); var xform = Transform(uid);
// A cluster of monsters // A cluster of entities
SpawnMonstersOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange, component.Spawns); SpawnEntitesOnOpenTiles(component, xform, component.MaxSpawnAmount, component.SpawnRange, component.Spawns);
// And so much meat (for the meat anomaly at least) // 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<EntProtoId> 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<EntProtoId> spawns)
{ {
if (!component.Spawns.Any()) if (!component.Spawns.Any())
return; return;

View File

@@ -1,7 +1,4 @@
using Content.Shared.Maps;
using Robust.Shared.Prototypes; 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; namespace Content.Shared.Anomaly.Effects.Components;
@@ -36,8 +33,21 @@ public sealed partial class EntitySpawnAnomalyComponent : Component
public float SpawnRange = 5f; public float SpawnRange = 5f;
/// <summary> /// <summary>
/// The tile that is spawned by the anomaly's effect /// Whether or not anomaly spawns entities on Pulse
/// </summary> /// </summary>
[DataField("floorTileId", customTypeSerializer: typeof(PrototypeIdSerializer<ContentTileDefinition>)), ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public string FloorTileId = "FloorFlesh"; public bool SpawnOnPulse = true;
/// <summary>
/// Whether or not anomaly spawns entities on SuperCritical
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public bool SpawnOnSuperCritical = true;
/// <summary>
/// Whether or not anomaly spawns entities on StabilityChanged
/// The idea was to spawn entities either on Pulse/Supercritical OR StabilityChanged
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public bool SpawnOnStabilityChanged = false;
} }

View File

@@ -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
{
/// <summary>
/// Modified mob friction while on FrictionContactsComponent
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
public float MobFriction = 0.5f;
/// <summary>
/// Modified mob friction without input while on FrictionContactsComponent
/// </summary>
[AutoNetworkedField]
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MobFrictionNoInput = 0.05f;
/// <summary>
/// Modified mob acceleration while on FrictionContactsComponent
/// </summary>
[AutoNetworkedField]
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MobAcceleration = 2.0f;
}

View File

@@ -87,19 +87,19 @@ namespace Content.Shared.Movement.Components
/// <summary> /// <summary>
/// The acceleration applied to mobs when moving. /// The acceleration applied to mobs when moving.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField] [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite), DataField]
public float Acceleration = DefaultAcceleration; public float Acceleration = DefaultAcceleration;
/// <summary> /// <summary>
/// The negative velocity applied for friction. /// The negative velocity applied for friction.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField] [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite), DataField]
public float Friction = DefaultFriction; public float Friction = DefaultFriction;
/// <summary> /// <summary>
/// The negative velocity applied for friction. /// The negative velocity applied for friction.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField] [AutoNetworkedField, ViewVariables(VVAccess.ReadWrite), DataField]
public float? FrictionNoInput; public float? FrictionNoInput;
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]

View File

@@ -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<EntityUid> _toUpdate = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FrictionContactsComponent, StartCollideEvent>(OnEntityEnter);
SubscribeLocalEvent<FrictionContactsComponent, EndCollideEvent>(OnEntityExit);
SubscribeLocalEvent<FrictionContactsComponent, ComponentShutdown>(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<PhysicsComponent>(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;
}
}

View File

@@ -25,7 +25,7 @@ namespace Content.Shared.Movement.Systems
move.WalkSpeedModifier = ev.WalkSpeedModifier; move.WalkSpeedModifier = ev.WalkSpeedModifier;
move.SprintSpeedModifier = ev.SprintSpeedModifier; move.SprintSpeedModifier = ev.SprintSpeedModifier;
Dirty(move); Dirty(uid, move);
} }
public void ChangeBaseSpeed(EntityUid uid, float baseWalkSpeed, float baseSprintSpeed, float acceleration, MovementSpeedModifierComponent? move = null) 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.BaseWalkSpeed = baseWalkSpeed;
move.BaseSprintSpeed = baseSprintSpeed; move.BaseSprintSpeed = baseSprintSpeed;
move.Acceleration = acceleration; 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);
} }
} }

View File

@@ -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

View File

@@ -241,6 +241,13 @@
- type: ProjectileAnomaly - type: ProjectileAnomaly
projectilePrototype: ProjectileIcicle projectilePrototype: ProjectileIcicle
targetNonSentientChance: 0.1 targetNonSentientChance: 0.1
- type: EntitySpawnAnomaly
spawns:
- IceCrust
maxSpawnAmount: 17
spawnOnPulse: false
spawnOnSuperCritical: false
spawnOnStabilityChanged: true
- type: TempAffectingAnomaly - type: TempAffectingAnomaly
tempChangePerSecond: -25 tempChangePerSecond: -25
hotspotExposeTemperature: -1000 hotspotExposeTemperature: -1000

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

View File

@@ -0,0 +1,14 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from space-station-14+Resources+Textures+Tiles+Planet+Snow",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "ice"
}
]
}