Rat King Refactor Part 0: Separate Rummaging from RatKingComponent. (#40530)

* separate rummager into its own component/system

* thing

* address review and entitytables

* reviews

* review

* warnings

* Update Content.Shared/RatKing/Systems/RummagerSystem.cs

---------

Co-authored-by: Jessica M <jessica@maybe.sh>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
Jessica M
2025-09-24 18:50:24 -07:00
committed by GitHub
parent d699a4e985
commit b2d09ba457
7 changed files with 117 additions and 75 deletions

View File

@@ -1,17 +1,16 @@
using Content.Shared.Random; using Content.Shared.EntityTable.EntitySelectors;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.RatKing; namespace Content.Shared.RatKing.Components;
/// <summary> /// <summary>
/// This is used for entities that can be /// This is used for entities that can be
/// rummaged through by the rat king to get loot. /// rummaged through by the rat king to get loot.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRatKingSystem))] [RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState] [AutoGenerateComponentState]
public sealed partial class RatKingRummageableComponent : Component public sealed partial class RummageableComponent : Component
{ {
/// <summary> /// <summary>
/// Whether or not this entity has been rummaged through already. /// Whether or not this entity has been rummaged through already.
@@ -28,11 +27,10 @@ public sealed partial class RatKingRummageableComponent : Component
public float RummageDuration = 3f; public float RummageDuration = 3f;
/// <summary> /// <summary>
/// A weighted random entity prototype containing the different loot that rummaging can provide. /// The entity table to select loot from.
/// </summary> /// </summary>
[DataField("rummageLoot", customTypeSerializer: typeof(PrototypeIdSerializer<WeightedRandomEntityPrototype>)), ViewVariables(VVAccess.ReadWrite)] [DataField(required: true)]
[AutoNetworkedField] public EntityTableSelector Table = default!;
public string RummageLoot = "RatKingLoot";
/// <summary> /// <summary>
/// Sound played on rummage completion. /// Sound played on rummage completion.

View File

@@ -0,0 +1,11 @@
using Robust.Shared.GameStates;
namespace Content.Shared.RatKing.Components;
/// <summary>
/// This is used for entities that can rummage through entities
/// with the <see cref="RummageableComponent"/>
/// </summary>
///
[RegisterComponent, NetworkedComponent]
public sealed partial class RummagerComponent : Component;

View File

@@ -1,26 +1,15 @@
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Actions.Components; using Content.Shared.Actions.Components;
using Content.Shared.DoAfter;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization;
namespace Content.Shared.RatKing; namespace Content.Shared.RatKing;
public abstract class SharedRatKingSystem : EntitySystem public abstract class SharedRatKingSystem : EntitySystem
{ {
[Dependency] private readonly INetManager _net = default!;
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
[Dependency] protected readonly IRobustRandom Random = default!; [Dependency] protected readonly IRobustRandom Random = default!;
[Dependency] private readonly SharedActionsSystem _action = default!; [Dependency] private readonly SharedActionsSystem _action = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -28,11 +17,7 @@ public abstract class SharedRatKingSystem : EntitySystem
SubscribeLocalEvent<RatKingComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<RatKingComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<RatKingComponent, ComponentShutdown>(OnShutdown); SubscribeLocalEvent<RatKingComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<RatKingComponent, RatKingOrderActionEvent>(OnOrderAction); SubscribeLocalEvent<RatKingComponent, RatKingOrderActionEvent>(OnOrderAction);
SubscribeLocalEvent<RatKingServantComponent, ComponentShutdown>(OnServantShutdown); SubscribeLocalEvent<RatKingServantComponent, ComponentShutdown>(OnServantShutdown);
SubscribeLocalEvent<RatKingRummageableComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerb);
SubscribeLocalEvent<RatKingRummageableComponent, RatKingRummageDoAfterEvent>(OnDoAfterComplete);
} }
private void OnStartup(EntityUid uid, RatKingComponent component, ComponentStartup args) private void OnStartup(EntityUid uid, RatKingComponent component, ComponentStartup args)
@@ -105,43 +90,6 @@ public abstract class SharedRatKingSystem : EntitySystem
_action.StartUseDelay(component.ActionOrderLooseEntity); _action.StartUseDelay(component.ActionOrderLooseEntity);
} }
private void OnGetVerb(EntityUid uid, RatKingRummageableComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!HasComp<RatKingComponent>(args.User) || component.Looted)
return;
args.Verbs.Add(new AlternativeVerb
{
Text = Loc.GetString("rat-king-rummage-text"),
Priority = 0,
Act = () =>
{
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.RummageDuration,
new RatKingRummageDoAfterEvent(), uid, uid)
{
BlockDuplicate = true,
BreakOnDamage = true,
BreakOnMove = true,
DistanceThreshold = 2f
});
}
});
}
private void OnDoAfterComplete(EntityUid uid, RatKingRummageableComponent component, RatKingRummageDoAfterEvent args)
{
if (args.Cancelled || component.Looted)
return;
component.Looted = true;
Dirty(uid, component);
_audio.PlayPredicted(component.Sound, uid, args.User);
var spawn = PrototypeManager.Index<WeightedRandomEntityPrototype>(component.RummageLoot).Pick(Random);
if (_net.IsServer)
Spawn(spawn, Transform(uid).Coordinates);
}
public void UpdateAllServants(EntityUid uid, RatKingComponent component) public void UpdateAllServants(EntityUid uid, RatKingComponent component)
{ {
foreach (var servant in component.Servants) foreach (var servant in component.Servants)
@@ -160,9 +108,3 @@ public abstract class SharedRatKingSystem : EntitySystem
} }
} }
[Serializable, NetSerializable]
public sealed partial class RatKingRummageDoAfterEvent : SimpleDoAfterEvent
{
}

View File

@@ -0,0 +1,82 @@
using Content.Shared.DoAfter;
using Content.Shared.EntityTable;
using Content.Shared.RatKing.Components;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
namespace Content.Shared.RatKing.Systems;
public sealed class RummagerSystem : EntitySystem
{
[Dependency] private readonly EntityTableSystem _entityTable = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RummageableComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerb);
SubscribeLocalEvent<RummageableComponent, RummageDoAfterEvent>(OnDoAfterComplete);
}
private void OnGetVerb(Entity<RummageableComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!HasComp<RummagerComponent>(args.User) || ent.Comp.Looted)
return;
var user = args.User;
args.Verbs.Add(new AlternativeVerb
{
Text = Loc.GetString("rat-king-rummage-text"),
Priority = 0,
Act = () =>
{
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager,
user,
ent.Comp.RummageDuration,
new RummageDoAfterEvent(),
ent,
ent)
{
BlockDuplicate = true,
BreakOnDamage = true,
BreakOnMove = true,
DistanceThreshold = 2f
});
}
});
}
private void OnDoAfterComplete(Entity<RummageableComponent> ent, ref RummageDoAfterEvent args)
{
if (args.Cancelled || ent.Comp.Looted)
return;
ent.Comp.Looted = true;
Dirty(ent, ent.Comp);
_audio.PlayPredicted(ent.Comp.Sound, ent, args.User);
if (_net.IsClient)
return;
var spawns = _entityTable.GetSpawns(ent.Comp.Table);
var coordinates = Transform(ent).Coordinates;
foreach (var spawn in spawns)
{
Spawn(spawn, coordinates);
}
}
}
/// <summary>
/// DoAfter event for rummaging through a container with RummageableComponent.
/// </summary>
[Serializable, NetSerializable]
public sealed partial class RummageDoAfterEvent : SimpleDoAfterEvent;

View File

@@ -102,6 +102,7 @@
- CannotSuicide - CannotSuicide
- FootstepSound - FootstepSound
- type: NoSlip - type: NoSlip
- type: Rummager
- type: RatKing - type: RatKing
hungerPerArmyUse: 25 hungerPerArmyUse: 25
hungerPerDomainUse: 50 hungerPerDomainUse: 50
@@ -306,12 +307,16 @@
sprite: Mobs/Effects/onfire.rsi sprite: Mobs/Effects/onfire.rsi
normalState: Mouse_burning normalState: Mouse_burning
- type: weightedRandomEntity - type: entityTable
id: RatKingLoot id: RatKingLoot
weights: table: !type:GroupSelector
RandomSpawner100: 66 #garbage children:
FoodCheese: 28 #food - id: RandomSpawner100
IngotGold1: 5 #loot weight: 66
- id: FoodCheese
weight: 28
- id: IngotGold1
weight: 5
- type: entity - type: entity
parent: BaseAction parent: BaseAction

View File

@@ -80,7 +80,9 @@
interfaces: interfaces:
enum.DisposalUnitUiKey.Key: enum.DisposalUnitUiKey.Key:
type: DisposalUnitBoundUserInterface type: DisposalUnitBoundUserInterface
- type: RatKingRummageable - type: Rummageable
table: !type:NestedSelector
tableId: RatKingLoot
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
drainBuffer: drainBuffer:

View File

@@ -97,7 +97,9 @@
interfaces: interfaces:
enum.DisposalUnitUiKey.Key: enum.DisposalUnitUiKey.Key:
type: DisposalUnitBoundUserInterface type: DisposalUnitBoundUserInterface
- type: RatKingRummageable - type: Rummageable
table: !type:NestedSelector
tableId: RatKingLoot
- type: RequireProjectileTarget - type: RequireProjectileTarget
- type: entity - type: entity