From f16ff3a2d943845853cee0238216135b704857a5 Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Fri, 22 Sep 2023 16:01:05 -0400
Subject: [PATCH] Rat King Milsim + Buffs (#20190)
* rat king update
* rummaging
* buuuuunnnnncccchhh of shit
* the last of it
* make rat servants not ghost roles
* pissma buff and cooldown
---
Content.Client/RatKing/RatKingSystem.cs | 9 +
.../Preconditions/HasOrdersPrecondition.cs | 13 ++
.../Operators/Combat/Melee/MeleeOperator.cs | 14 ++
Content.Server/NPC/NPCBlackboard.cs | 10 ++
.../Considerations/OrderedTargetCon.cs | 6 +
.../NPC/Systems/NPCUtilitySystem.cs | 10 ++
.../Pointing/EntitySystems/PointingSystem.cs | 5 +
Content.Server/RatKing/RatKingComponent.cs | 50 ------
Content.Server/RatKing/RatKingSystem.cs | 72 ++++++--
Content.Shared/Actions/SharedActionsSystem.cs | 13 ++
Content.Shared/Pointing/PointingEvents.cs | 14 ++
Content.Shared/RatKing/RatKingActions.cs | 9 +
Content.Shared/RatKing/RatKingComponent.cs | 111 ++++++++++++
.../RatKing/RatKingRummageableComponent.cs | 42 +++++
.../RatKing/RatKingServantComponent.cs | 15 ++
Content.Shared/RatKing/SharedRatKingSystem.cs | 163 ++++++++++++++++++
.../en-US/animals/rat-king/rat-king.ftl | 2 +
.../Prototypes/Datasets/rat_king_commands.yml | 26 +++
.../Markers/Spawners/Random/trash.yml | 11 ++
.../Entities/Mobs/NPCs/regalrat.yml | 131 +++++++++++---
.../Objects/Specific/rehydrateable.yml | 1 -
.../Structures/Piping/Disposal/units.yml | 1 +
Resources/Prototypes/GameRules/events.yml | 8 +-
Resources/Prototypes/NPCs/Combat/melee.yml | 55 ++++++
Resources/Prototypes/NPCs/mob.yml | 10 ++
Resources/Prototypes/NPCs/regalrat.yml | 28 +++
Resources/Prototypes/NPCs/utility_queries.yml | 21 +++
Resources/Prototypes/Reagents/gases.yml | 7 +-
.../Actions/actions_rat_king.rsi/attack.png | Bin 0 -> 636 bytes
.../actions_rat_king.rsi/attackOff.png | Bin 0 -> 617 bytes
.../Actions/actions_rat_king.rsi/follow.png | Bin 0 -> 432 bytes
.../actions_rat_king.rsi/followOff.png | Bin 0 -> 437 bytes
.../Actions/actions_rat_king.rsi/loose.png | Bin 0 -> 522 bytes
.../Actions/actions_rat_king.rsi/looseOff.png | Bin 0 -> 529 bytes
.../Actions/actions_rat_king.rsi/meta.json | 41 +++++
.../ratKingArmy.png | Bin
.../ratKingDomain.png | Bin
.../Actions/actions_rat_king.rsi/stay.png | Bin 0 -> 503 bytes
.../Actions/actions_rat_king.rsi/stayOff.png | Bin 0 -> 483 bytes
.../Textures/Interface/Actions/meta.json | 6 -
.../Mobs/Animals/regalrat.rsi/critical.png | Bin 0 -> 942 bytes
.../Mobs/Animals/regalrat.rsi/meta.json | 3 +
42 files changed, 801 insertions(+), 106 deletions(-)
create mode 100644 Content.Client/RatKing/RatKingSystem.cs
create mode 100644 Content.Server/NPC/HTN/Preconditions/HasOrdersPrecondition.cs
create mode 100644 Content.Server/NPC/Queries/Considerations/OrderedTargetCon.cs
delete mode 100644 Content.Server/RatKing/RatKingComponent.cs
create mode 100644 Content.Shared/RatKing/RatKingComponent.cs
create mode 100644 Content.Shared/RatKing/RatKingRummageableComponent.cs
create mode 100644 Content.Shared/RatKing/RatKingServantComponent.cs
create mode 100644 Content.Shared/RatKing/SharedRatKingSystem.cs
create mode 100644 Resources/Prototypes/Datasets/rat_king_commands.yml
create mode 100644 Resources/Prototypes/NPCs/regalrat.yml
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/attack.png
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/attackOff.png
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/follow.png
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/followOff.png
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/loose.png
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/looseOff.png
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/meta.json
rename Resources/Textures/Interface/Actions/{ => actions_rat_king.rsi}/ratKingArmy.png (100%)
rename Resources/Textures/Interface/Actions/{ => actions_rat_king.rsi}/ratKingDomain.png (100%)
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/stay.png
create mode 100644 Resources/Textures/Interface/Actions/actions_rat_king.rsi/stayOff.png
create mode 100644 Resources/Textures/Mobs/Animals/regalrat.rsi/critical.png
diff --git a/Content.Client/RatKing/RatKingSystem.cs b/Content.Client/RatKing/RatKingSystem.cs
new file mode 100644
index 0000000000..25cc9fdf55
--- /dev/null
+++ b/Content.Client/RatKing/RatKingSystem.cs
@@ -0,0 +1,9 @@
+using Content.Shared.RatKing;
+
+namespace Content.Client.RatKing;
+
+///
+public sealed class RatKingSystem : SharedRatKingSystem
+{
+
+}
diff --git a/Content.Server/NPC/HTN/Preconditions/HasOrdersPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/HasOrdersPrecondition.cs
new file mode 100644
index 0000000000..d2976c98a6
--- /dev/null
+++ b/Content.Server/NPC/HTN/Preconditions/HasOrdersPrecondition.cs
@@ -0,0 +1,13 @@
+namespace Content.Server.NPC.HTN.Preconditions;
+
+public sealed partial class HasOrdersPrecondition : HTNPrecondition
+{
+ [Dependency] private readonly IEntityManager _entManager = default!;
+
+ [DataField("orders", required: true)] public Enum Orders = default!;
+
+ public override bool IsMet(NPCBlackboard blackboard)
+ {
+ return Equals(blackboard.GetValueOrDefault(NPCBlackboard.CurrentOrders, _entManager), Orders);
+ }
+}
diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs
index c40b037d98..32be027ec4 100644
--- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs
+++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs
@@ -68,6 +68,20 @@ public sealed partial class MeleeOperator : HTNOperator, IHtnConditionalShutdown
blackboard.Remove(TargetKey);
}
+ public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
+ {
+ base.TaskShutdown(blackboard, status);
+
+ ConditionalShutdown(blackboard);
+ }
+
+ public override void PlanShutdown(NPCBlackboard blackboard)
+ {
+ base.PlanShutdown(blackboard);
+
+ ConditionalShutdown(blackboard);
+ }
+
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
{
base.Update(blackboard, frameTime);
diff --git a/Content.Server/NPC/NPCBlackboard.cs b/Content.Server/NPC/NPCBlackboard.cs
index 294d9d8027..322ff0f85b 100644
--- a/Content.Server/NPC/NPCBlackboard.cs
+++ b/Content.Server/NPC/NPCBlackboard.cs
@@ -320,6 +320,16 @@ public sealed partial class NPCBlackboard : IEnumerable
+ /// A configurable "order" enum that can be given to an NPC from an external source.
+ ///
+ public const string CurrentOrders = "CurrentOrders";
+
+ ///
+ /// A configurable target that's ordered by external sources.
+ ///
+ public const string CurrentOrderedTarget = "CurrentOrderedTarget";
+
public IEnumerator> GetEnumerator()
{
return _blackboard.GetEnumerator();
diff --git a/Content.Server/NPC/Queries/Considerations/OrderedTargetCon.cs b/Content.Server/NPC/Queries/Considerations/OrderedTargetCon.cs
new file mode 100644
index 0000000000..be56d953c3
--- /dev/null
+++ b/Content.Server/NPC/Queries/Considerations/OrderedTargetCon.cs
@@ -0,0 +1,6 @@
+namespace Content.Server.NPC.Queries.Considerations;
+
+public sealed partial class OrderedTargetCon : UtilityConsideration
+{
+
+}
diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs
index d9aec892ce..d764ab2c2e 100644
--- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs
+++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs
@@ -191,6 +191,16 @@ public sealed class NPCUtilitySystem : EntitySystem
return 1f;
}
+ case OrderedTargetCon:
+ {
+ if (!blackboard.TryGetValue(NPCBlackboard.CurrentOrderedTarget, out var orderedTarget, EntityManager))
+ return 0f;
+
+ if (targetUid != orderedTarget)
+ return 0f;
+
+ return 1f;
+ }
case TargetAccessibleCon:
{
if (_container.TryGetContainingContainer(targetUid, out var container))
diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs
index dc610ef409..8756134310 100644
--- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs
+++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs
@@ -204,6 +204,11 @@ namespace Content.Server.Pointing.EntitySystems
viewerPointedAtMessage = Loc.GetString("pointing-system-point-at-you-other", ("otherName", playerName));
+ var ev = new AfterPointedAtEvent(pointed);
+ RaiseLocalEvent(player, ref ev);
+ var gotev = new AfterGotPointedAtEvent(player);
+ RaiseLocalEvent(pointed, ref gotev);
+
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):user} pointed at {ToPrettyString(pointed):target} {Transform(pointed).Coordinates}");
}
else
diff --git a/Content.Server/RatKing/RatKingComponent.cs b/Content.Server/RatKing/RatKingComponent.cs
deleted file mode 100644
index 22e9de4135..0000000000
--- a/Content.Server/RatKing/RatKingComponent.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.RatKing
-{
- [RegisterComponent]
- public sealed partial class RatKingComponent : Component
- {
- [DataField("actionRaiseArmy", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string ActionRaiseArmy = "ActionRatKingRaiseArmy";
-
- ///
- /// The action for the Raise Army ability
- ///
- [DataField("actionRaiseArmyEntity")] public EntityUid? ActionRaiseArmyEntity;
-
- ///
- /// The amount of hunger one use of Raise Army consumes
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("hungerPerArmyUse", required: true)]
- public float HungerPerArmyUse = 25f;
-
- ///
- /// The entity prototype of the mob that Raise Army summons
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("armyMobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string ArmyMobSpawnId = "MobRatServant";
-
- [DataField("actionDomain", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string ActionDomain = "ActionRatKingDomain";
-
- ///
- /// The action for the Domain ability
- ///
- [DataField("actionDomainEntity")]
- public EntityUid? ActionDomainEntity;
-
- ///
- /// The amount of hunger one use of Domain consumes
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("hungerPerDomainUse", required: true)]
- public float HungerPerDomainUse = 50f;
-
- ///
- /// How many moles of Miasma are released after one us of Domain
- ///
- [DataField("molesMiasmaPerDomain")]
- public float MolesMiasmaPerDomain = 100f;
- }
-}
diff --git a/Content.Server/RatKing/RatKingSystem.cs b/Content.Server/RatKing/RatKingSystem.cs
index cd3cbbb691..3cf6d48751 100644
--- a/Content.Server/RatKing/RatKingSystem.cs
+++ b/Content.Server/RatKing/RatKingSystem.cs
@@ -1,19 +1,30 @@
-using Content.Server.Actions;
+using System.Numerics;
using Content.Server.Atmos.EntitySystems;
+using Content.Server.Chat.Systems;
+using Content.Server.NPC;
+using Content.Server.NPC.HTN;
+using Content.Server.NPC.Systems;
using Content.Server.Popups;
using Content.Shared.Atmos;
+using Content.Shared.Dataset;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
+using Content.Shared.Pointing;
using Content.Shared.RatKing;
using Robust.Server.GameObjects;
+using Robust.Shared.Map;
+using Robust.Shared.Random;
namespace Content.Server.RatKing
{
- public sealed class RatKingSystem : EntitySystem
+ ///
+ public sealed class RatKingSystem : SharedRatKingSystem
{
- [Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!;
+ [Dependency] private readonly ChatSystem _chat = default!;
+ [Dependency] private readonly HTNSystem _htn = default!;
[Dependency] private readonly HungerSystem _hunger = default!;
+ [Dependency] private readonly NPCSystem _npc = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly TransformSystem _xform = default!;
@@ -21,16 +32,9 @@ namespace Content.Server.RatKing
{
base.Initialize();
- SubscribeLocalEvent(OnMapInit);
-
SubscribeLocalEvent(OnRaiseArmy);
SubscribeLocalEvent(OnDomain);
- }
-
- private void OnMapInit(EntityUid uid, RatKingComponent component, MapInitEvent args)
- {
- _action.AddAction(uid, ref component.ActionRaiseArmyEntity, component.ActionRaiseArmy);
- _action.AddAction(uid, ref component.ActionDomainEntity, component.ActionDomain);
+ SubscribeLocalEvent(OnPointedAt);
}
///
@@ -52,7 +56,14 @@ namespace Content.Server.RatKing
}
args.Handled = true;
_hunger.ModifyHunger(uid, -component.HungerPerArmyUse, hunger);
- Spawn(component.ArmyMobSpawnId, Transform(uid).Coordinates); //spawn the little mouse boi
+ var servant = Spawn(component.ArmyMobSpawnId, Transform(uid).Coordinates);
+ var comp = EnsureComp(servant);
+ comp.King = uid;
+ Dirty(servant, comp);
+
+ component.Servants.Add(servant);
+ _npc.SetBlackboard(servant, NPCBlackboard.FollowTarget, new EntityCoordinates(uid, Vector2.Zero));
+ UpdateServantNpc(servant, component.CurrentOrder);
}
///
@@ -83,5 +94,42 @@ namespace Content.Server.RatKing
var tileMix = _atmos.GetTileMixture(transform.GridUid, transform.MapUid, indices, true);
tileMix?.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain);
}
+
+ private void OnPointedAt(EntityUid uid, RatKingComponent component, ref AfterPointedAtEvent args)
+ {
+ if (component.CurrentOrder != RatKingOrderType.CheeseEm)
+ return;
+
+ foreach (var servant in component.Servants)
+ {
+ _npc.SetBlackboard(servant, NPCBlackboard.CurrentOrderedTarget, args.Pointed);
+ }
+ }
+
+ public override void UpdateServantNpc(EntityUid uid, RatKingOrderType orderType)
+ {
+ base.UpdateServantNpc(uid, orderType);
+
+ if (!TryComp(uid, out var htn))
+ return;
+
+ if (htn.Plan != null)
+ _htn.ShutdownPlan(htn);
+
+ _npc.SetBlackboard(uid, NPCBlackboard.CurrentOrders, orderType);
+ _htn.Replan(htn);
+ }
+
+ public override void DoCommandCallout(EntityUid uid, RatKingComponent component)
+ {
+ base.DoCommandCallout(uid, component);
+
+ if (!component.OrderCallouts.TryGetValue(component.CurrentOrder, out var datasetId) ||
+ !PrototypeManager.TryIndex(datasetId, out var datasetPrototype))
+ return;
+
+ var msg = Random.Pick(datasetPrototype.Values);
+ _chat.TrySendInGameICMessage(uid, msg, InGameICChatType.Speak, true);
+ }
}
}
diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs
index 3d476dd48c..dc0afc8af7 100644
--- a/Content.Shared/Actions/SharedActionsSystem.cs
+++ b/Content.Shared/Actions/SharedActionsSystem.cs
@@ -204,6 +204,19 @@ public abstract class SharedActionsSystem : EntitySystem
Dirty(actionId.Value, action);
}
+ public void StartUseDelay(EntityUid? actionId)
+ {
+ if (actionId == null)
+ return;
+
+ var action = GetActionData(actionId);
+ if (action == null || action.UseDelay == null)
+ return;
+
+ action.Cooldown = (GameTiming.CurTime, GameTiming.CurTime + action.UseDelay.Value);
+ Dirty(actionId.Value, action);
+ }
+
#region ComponentStateManagement
public virtual void Dirty(EntityUid? actionId)
{
diff --git a/Content.Shared/Pointing/PointingEvents.cs b/Content.Shared/Pointing/PointingEvents.cs
index 90c31bcfec..878bd35c7e 100644
--- a/Content.Shared/Pointing/PointingEvents.cs
+++ b/Content.Shared/Pointing/PointingEvents.cs
@@ -17,3 +17,17 @@ public sealed class PointingAttemptEvent : EntityEventArgs
Target = target;
}
}
+
+///
+/// Raised on the entity who is pointing after they point at something.
+///
+///
+[ByRefEvent]
+public readonly record struct AfterPointedAtEvent(EntityUid Pointed);
+
+///
+/// Raised on an entity after they are pointed at by another entity.
+///
+///
+[ByRefEvent]
+public readonly record struct AfterGotPointedAtEvent(EntityUid Pointer);
diff --git a/Content.Shared/RatKing/RatKingActions.cs b/Content.Shared/RatKing/RatKingActions.cs
index 551b27a7a4..e2031e972b 100644
--- a/Content.Shared/RatKing/RatKingActions.cs
+++ b/Content.Shared/RatKing/RatKingActions.cs
@@ -11,3 +11,12 @@ public sealed partial class RatKingDomainActionEvent : InstantActionEvent
{
}
+
+public sealed partial class RatKingOrderActionEvent : InstantActionEvent
+{
+ ///
+ /// The type of order being given
+ ///
+ [DataField("type")]
+ public RatKingOrderType Type;
+}
diff --git a/Content.Shared/RatKing/RatKingComponent.cs b/Content.Shared/RatKing/RatKingComponent.cs
new file mode 100644
index 0000000000..43c2858de8
--- /dev/null
+++ b/Content.Shared/RatKing/RatKingComponent.cs
@@ -0,0 +1,111 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared.RatKing;
+
+[RegisterComponent, NetworkedComponent, Access(typeof(SharedRatKingSystem))]
+[AutoGenerateComponentState]
+public sealed partial class RatKingComponent : Component
+{
+ [DataField("actionRaiseArmy", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionRaiseArmy = "ActionRatKingRaiseArmy";
+
+ ///
+ /// The action for the Raise Army ability
+ ///
+ [DataField("actionRaiseArmyEntity")]
+ public EntityUid? ActionRaiseArmyEntity;
+
+ ///
+ /// The amount of hunger one use of Raise Army consumes
+ ///
+ [ViewVariables(VVAccess.ReadWrite), DataField("hungerPerArmyUse", required: true)]
+ public float HungerPerArmyUse = 25f;
+
+ ///
+ /// The entity prototype of the mob that Raise Army summons
+ ///
+ [ViewVariables(VVAccess.ReadWrite), DataField("armyMobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ArmyMobSpawnId = "MobRatServant";
+
+ [DataField("actionDomain", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionDomain = "ActionRatKingDomain";
+
+ ///
+ /// The action for the Domain ability
+ ///
+ [DataField("actionDomainEntity")]
+ public EntityUid? ActionDomainEntity;
+
+ ///
+ /// The amount of hunger one use of Domain consumes
+ ///
+ [DataField("hungerPerDomainUse", required: true), ViewVariables(VVAccess.ReadWrite)]
+ public float HungerPerDomainUse = 50f;
+
+ ///
+ /// How many moles of Miasma are released after one us of Domain
+ ///
+ [DataField("molesMiasmaPerDomain"), ViewVariables(VVAccess.ReadWrite)]
+ public float MolesMiasmaPerDomain = 100f;
+
+ ///
+ /// The current order that the Rat King assigned.
+ ///
+ [DataField("currentOrders"), ViewVariables(VVAccess.ReadWrite)]
+ [AutoNetworkedField]
+ public RatKingOrderType CurrentOrder = RatKingOrderType.Loose;
+
+ ///
+ /// The servants that the rat king is currently controlling
+ ///
+ [DataField("servants")]
+ public HashSet Servants = new();
+
+ [DataField("actionOrderStay", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionOrderStay = "ActionRatKingOrderStay";
+
+ [DataField("actionOrderStayEntity")]
+ public EntityUid? ActionOrderStayEntity;
+
+ [DataField("actionOrderFollow", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionOrderFollow = "ActionRatKingOrderFollow";
+
+ [DataField("actionOrderFollowEntity")]
+ public EntityUid? ActionOrderFollowEntity;
+
+ [DataField("actionOrderCheeseEm", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionOrderCheeseEm = "ActionRatKingOrderCheeseEm";
+
+ [DataField("actionOrderCheeseEmEntity")]
+ public EntityUid? ActionOrderCheeseEmEntity;
+
+ [DataField("actionOrderLoose", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionOrderLoose = "ActionRatKingOrderLoose";
+
+ [DataField("actionOrderLooseEntity")]
+ public EntityUid? ActionOrderLooseEntity;
+
+ ///
+ /// A dictionary with an order type to the corresponding callout dataset.
+ ///
+ [DataField("orderCallouts")]
+ public Dictionary OrderCallouts = new()
+ {
+ { RatKingOrderType.Stay, "RatKingCommandStay" },
+ { RatKingOrderType.Follow, "RatKingCommandFollow" },
+ { RatKingOrderType.CheeseEm, "RatKingCommandCheeseEm" },
+ { RatKingOrderType.Loose, "RatKingCommandLoose" }
+ };
+}
+
+[Serializable, NetSerializable]
+public enum RatKingOrderType : byte
+{
+ Stay,
+ Follow,
+ CheeseEm,
+ Loose
+}
diff --git a/Content.Shared/RatKing/RatKingRummageableComponent.cs b/Content.Shared/RatKing/RatKingRummageableComponent.cs
new file mode 100644
index 0000000000..f3a389ef76
--- /dev/null
+++ b/Content.Shared/RatKing/RatKingRummageableComponent.cs
@@ -0,0 +1,42 @@
+using Content.Shared.Random;
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared.RatKing;
+
+///
+/// This is used for entities that can be
+/// rummaged through by the rat king to get loot.
+///
+[RegisterComponent, NetworkedComponent, Access(typeof(SharedRatKingSystem))]
+[AutoGenerateComponentState]
+public sealed partial class RatKingRummageableComponent : Component
+{
+ ///
+ /// Whether or not this entity has been rummaged through already.
+ ///
+ [DataField("looted"), ViewVariables(VVAccess.ReadWrite)]
+ [AutoNetworkedField]
+ public bool Looted;
+
+ ///
+ /// How long it takes to rummage through a rummageable container.
+ ///
+ [DataField("rummageDuration"), ViewVariables(VVAccess.ReadWrite)]
+ [AutoNetworkedField]
+ public float RummageDuration = 3f;
+
+ ///
+ /// A weighted random entity prototype containing the different loot that rummaging can provide.
+ ///
+ [DataField("rummageLoot", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ [AutoNetworkedField]
+ public string RummageLoot = "RatKingLoot";
+
+ ///
+ /// Sound played on rummage completion.
+ ///
+ [DataField("sound")]
+ public SoundSpecifier? Sound = new SoundCollectionSpecifier("storageRustle");
+}
diff --git a/Content.Shared/RatKing/RatKingServantComponent.cs b/Content.Shared/RatKing/RatKingServantComponent.cs
new file mode 100644
index 0000000000..f6e8d0110e
--- /dev/null
+++ b/Content.Shared/RatKing/RatKingServantComponent.cs
@@ -0,0 +1,15 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.RatKing;
+
+[RegisterComponent, NetworkedComponent, Access(typeof(SharedRatKingSystem))]
+[AutoGenerateComponentState]
+public sealed partial class RatKingServantComponent : Component
+{
+ ///
+ /// The king this rat belongs to.
+ ///
+ [DataField("king")]
+ [AutoNetworkedField]
+ public EntityUid? King;
+}
diff --git a/Content.Shared/RatKing/SharedRatKingSystem.cs b/Content.Shared/RatKing/SharedRatKingSystem.cs
new file mode 100644
index 0000000000..2d39815387
--- /dev/null
+++ b/Content.Shared/RatKing/SharedRatKingSystem.cs
@@ -0,0 +1,163 @@
+using Content.Shared.Actions;
+using Content.Shared.DoAfter;
+using Content.Shared.Random;
+using Content.Shared.Random.Helpers;
+using Content.Shared.Verbs;
+using Robust.Shared.Network;
+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()
+ {
+ SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnShutdown);
+ SubscribeLocalEvent(OnOrderAction);
+
+ SubscribeLocalEvent(OnServantShutdown);
+
+ SubscribeLocalEvent>(OnGetVerb);
+ SubscribeLocalEvent(OnDoAfterComplete);
+ }
+
+ private void OnStartup(EntityUid uid, RatKingComponent component, ComponentStartup args)
+ {
+ if (!TryComp(uid, out ActionsComponent? comp))
+ return;
+
+ _action.AddAction(uid, ref component.ActionRaiseArmyEntity, component.ActionRaiseArmy, holderComp: comp);
+ _action.AddAction(uid, ref component.ActionDomainEntity, component.ActionDomain, holderComp: comp);
+ _action.AddAction(uid, ref component.ActionOrderStayEntity, component.ActionOrderStay, holderComp: comp);
+ _action.AddAction(uid, ref component.ActionOrderFollowEntity, component.ActionOrderFollow, holderComp: comp);
+ _action.AddAction(uid, ref component.ActionOrderCheeseEmEntity, component.ActionOrderCheeseEm, holderComp: comp);
+ _action.AddAction(uid, ref component.ActionOrderLooseEntity, component.ActionOrderLoose, holderComp: comp);
+
+ UpdateActions(uid, component);
+ }
+
+ private void OnShutdown(EntityUid uid, RatKingComponent component, ComponentShutdown args)
+ {
+ foreach (var servant in component.Servants)
+ {
+ if (TryComp(servant, out RatKingServantComponent? servantComp))
+ servantComp.King = null;
+ }
+
+ if (!TryComp(uid, out ActionsComponent? comp))
+ return;
+
+ _action.RemoveAction(uid, component.ActionRaiseArmyEntity, comp);
+ _action.RemoveAction(uid, component.ActionDomainEntity, comp);
+ _action.RemoveAction(uid, component.ActionOrderStayEntity, comp);
+ _action.RemoveAction(uid, component.ActionOrderFollowEntity, comp);
+ _action.RemoveAction(uid, component.ActionOrderCheeseEmEntity, comp);
+ _action.RemoveAction(uid, component.ActionOrderLooseEntity, comp);
+ }
+
+ private void OnOrderAction(EntityUid uid, RatKingComponent component, RatKingOrderActionEvent args)
+ {
+ if (component.CurrentOrder == args.Type)
+ return;
+ args.Handled = true;
+
+ component.CurrentOrder = args.Type;
+ Dirty(uid, component);
+
+ DoCommandCallout(uid, component);
+ UpdateActions(uid, component);
+ UpdateAllServants(uid, component);
+ }
+
+ private void OnServantShutdown(EntityUid uid, RatKingServantComponent component, ComponentShutdown args)
+ {
+ if (TryComp(component.King, out RatKingComponent? ratKingComponent))
+ ratKingComponent.Servants.Remove(uid);
+ }
+
+ private void UpdateActions(EntityUid uid, RatKingComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
+
+ _action.SetToggled(component.ActionOrderStayEntity, component.CurrentOrder == RatKingOrderType.Stay);
+ _action.SetToggled(component.ActionOrderFollowEntity, component.CurrentOrder == RatKingOrderType.Follow);
+ _action.SetToggled(component.ActionOrderCheeseEmEntity, component.CurrentOrder == RatKingOrderType.CheeseEm);
+ _action.SetToggled(component.ActionOrderLooseEntity, component.CurrentOrder == RatKingOrderType.Loose);
+ _action.StartUseDelay(component.ActionOrderStayEntity);
+ _action.StartUseDelay(component.ActionOrderFollowEntity);
+ _action.StartUseDelay(component.ActionOrderCheeseEmEntity);
+ _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,
+ BreakOnUserMove = true
+ });
+ }
+ });
+ }
+
+ private void OnDoAfterComplete(EntityUid uid, RatKingRummageableComponent component, RatKingRummageDoAfterEvent args)
+ {
+ if (args.Cancelled || component.Looted)
+ return;
+
+ component.Looted = true;
+ Dirty(uid, component);
+ _audio.PlayPvs(component.Sound, uid);
+
+ 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)
+ {
+ UpdateServantNpc(servant, component.CurrentOrder);
+ }
+ }
+
+ public virtual void UpdateServantNpc(EntityUid uid, RatKingOrderType orderType)
+ {
+
+ }
+
+ public virtual void DoCommandCallout(EntityUid uid, RatKingComponent component)
+ {
+
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed partial class RatKingRummageDoAfterEvent : SimpleDoAfterEvent
+{
+
+}
diff --git a/Resources/Locale/en-US/animals/rat-king/rat-king.ftl b/Resources/Locale/en-US/animals/rat-king/rat-king.ftl
index 1c587f4328..f6235a2708 100644
--- a/Resources/Locale/en-US/animals/rat-king/rat-king.ftl
+++ b/Resources/Locale/en-US/animals/rat-king/rat-king.ftl
@@ -1,3 +1,5 @@
rat-king-domain-popup = A cloud of miasma is released into the air!
rat-king-too-hungry = You are too hungry to use this ability!
+
+rat-king-rummage-text = Rummage
diff --git a/Resources/Prototypes/Datasets/rat_king_commands.yml b/Resources/Prototypes/Datasets/rat_king_commands.yml
new file mode 100644
index 0000000000..c53d1e135b
--- /dev/null
+++ b/Resources/Prototypes/Datasets/rat_king_commands.yml
@@ -0,0 +1,26 @@
+- type: dataset
+ id: RatKingCommandStay
+ values:
+ - "Sit!"
+ - "Stay!"
+ - "Stop!"
+
+- type: dataset
+ id: RatKingCommandFollow
+ values:
+ - "Heel!"
+ - "Follow!"
+
+- type: dataset
+ id: RatKingCommandCheeseEm
+ values:
+ - "Attack!"
+ - "Sic!"
+ - "Kill!"
+ - "Cheese 'Em!"
+
+- type: dataset
+ id: RatKingCommandLoose
+ values:
+ - "Free!"
+ - "Loose!"
diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/trash.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/trash.yml
index 60a7098328..4806755a11 100644
--- a/Resources/Prototypes/Entities/Markers/Spawners/Random/trash.yml
+++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/trash.yml
@@ -2,6 +2,7 @@
name: Trash Spawner
id: RandomSpawner
parent: MarkerBase
+ suffix: 50
components:
- type: Sprite
layers:
@@ -35,3 +36,13 @@
offset: 0.2
placement:
mode: AlignTileAny
+
+- type: entity
+ parent: RandomSpawner
+ id: RandomSpawner100
+ suffix: 100
+ placement:
+ mode: AlignTileAny
+ components:
+ - type: RandomSpawner
+ chance: 1
diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml
index 9472954cbb..a421a42abc 100644
--- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml
+++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml
@@ -21,7 +21,7 @@
factions:
- SimpleHostile
- type: Sprite
- drawdepth: Mobs
+ drawdepth: SmallMobs
sprite: Mobs/Animals/regalrat.rsi
layers:
- map: ["enum.DamageStateVisualLayers.Base"]
@@ -78,6 +78,8 @@
states:
Alive:
Base: regalrat
+ Critical:
+ Base: critical
Dead:
Base: dead
- type: GhostRole
@@ -89,7 +91,6 @@
- type: Tag
tags:
- CannotSuicide
- - DoorBumpOpener
- FootstepSound
- type: NoSlip
- type: RatKing
@@ -118,7 +119,6 @@
suffix: Buff
components:
- type: Sprite
- drawdepth: Mobs
sprite: Mobs/Animals/buffrat.rsi
scale: 1.2, 1.2
layers:
@@ -139,17 +139,6 @@
damage:
types:
Blunt: 66 #oof ouch owie my bones
- - type: Fixtures
- fixtures:
- fix1:
- shape:
- !type:PhysShapeCircle
- radius: 0.35
- density: 400
- mask:
- - MobMask
- layer:
- - MobLayer
- type: SlowOnDamage
speedModifierThresholds:
200: 0.7
@@ -163,6 +152,7 @@
id: MobRatServant
parent: SimpleMobBase
description: He's da mini rat. He don't make da roolz.
+ noSpawn: true #Must be configured to a King or the AI breaks.
components:
- type: CombatMode
- type: MovementSpeedModifier
@@ -172,7 +162,14 @@
- type: MobMover
- type: HTN
rootTask:
- task: SimpleHostileCompound
+ task: RatServantCompound
+ blackboard:
+ IdleRange: !type:Single
+ 3.5
+ FollowCloseRange: !type:Single
+ 2.0
+ FollowRange: !type:Single
+ 3.0
- type: Reactive
groups:
Flammable: [Touch]
@@ -254,16 +251,9 @@
Female: Mouse
Unsexed: Mouse
wilhelmProbability: 0.001
- - type: GhostRole
- makeSentient: true
- name: ghost-role-information-rat-servant-name
- description: ghost-role-information-rat-servant-description
- rules: ghost-role-information-rat-servant-rules
- - type: GhostTakeoverAvailable
- type: Tag
tags:
- CannotSuicide
- - DoorBumpOpener
- FootstepSound
- type: NoSlip
- type: MobPrice
@@ -276,6 +266,13 @@
guides:
- MinorAntagonists
+- type: weightedRandomEntity
+ id: RatKingLoot
+ weights:
+ RandomSpawner100: 66 #garbage
+ FoodCheese: 28 #food
+ IngotGold1: 5 #loot
+
- type: entity
id: ActionRatKingRaiseArmy
name: Raise Army
@@ -283,10 +280,11 @@
noSpawn: true
components:
- type: InstantAction
- icon: Interface/Actions/ratKingArmy.png
- itemIconStyle: NoItem
- event: !type:RatKingRaiseArmyActionEvent
useDelay: 4
+ icon:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: ratKingArmy
+ event: !type:RatKingRaiseArmyActionEvent
- type: entity
id: ActionRatKingDomain
@@ -295,7 +293,84 @@
noSpawn: true
components:
- type: InstantAction
- useDelay: 10
- icon: Interface/Actions/ratKingDomain.png
- itemIconStyle: NoItem
+ useDelay: 6
+ icon:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: ratKingDomain
event: !type:RatKingDomainActionEvent
+
+- type: entity
+ id: ActionRatKingOrderStay
+ name: Stay
+ description: Command your army to stand in place.
+ noSpawn: true
+ components:
+ - type: InstantAction
+ useDelay: 1
+ icon:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: stayOff
+ iconOn:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: stay
+ event:
+ !type:RatKingOrderActionEvent
+ type: Stay
+ priority: 5
+
+- type: entity
+ id: ActionRatKingOrderFollow
+ name: Follow
+ description: Command your army to follow you around.
+ noSpawn: true
+ components:
+ - type: InstantAction
+ useDelay: 1
+ icon:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: followOff
+ iconOn:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: follow
+ event:
+ !type:RatKingOrderActionEvent
+ type: Follow
+ priority: 6
+
+- type: entity
+ id: ActionRatKingOrderCheeseEm
+ name: Cheese 'Em
+ description: Command your army to attack whoever you point at.
+ noSpawn: true
+ components:
+ - type: InstantAction
+ useDelay: 1
+ icon:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: attackOff
+ iconOn:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: attack
+ event:
+ !type:RatKingOrderActionEvent
+ type: CheeseEm
+ priority: 7
+
+- type: entity
+ id: ActionRatKingOrderLoose
+ name: Loose
+ description: Command your army to act at their own will.
+ noSpawn: true
+ components:
+ - type: InstantAction
+ useDelay: 1
+ icon:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: looseOff
+ iconOn:
+ sprite: Interface/Actions/actions_rat_king.rsi
+ state: loose
+ event:
+ !type:RatKingOrderActionEvent
+ type: Loose
+ priority: 8
diff --git a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml
index 6521fdf61c..8a2e4a29ce 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml
@@ -132,7 +132,6 @@
solution: cube
- type: Rehydratable
possibleSpawns:
- - MobRatServant
- MobCarpHolo
- MobXenoRavager
- MobAngryBee
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
index c8792bc3ba..40fb09f7ef 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
@@ -86,6 +86,7 @@
interfaces:
- key: enum.DisposalUnitUiKey.Key
type: DisposalUnitBoundUserInterface
+ - type: RatKingRummageable
- type: entity
id: MailingUnit
diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml
index a8f5d50684..23732b9762 100644
--- a/Resources/Prototypes/GameRules/events.yml
+++ b/Resources/Prototypes/GameRules/events.yml
@@ -176,13 +176,11 @@
- type: VentCrittersRule
entries:
- id: MobMouse
- prob: 0.015
+ prob: 0.02
- id: MobMouse1
- prob: 0.015
+ prob: 0.02
- id: MobMouse2
- prob: 0.015
- - id: MobRatServant
- prob: 0.015
+ prob: 0.02
specialEntries:
- id: SpawnPointGhostRatKing
prob: 0.005
diff --git a/Resources/Prototypes/NPCs/Combat/melee.yml b/Resources/Prototypes/NPCs/Combat/melee.yml
index e35dc82050..282e794ee7 100644
--- a/Resources/Prototypes/NPCs/Combat/melee.yml
+++ b/Resources/Prototypes/NPCs/Combat/melee.yml
@@ -25,6 +25,28 @@
- !type:HTNCompoundTask
task: MeleeAttackTargetCompound
+- type: htnCompound
+ id: RatServantCombatCompound
+ branches:
+ - preconditions:
+ - !type:ActiveHandComponentPrecondition
+ components:
+ - type: MeleeWeapon
+ damage:
+ types:
+ Brute: 0
+ invert: true
+ tasks:
+ - !type:HTNCompoundTask
+ task: PickupMeleeCompound
+
+ - tasks:
+ - !type:HTNPrimitiveTask
+ operator: !type:UtilityOperator
+ proto: OrderedTargets
+ - !type:HTNCompoundTask
+ task: MeleeAttackOrderedTargetCompound
+
- type: htnCompound
id: PickupMeleeCompound
branches:
@@ -79,3 +101,36 @@
id: MeleeService
proto: NearbyMeleeTargets
key: Target
+
+- type: htnCompound
+ id: MeleeAttackOrderedTargetCompound
+ preconditions:
+ - !type:KeyExistsPrecondition
+ key: Target
+ branches:
+ - tasks:
+ - !type:HTNPrimitiveTask
+ operator: !type:MoveToOperator
+ shutdownState: PlanFinished
+ pathfindInPlanning: true
+ removeKeyOnFinish: false
+ targetKey: TargetCoordinates
+ pathfindKey: TargetPathfind
+ rangeKey: MeleeRange
+ - !type:HTNPrimitiveTask
+ operator: !type:JukeOperator
+ jukeType: Away
+ - !type:HTNPrimitiveTask
+ operator: !type:MeleeOperator
+ targetKey: Target
+ preconditions:
+ - !type:KeyExistsPrecondition
+ key: Target
+ - !type:TargetInRangePrecondition
+ targetKey: Target
+ rangeKey: MeleeRange
+ services:
+ - !type:UtilityService
+ id: MeleeService
+ proto: OrderedTargets
+ key: Target
diff --git a/Resources/Prototypes/NPCs/mob.yml b/Resources/Prototypes/NPCs/mob.yml
index 61ab47c5b2..bba5a76ad8 100644
--- a/Resources/Prototypes/NPCs/mob.yml
+++ b/Resources/Prototypes/NPCs/mob.yml
@@ -9,6 +9,16 @@
- !type:HTNCompoundTask
task: IdleCompound
+- type: htnCompound
+ id: RatServantTargetAttackCompound
+ branches:
+ - tasks:
+ - !type:HTNCompoundTask
+ task: RatServantCombatCompound
+ - tasks:
+ - !type:HTNCompoundTask
+ task: IdleCompound
+
- type: htnCompound
id: MouseCompound
branches:
diff --git a/Resources/Prototypes/NPCs/regalrat.yml b/Resources/Prototypes/NPCs/regalrat.yml
new file mode 100644
index 0000000000..8685a51236
--- /dev/null
+++ b/Resources/Prototypes/NPCs/regalrat.yml
@@ -0,0 +1,28 @@
+- type: htnCompound
+ id: RatServantCompound
+ branches:
+ - preconditions:
+ - !type:HasOrdersPrecondition
+ orders: enum.RatKingOrderType.Stay
+ tasks:
+ - !type:HTNCompoundTask
+ task: IdleCompound
+ - preconditions:
+ - !type:HasOrdersPrecondition
+ orders: enum.RatKingOrderType.Follow
+ tasks:
+ - !type:HTNCompoundTask
+ task: FollowCompound
+ - preconditions:
+ - !type:HasOrdersPrecondition
+ orders: enum.RatKingOrderType.CheeseEm
+ tasks:
+ - !type:HTNCompoundTask
+ task: RatServantTargetAttackCompound
+ - preconditions:
+ - !type:HasOrdersPrecondition
+ orders: enum.RatKingOrderType.Loose
+ tasks:
+ - !type:HTNCompoundTask
+ task: SimpleHostileCompound
+
diff --git a/Resources/Prototypes/NPCs/utility_queries.yml b/Resources/Prototypes/NPCs/utility_queries.yml
index b6e037fdd0..d62e1a7e8c 100644
--- a/Resources/Prototypes/NPCs/utility_queries.yml
+++ b/Resources/Prototypes/NPCs/utility_queries.yml
@@ -99,6 +99,27 @@
- !type:TargetInLOSOrCurrentCon
curve: !type:BoolCurve
+- type: utilityQuery
+ id: OrderedTargets
+ query:
+ - !type:NearbyHostilesQuery
+ considerations:
+ - !type:TargetIsAliveCon
+ curve: !type:BoolCurve
+ - !type:TargetDistanceCon
+ curve: !type:PresetCurve
+ preset: TargetDistance
+ - !type:TargetHealthCon
+ curve: !type:PresetCurve
+ preset: TargetHealth
+ - !type:TargetAccessibleCon
+ curve: !type:BoolCurve
+ - !type:TargetInLOSOrCurrentCon
+ curve: !type:BoolCurve
+ # they gotta be what we ordered
+ - !type:OrderedTargetCon
+ curve: !type:BoolCurve
+
- type: utilityQuery
id: NearbyMeleeWeapons
query:
diff --git a/Resources/Prototypes/Reagents/gases.yml b/Resources/Prototypes/Reagents/gases.yml
index f3f298a721..9fa06f632c 100644
--- a/Resources/Prototypes/Reagents/gases.yml
+++ b/Resources/Prototypes/Reagents/gases.yml
@@ -234,10 +234,9 @@
scaleByQuantity: true
ignoreResistances: true
damage:
- types:
- Blunt: -4
- Slash: -3
- Piercing: -3
+ Groups:
+ Brute: -5
+ Burn: -5
- type: reagent
id: NitrousOxide
diff --git a/Resources/Textures/Interface/Actions/actions_rat_king.rsi/attack.png b/Resources/Textures/Interface/Actions/actions_rat_king.rsi/attack.png
new file mode 100644
index 0000000000000000000000000000000000000000..35f663c66ee9512af33b23bf681256c3da238358
GIT binary patch
literal 636
zcmV-?0)zdDP)Px%Hc3Q5R9J=0S1oS>K@fd37)S$x9_qvfL2{y^;*$Og(jS3rY7vzcSc0tUA7~K$
z163p|Dw>Am1UYev3j#ulYz54AcG>%YqqI-$$LzkhGqZEIz#pRo0C$&HU)@^uq{NTQ
zPYmAQN@W1()~a}1eq!SKCn5JDiEgcm0e~|aJQcagb(Uh63g5qm?_U=dQ_dm*04c{B
zSr|?chEruD<@jI$i5?)rFr311n%bNYEeO$4^F@#=1Vom1{AdEgaH>dF&qLh5K4JAd
z)NJB-_B#Tk9Pga4H(w}WgrbR<=>Z!q-d?o6nIlIN5XZCBB%?VYS_g|bxDFf(sRGPX
z^&55c8+C20s25TT5hoEhw|Ln0&yk!Tj?>)hW+V!5oF;^5DWW98q6I0_12zJFJx$_$
z>q4pkUWm95PFl1OB?^e+862mX>x83qpxT{E8(t)}WbIViJ2sgDlE`d?=BE8dUF+M3
z37K_(lbOVP!_hi`7fAq{l08}n+L&oW&^*9h*PAcYpNm?=+&WqZnoXzD2Hefu#P!i3
zyGEee+{P+^yN)k3i;rzH@{Sqr-h83#xQ$i74uR3Og~JSmxt7Rn@dr|JfRow8abk)$
zZ&Tlkq&8>ffF^(^4}JS>o-jeSra)j5KH9f8U+jca6EJaowRoT3^oi?hBFvN0Uba1R
zeT?#oAlR~f?;I!soA>9`GW0l-E(}jlfT-#`Y~*1~w1HZmkL|KMD)Th3#APU*H=7
W1|Qai*4Oa>0000Px%BS}O-R9J<@SHFwmKotIV2`R6O;8uf$?G_?zY~>VT!FK<|ZG{CfVr%4rg{_55
zwFd?c_DTud6*9ALUM7=h-0WA)k2mvu^WK}63GmIh1AynBqkp|Y*eLOK9ph~o-FX1e
z8-&=dW1KwE2)Un0^adf`mJ#mNU{mCr>%Q2`jeh?L{r*#JFiYS^Ud@oNMm
z$tHKg`6AN76zV2osRwNM`2EZ1^BlREfHXZilT6JC(Yad0)pg)ja0=k3nv6%7j7P>;
zT`xEb5hZyHU-16-+xyyPKRCHo~`!qGifC2Y_-qXlnN*!vk~%5C*zUPw-GCp)&WY!
ziRp&ZVHbWT0h~+rbl5ezB^S6Ivh-1%G)NA)v0Ck~S|s
zYBP|{ly`W%=Zi?&Q5&m(906n;WPx$Px$YDq*vR9J=WS3zonKoorjFHm9J1tdbZF3QkcAkejk$UY}=?;%_lyg-9wk!`^u
zbe92hgmj@Z{f=X!CbeDoAjJ9k|NZyhj6s1vh6Ml*&)W+N!buy8V}kQOwrl`kK?re7
zz@2HDXJys%ypOSv!Ks?-y%ybR(_j`b3z!AW0%ifd1u*7{<{+VfV(
zvbVd`^md>qJ`@oEaK64v&5r^YbHVvq5lBJ_6XDRBP62Z7=oHja6Zx1L;~9`d`V@36
z#OdZ5KyyHzT0ODL&apKV4F@QR5GSKzJvOF>qTxXKv}=6yI9;2rujQK7tux>pqEgCzFxe~0BHucYAs0000IHOIb#+);K$!FZWu>l6}u?jqD5O}Wl(N1RfdV!_IcFRlp{{E{K@XKdE^{Jbi
zvEjKc`@#hw#OGUoMbG^
zdU`<5y3|rhimyW{v@{@0XZ_0p7ZaI^$`}*7PrEF_d-IR3dbZa1!>?~Ql~gjr`oj}n
zm*0(OyB2)FYQ{XTePvrR-*11mE@EeKTOK=Gjqm!|kstB}&Hr;wHhH|xpqkMnCMh{I
zXHr^3{I=x1T+&hpo@R-;UbOuG<63?o^S{G9g)-517z@w;mrBx;UZLNWroY@+WcQtf
decC@5Kf3LjsJlvKHZUF-JYD@<);T3K0RXFdvZ(+7
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Interface/Actions/actions_rat_king.rsi/loose.png b/Resources/Textures/Interface/Actions/actions_rat_king.rsi/loose.png
new file mode 100644
index 0000000000000000000000000000000000000000..bfbc45daed736351fa95f3516abb61ff2853b397
GIT binary patch
literal 522
zcmV+l0`>igP)Px$#7RU!R9J=Wmp^X8FcikWAZ}2?Y(uiuUX
z0Pk=2`&qcd$JetJ@kV`)PP>AK$4{q3Nn7HwRw-wkmG}p^2*7o#aNX)z+Zq4>2;sr^
zTM)uaL9769Q4T|WML`MyWGx=Yn-qeaF$F1`iu-;mvrl$}=aOWbyRNYValFy?tH$V@
zFa}7Htx|vsXPu3_FfIW>kfnTQvJeLn!pkkgP5y|uGqM}H(p?3Rw{z3D+5wdqf#`M*
z6g&eeA*!qf{^YZoMzjfk(#(^kWWH66IU0BZbx;3SU
zDpv>q;9s(<%-m$U#(0FZP5lI``trWX8Q?(u07p@z?Slhq7cv2OedV12>Kv~(<>a;G
zNMA;S;giWGZx+MB;`C+IsL!p5bFDIPcqkqorwdC670kk&B`r<-0NRw@ghrZZ&j0`b
M07*qoM6N<$g5nPx$%Sl8*R9J=Wm*0wlKp4f3i}VHt-3?y!0^yR~#umYXj}$$U5Qv7i87sYj7vbGP
zzCi@-MSU_&Gdlg**_%B#ELVwT1HfS9W4q39
zu$d9_&auQ`GQ{
zFf?{A$Xx)c$;)zALr^lhAhxc#=M9x(Vj_I4s`icZY9~;ZyVh~pIc-U3160*sYQP(-
z?u{mCw}2ohQhsHk5f8+e+t`MeJc#&Y#5CGk_Y^?W%60X00x~lKXCMNFQzCi3c?_WKViIjhz6CnhX#wbnqSlFA
z3m_Qx5vIjW4Zu}htaXkhX46SM9;QWoj(BY&fD9;#lk@-Sq@Gx;%`U&9WHplZbKqdJ
zn>S~%7R3pIaUa3BkIu2YS^$6i5P&XZm=<*!q%bX%ul>pv@coVA1i@v(C{6&INo#z+
zsV2@P$^wA-e16KlzO`5S3iPx$u}MThR9J=Wmp_WaKorKmh@L>65vmxxP?siW!#yVQQ{VUAc-??=KbEE3<>aH1pv@~Z=WckS{t`g4xI9qI+p&V}d}u3D?r
zccuNjN}!!exr3Cu!dx5=D(zJ%1A24QDVJ-`8-S=&NHxFb4yD{x?uk+W)43@jx;cbT
zLplL?w{w|kzU4`<0+`*-WtM=b;|<|;4%lFPAW9#@Yr9&AuC-)00ceOa5IrBqgGy(C
zwFF8XE+XO({4nBcv`h%w3F+(sS50kmdi>gcaF$>#<~UBb`42$hzaHauYcbF5_|+z7
z2EuWO)~Ht)YmIt1PPa%WY5sD_?ZPR5(Qg1DLMMPT1L-8ah|DIQ5xq7Xhe#)>bDc8-
zksRjH@W}p>&VYu9YrXdU+=!TOZ$%{HgB6C?}4%!
t-&5RI6F7e;QbO2cSqlK+{4M$~^aEnq$;RDSET#Ye002ovPDHLkV1liS+VTJZ
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Interface/Actions/actions_rat_king.rsi/stayOff.png b/Resources/Textures/Interface/Actions/actions_rat_king.rsi/stayOff.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa739f71d77a819fa191acb684637c8a491b26dc
GIT binary patch
literal 483
zcmV<90UZ8`P)Px$ok>JNR9J=WmqCicFcgM=rFer1vkNY~fD~-EG8JKdVww^YkLC`
zW)_n25+|vR5I6ptrp-(KFL^J40&ms?0DivX-&GVkHEyaJ`$IAD0ALk`XsQ}lk2x`)
z-8fcJi2b3!RD5)0u6j-NVQ%yR_yD{ez_S_T`}4ltbr-h)!kn>%^b|Usj-%$YN`Wxt
z{0z>|QeP0PN6j^A2gKy6DV3?u8o>AG(pJ+pHRSv}x+VqzRC85~EqVx>f;0lqYNt}S
z`IVEP1yHM?g1T)4aDFve733?TWC}E5`cgh0ulW|upV_6Xj9O2mxmQm
z2znXOF+wVY?uInhfGMZCKJoq8dN2aaZXCO9+`p$~i)6EuV$!me;Px&Xh}ptR9J=WR!wLcK^T22iS@9C1&v$7iWun8Q=u4yP$4uIY4FrbXekm%k(Qh!
zf!T6DUe99wbC+O;b7~LW@$i2RqZ9-C1oQheGpU@n>h|
zy?Ng^-#5Vj4$1CCA{NyLP0SU`wsWt75j*mQxf%Am)o#PDs@x|4{W?Tg7NF#coCl@1zL62&=?h(-04r6j7A_sCbOgKXAY7bkE!N1-SR06>=A
z0D$Q!=vNWpFq5A*PKJ@gOm2jx1u#7o8kj#uNM`cvPJxjd6OsNGxvS}Ba#?nx)460W
z06?>O%DxYBgbRV0JRS*iNoTk7PUn(+QZbqqVDG1=pxX&?7i_^oPDQmYPIRLK
zvg}5)dCCbQga{LJkYI;-P$Cx9*D}kN$^Nk*y4@bz-7s=vLW+nfMgUmiUw`)j-cXPe
z)h|AL0ivX5XJ6p+jkEEZ7O*803NVrZz?}!TkxupP|3ePsD^*qly4{{|L0NX==;+9{
zPMF~UtPZ48+gu^?t12r_mBXgg?e>@mLJ0h0K`hQb#LLC!_Gmlv|^79K-Ku2`0`I|cYvRk+Kwjd`v{P~?hbDgEpf3*YMc
zChL-ulat&Uv?|h*VcaNJ2Y%lHSf9@o%Mw!JzE8vRt$vG?1PL^Kq1!N0kZ
zQE@bU9@hj)2MwuyoYCvY8Owa*v818nLSS?v9s%cs2s8|(Hy`;sghNB}=?hEj0bpx?
z7arFHfc|*l;^u3aoe0r=0q9##$rVLGt@h_?_vHR8{Hn?lTiA@K6UNvM7$+3(4F&N!
z{}#1cmCFh9X|wHyRK!39Ht>dmtR@%>PwAi`d0YkEPW-iDSOVYR2LBX(0c`;O@eKr^
QA^-pY07*qoM6N<$g8s3jasU7T
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Mobs/Animals/regalrat.rsi/meta.json b/Resources/Textures/Mobs/Animals/regalrat.rsi/meta.json
index 260803a448..5c80471313 100644
--- a/Resources/Textures/Mobs/Animals/regalrat.rsi/meta.json
+++ b/Resources/Textures/Mobs/Animals/regalrat.rsi/meta.json
@@ -7,6 +7,9 @@
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/tgstation/tgstation/commit/53d1f1477d22a11a99c6c6924977cd431075761b Changed by Alekshhh",
"states": [
+ {
+ "name": "critical"
+ },
{
"name": "dead"
},
--
2.51.2