diff --git a/Content.Shared/RatKing/RatKingRummageableComponent.cs b/Content.Shared/RatKing/Components/RummageableComponent.cs
similarity index 59%
rename from Content.Shared/RatKing/RatKingRummageableComponent.cs
rename to Content.Shared/RatKing/Components/RummageableComponent.cs
index f3a389ef76..ea92b55805 100644
--- a/Content.Shared/RatKing/RatKingRummageableComponent.cs
+++ b/Content.Shared/RatKing/Components/RummageableComponent.cs
@@ -1,17 +1,16 @@
-using Content.Shared.Random;
+using Content.Shared.EntityTable.EntitySelectors;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Shared.RatKing;
+namespace Content.Shared.RatKing.Components;
///
/// This is used for entities that can be
/// rummaged through by the rat king to get loot.
///
-[RegisterComponent, NetworkedComponent, Access(typeof(SharedRatKingSystem))]
+[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState]
-public sealed partial class RatKingRummageableComponent : Component
+public sealed partial class RummageableComponent : Component
{
///
/// Whether or not this entity has been rummaged through already.
@@ -28,11 +27,10 @@ public sealed partial class RatKingRummageableComponent : Component
public float RummageDuration = 3f;
///
- /// A weighted random entity prototype containing the different loot that rummaging can provide.
+ /// The entity table to select loot from.
///
- [DataField("rummageLoot", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)]
- [AutoNetworkedField]
- public string RummageLoot = "RatKingLoot";
+ [DataField(required: true)]
+ public EntityTableSelector Table = default!;
///
/// Sound played on rummage completion.
diff --git a/Content.Shared/RatKing/Components/RummagerComponent.cs b/Content.Shared/RatKing/Components/RummagerComponent.cs
new file mode 100644
index 0000000000..338e9eee13
--- /dev/null
+++ b/Content.Shared/RatKing/Components/RummagerComponent.cs
@@ -0,0 +1,11 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.RatKing.Components;
+
+///
+/// This is used for entities that can rummage through entities
+/// with the
+///
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class RummagerComponent : Component;
diff --git a/Content.Shared/RatKing/SharedRatKingSystem.cs b/Content.Shared/RatKing/SharedRatKingSystem.cs
index edb2ab90db..3f6c9bdc22 100644
--- a/Content.Shared/RatKing/SharedRatKingSystem.cs
+++ b/Content.Shared/RatKing/SharedRatKingSystem.cs
@@ -1,26 +1,15 @@
using Content.Shared.Actions;
-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 Content.Shared.Actions.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-using Robust.Shared.Serialization;
namespace Content.Shared.RatKing;
public abstract class SharedRatKingSystem : EntitySystem
{
- [Dependency] private readonly INetManager _net = default!;
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
[Dependency] protected readonly IRobustRandom Random = default!;
[Dependency] private readonly SharedActionsSystem _action = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
///
public override void Initialize()
@@ -28,11 +17,7 @@ public abstract class SharedRatKingSystem : EntitySystem
SubscribeLocalEvent(OnStartup);
SubscribeLocalEvent(OnShutdown);
SubscribeLocalEvent(OnOrderAction);
-
SubscribeLocalEvent(OnServantShutdown);
-
- SubscribeLocalEvent>(OnGetVerb);
- SubscribeLocalEvent(OnDoAfterComplete);
}
private void OnStartup(EntityUid uid, RatKingComponent component, ComponentStartup args)
@@ -105,43 +90,6 @@ public abstract class SharedRatKingSystem : EntitySystem
_action.StartUseDelay(component.ActionOrderLooseEntity);
}
- private void OnGetVerb(EntityUid uid, RatKingRummageableComponent component, GetVerbsEvent args)
- {
- if (!HasComp(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(component.RummageLoot).Pick(Random);
- if (_net.IsServer)
- Spawn(spawn, Transform(uid).Coordinates);
- }
-
public void UpdateAllServants(EntityUid uid, RatKingComponent component)
{
foreach (var servant in component.Servants)
@@ -160,9 +108,3 @@ public abstract class SharedRatKingSystem : EntitySystem
}
}
-
-[Serializable, NetSerializable]
-public sealed partial class RatKingRummageDoAfterEvent : SimpleDoAfterEvent
-{
-
-}
diff --git a/Content.Shared/RatKing/Systems/RummagerSystem.cs b/Content.Shared/RatKing/Systems/RummagerSystem.cs
new file mode 100644
index 0000000000..d9e9a87694
--- /dev/null
+++ b/Content.Shared/RatKing/Systems/RummagerSystem.cs
@@ -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!;
+
+ ///
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent>(OnGetVerb);
+ SubscribeLocalEvent(OnDoAfterComplete);
+ }
+
+ private void OnGetVerb(Entity ent, ref GetVerbsEvent args)
+ {
+ if (!HasComp(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 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);
+ }
+ }
+}
+
+///
+/// DoAfter event for rummaging through a container with RummageableComponent.
+///
+[Serializable, NetSerializable]
+public sealed partial class RummageDoAfterEvent : SimpleDoAfterEvent;
diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml
index 711a638ec8..d48ac0dd78 100644
--- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml
+++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml
@@ -102,6 +102,7 @@
- CannotSuicide
- FootstepSound
- type: NoSlip
+ - type: Rummager
- type: RatKing
hungerPerArmyUse: 25
hungerPerDomainUse: 50
@@ -306,12 +307,16 @@
sprite: Mobs/Effects/onfire.rsi
normalState: Mouse_burning
-- type: weightedRandomEntity
+- type: entityTable
id: RatKingLoot
- weights:
- RandomSpawner100: 66 #garbage
- FoodCheese: 28 #food
- IngotGold1: 5 #loot
+ table: !type:GroupSelector
+ children:
+ - id: RandomSpawner100
+ weight: 66
+ - id: FoodCheese
+ weight: 28
+ - id: IngotGold1
+ weight: 5
- type: entity
parent: BaseAction
diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml
index 3af5a9d291..90c450e1c7 100644
--- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml
+++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml
@@ -80,7 +80,9 @@
interfaces:
enum.DisposalUnitUiKey.Key:
type: DisposalUnitBoundUserInterface
- - type: RatKingRummageable
+ - type: Rummageable
+ table: !type:NestedSelector
+ tableId: RatKingLoot
- type: SolutionContainerManager
solutions:
drainBuffer:
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
index dda7d51b69..6a9f0b30bc 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
@@ -97,7 +97,9 @@
interfaces:
enum.DisposalUnitUiKey.Key:
type: DisposalUnitBoundUserInterface
- - type: RatKingRummageable
+ - type: Rummageable
+ table: !type:NestedSelector
+ tableId: RatKingLoot
- type: RequireProjectileTarget
- type: entity