Grave digging and decomposition (#23646)
* Grave digging and decomposition * fix * update based on review comments * code review * remove unused field
This commit is contained in:
@@ -185,6 +185,23 @@ public sealed class RottingSystem : EntitySystem
|
||||
args.Handled = component.CurrentTemperature < Atmospherics.T0C + 0.85f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is anything speeding up the decay?
|
||||
/// e.g. buried in a grave
|
||||
/// TODO: hot temperatures increase rot?
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private float GetRotRate(EntityUid uid)
|
||||
{
|
||||
if (_container.TryGetContainingContainer(uid, out var container) &&
|
||||
TryComp<ProRottingContainerComponent>(container.Owner, out var rotContainer))
|
||||
{
|
||||
return rotContainer.DecayModifier;
|
||||
}
|
||||
|
||||
return 1f;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -199,7 +216,7 @@ public sealed class RottingSystem : EntitySystem
|
||||
if (IsRotten(uid) || !IsRotProgressing(uid, perishable))
|
||||
continue;
|
||||
|
||||
perishable.RotAccumulator += perishable.PerishUpdateRate;
|
||||
perishable.RotAccumulator += perishable.PerishUpdateRate * GetRotRate(uid);
|
||||
if (perishable.RotAccumulator >= perishable.RotAfter)
|
||||
{
|
||||
var rot = AddComp<RottingComponent>(uid);
|
||||
@@ -216,7 +233,7 @@ public sealed class RottingSystem : EntitySystem
|
||||
|
||||
if (!IsRotProgressing(uid, perishable))
|
||||
continue;
|
||||
rotting.TotalRotTime += rotting.RotUpdateRate;
|
||||
rotting.TotalRotTime += rotting.RotUpdateRate * GetRotRate(uid);
|
||||
|
||||
if (rotting.DealDamage)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Botany;
|
||||
using Content.Shared.Burial.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Examine;
|
||||
@@ -191,7 +192,7 @@ public sealed class PlantHolderSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tagSystem.HasTag(args.Used, "Shovel"))
|
||||
if (HasComp<ShovelComponent>(args.Used))
|
||||
{
|
||||
if (component.Seed != null)
|
||||
{
|
||||
|
||||
14
Content.Shared/Atmos/Rotting/ProRottingContainerComponent.cs
Normal file
14
Content.Shared/Atmos/Rotting/ProRottingContainerComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Atmos.Rotting;
|
||||
|
||||
/// <summary>
|
||||
/// Entities inside this container will rot at a faster pace, e.g. a grave
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ProRottingContainerComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float DecayModifier = 3f;
|
||||
}
|
||||
|
||||
173
Content.Shared/Burial/BurialSystem.cs
Normal file
173
Content.Shared/Burial/BurialSystem.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using Content.Shared.Burial;
|
||||
using Content.Shared.Burial.Components;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Placeable;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Burial.Systems;
|
||||
|
||||
public sealed class BurialSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedEntityStorageSystem _storageSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GraveComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<GraveComponent, ActivateInWorldEvent>(OnActivate);
|
||||
SubscribeLocalEvent<GraveComponent, AfterInteractUsingEvent>(OnAfterInteractUsing, before: new[] { typeof(PlaceableSurfaceSystem) });
|
||||
SubscribeLocalEvent<GraveComponent, GraveDiggingDoAfterEvent>(OnGraveDigging);
|
||||
|
||||
SubscribeLocalEvent<GraveComponent, StorageOpenAttemptEvent>(OnOpenAttempt);
|
||||
SubscribeLocalEvent<GraveComponent, StorageCloseAttemptEvent>(OnCloseAttempt);
|
||||
SubscribeLocalEvent<GraveComponent, StorageAfterOpenEvent>(OnAfterOpen);
|
||||
SubscribeLocalEvent<GraveComponent, StorageAfterCloseEvent>(OnAfterClose);
|
||||
|
||||
SubscribeLocalEvent<GraveComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, GraveComponent component, InteractUsingEvent args)
|
||||
{
|
||||
if (args.Handled || component.ActiveShovelDigging)
|
||||
return;
|
||||
|
||||
if (TryComp<ShovelComponent>(args.Used, out var shovel))
|
||||
{
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.DigDelay / shovel.SpeedModifier, new GraveDiggingDoAfterEvent(), uid, target: args.Target, used: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true,
|
||||
BreakOnHandChange = true
|
||||
};
|
||||
|
||||
if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs))
|
||||
return;
|
||||
|
||||
StartDigging(uid, args.User, args.Used, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
_popupSystem.PopupClient(Loc.GetString("grave-digging-requires-tool", ("grave", args.Target)), uid, args.User);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnAfterInteractUsing(EntityUid uid, GraveComponent component, AfterInteractUsingEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// don't place shovels on the grave, only dig
|
||||
if (HasComp<ShovelComponent>(args.Used))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, GraveComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
_popupSystem.PopupClient(Loc.GetString("grave-digging-requires-tool", ("grave", args.Target)), uid, args.User);
|
||||
}
|
||||
|
||||
private void OnGraveDigging(EntityUid uid, GraveComponent component, GraveDiggingDoAfterEvent args)
|
||||
{
|
||||
if (args.Used != null)
|
||||
{
|
||||
component.ActiveShovelDigging = false;
|
||||
component.Stream = _audioSystem.Stop(component.Stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
component.HandDiggingDoAfter = null;
|
||||
}
|
||||
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
|
||||
component.DiggingComplete = true;
|
||||
|
||||
if (args.Used != null)
|
||||
_storageSystem.ToggleOpen(args.User, uid);
|
||||
else
|
||||
_storageSystem.TryOpenStorage(args.User, uid); //can only claw out
|
||||
}
|
||||
|
||||
private void StartDigging(EntityUid uid, EntityUid user, EntityUid? used, GraveComponent component)
|
||||
{
|
||||
if (used != null)
|
||||
{
|
||||
_popupSystem.PopupClient(Loc.GetString("grave-start-digging-user", ("grave", uid), ("tool", used)), user, user);
|
||||
_popupSystem.PopupEntity(Loc.GetString("grave-start-digging-others", ("user", user), ("grave", uid), ("tool", used)), user, Filter.PvsExcept(user), true);
|
||||
if (component.Stream == null)
|
||||
component.Stream = _audioSystem.PlayPredicted(component.DigSound, uid, user)?.Entity;
|
||||
component.ActiveShovelDigging = true;
|
||||
Dirty(uid, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
_popupSystem.PopupClient(Loc.GetString("grave-start-digging-user-trapped", ("grave", uid)), user, user, PopupType.Medium);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOpenAttempt(EntityUid uid, GraveComponent component, ref StorageOpenAttemptEvent args)
|
||||
{
|
||||
if (component.DiggingComplete)
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnCloseAttempt(EntityUid uid, GraveComponent component, ref StorageCloseAttemptEvent args)
|
||||
{
|
||||
if (component.DiggingComplete)
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnAfterOpen(EntityUid uid, GraveComponent component, ref StorageAfterOpenEvent args)
|
||||
{
|
||||
component.DiggingComplete = false;
|
||||
}
|
||||
|
||||
private void OnAfterClose(EntityUid uid, GraveComponent component, ref StorageAfterCloseEvent args)
|
||||
{
|
||||
component.DiggingComplete = false;
|
||||
}
|
||||
|
||||
private void OnRelayMovement(EntityUid uid, GraveComponent component, ref ContainerRelayMovementEntityEvent args)
|
||||
{
|
||||
// We track a separate doAfter here, as we want someone with a shovel to
|
||||
// be able to come along and help someone trying to claw their way out
|
||||
if (component.HandDiggingDoAfter != null)
|
||||
return;
|
||||
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Entity, component.DigDelay / component.DigOutByHandModifier, new GraveDiggingDoAfterEvent(), uid, target: uid)
|
||||
{
|
||||
NeedHand = false,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnTargetMove = false,
|
||||
BreakOnHandChange = false,
|
||||
BreakOnDamage = false
|
||||
};
|
||||
|
||||
if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs, out component.HandDiggingDoAfter))
|
||||
return;
|
||||
|
||||
StartDigging(uid, args.Entity, null, component);
|
||||
}
|
||||
}
|
||||
54
Content.Shared/Burial/Components/GraveComponent.cs
Normal file
54
Content.Shared/Burial/Components/GraveComponent.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Burial.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class GraveComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How long it takes to dig this grave, without modifiers
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan DigDelay = TimeSpan.FromSeconds(15);
|
||||
|
||||
/// <summary>
|
||||
/// Modifier if digging yourself out by hand if buried alive
|
||||
/// TODO: Handle digging with bare hands in the tools system
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float DigOutByHandModifier = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Sound to make when digging/filling this grave
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public SoundPathSpecifier DigSound = new SoundPathSpecifier("/Audio/Items/shovel_dig.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithLoop(true)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Is this grave in the process of being dug/filled?
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool DiggingComplete = false;
|
||||
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public EntityUid? Stream;
|
||||
|
||||
/// <summary>
|
||||
/// Auto-networked field to track shovel digging.
|
||||
/// This makes sure a looping audio Stream isn't opened
|
||||
/// on the client-side. (DoAfterId/EntityUid isn't serializable.)
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly), AutoNetworkedField]
|
||||
public bool ActiveShovelDigging;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks someone digging themself out of the grave
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public DoAfterId? HandDiggingDoAfter;
|
||||
}
|
||||
13
Content.Shared/Burial/Components/ShovelComponent.cs
Normal file
13
Content.Shared/Burial/Components/ShovelComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Burial.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ShovelComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The speed modifier for how fast this shovel will dig.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SpeedModifier = 1f;
|
||||
}
|
||||
9
Content.Shared/Burial/GraveDiggingDoAfterEvent.cs
Normal file
9
Content.Shared/Burial/GraveDiggingDoAfterEvent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Burial;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class GraveDiggingDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
@@ -87,3 +87,8 @@
|
||||
license: "CC0-1.0"
|
||||
copyright: "User Hanbaal on freesound.org. Converted to ogg by TheShuEd"
|
||||
source: "https://freesound.org/people/Hanbaal/sounds/178669/"
|
||||
|
||||
- files: ["shovel_dig.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from tgstation, modified by themias (github) for ss14"
|
||||
source: "https://github.com/tgstation/tgstation/tree/85a0925051bb00e7a950ee66cb7f87982cd22439/sound/effects/shovel_dig.ogg"
|
||||
BIN
Resources/Audio/Items/shovel_dig.ogg
Normal file
BIN
Resources/Audio/Items/shovel_dig.ogg
Normal file
Binary file not shown.
5
Resources/Locale/en-US/burial/burial.ftl
Normal file
5
Resources/Locale/en-US/burial/burial.ftl
Normal file
@@ -0,0 +1,5 @@
|
||||
grave-start-digging-others = {CAPITALIZE($user)} starts digging {THE($grave)} with {THE($tool)}.
|
||||
grave-start-digging-user = You start digging {THE($grave)} with {THE($tool)}.
|
||||
grave-start-digging-user-trapped = You start clawing your way out of {THE($grave)}!
|
||||
|
||||
grave-digging-requires-tool = You need a tool to dig this {$grave}!
|
||||
@@ -30,3 +30,6 @@ tool-quality-woodcutting-tool-name = Hatchet
|
||||
|
||||
tool-quality-rolling-name = Rolling
|
||||
tool-quality-rolling-tool-name = Rolling Pin
|
||||
|
||||
tool-quality-digging-name = Digging
|
||||
tool-quality-digging-tool-name = Shovel
|
||||
@@ -79,6 +79,8 @@
|
||||
damage:
|
||||
types:
|
||||
Blunt: 1
|
||||
- type: Shovel
|
||||
speedModifier: 0.1 # you can try
|
||||
|
||||
- type: entity
|
||||
parent: UtensilBasePlastic
|
||||
@@ -93,6 +95,8 @@
|
||||
- type: Utensil
|
||||
types:
|
||||
- Spoon
|
||||
- type: Shovel
|
||||
speedModifier: 0.1 # you can try
|
||||
|
||||
- type: entity
|
||||
parent: UtensilBase
|
||||
|
||||
@@ -96,7 +96,6 @@
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- Shovel
|
||||
- BotanyShovel
|
||||
- type: Sprite
|
||||
sprite: Objects/Tools/Hydroponics/spade.rsi
|
||||
@@ -109,6 +108,8 @@
|
||||
Piercing: 5 # I guess you can stab it into them?
|
||||
- type: Item
|
||||
sprite: Objects/Tools/Hydroponics/spade.rsi
|
||||
- type: Shovel
|
||||
speedModifier: 0.75 # slower at digging than a full-sized shovel
|
||||
|
||||
- type: entity
|
||||
name: plant bag
|
||||
|
||||
@@ -489,9 +489,6 @@
|
||||
id: Shovel
|
||||
description: A large tool for digging and moving dirt.
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- Shovel
|
||||
- type: Sprite
|
||||
sprite: Objects/Tools/shovel.rsi
|
||||
state: icon
|
||||
@@ -509,6 +506,7 @@
|
||||
Wood: 50
|
||||
- type: StaticPrice
|
||||
price: 25
|
||||
- type: Shovel
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
|
||||
@@ -496,7 +496,7 @@
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 15
|
||||
damage: 200 # discourage just beating the grave to break it open
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound:
|
||||
@@ -510,6 +510,15 @@
|
||||
acts: [ "Destruction" ]
|
||||
- type: Physics
|
||||
bodyType: Static
|
||||
- type: Grave
|
||||
- type: ProRottingContainer
|
||||
- type: EntityStorage
|
||||
airtight: true
|
||||
isCollidableWhenOpen: false
|
||||
closeSound:
|
||||
path: /Audio/Items/shovel_dig.ogg
|
||||
openSound:
|
||||
path: /Audio/Items/shovel_dig.ogg
|
||||
|
||||
- type: entity
|
||||
parent: CrateWoodenGrave
|
||||
|
||||
Reference in New Issue
Block a user