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