Cherrypicks 2 (#371)
* Handheld teleporter portals now must start on the same grid. (#28423) Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com> * hand teleport portals now may start in the same grid. (#28556) * Implement vital chef's hat functionality (#25950) * Implement crucial chef's hat functionality * Unified stopping code and added events. * Added documentation to events * Rerun tests * Made review changes, and fixed potential desync bug. * Update whitelist * Make Hamlet a valid chef's hat pilot (#29191) * Dropping Corpses Devoured by Space Dragons on Gib/Butcher. (#28709) * Update DevourSystem.cs * Update DevourSystem.cs * Update Content.Server/Devour/DevourSystem.cs --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Treatment of blood loss in the Rat King (#26887) Bloodloss-RatKing * - fix: Fix error. --------- Co-authored-by: Moony <moony@hellomouse.net> Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com> Co-authored-by: icekot8 <93311212+icekot8@users.noreply.github.com> Co-authored-by: Tayrtahn <tayrtahn@gmail.com> Co-authored-by: Lyndomen <49795619+Lyndomen@users.noreply.github.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: IlyaElDunaev <154531074+IlyaElDunaev@users.noreply.github.com>
This commit is contained in:
19
Content.Client/Clothing/Systems/PilotedByClothingSystem.cs
Normal file
19
Content.Client/Clothing/Systems/PilotedByClothingSystem.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Shared.Clothing.Components;
|
||||||
|
using Robust.Client.Physics;
|
||||||
|
|
||||||
|
namespace Content.Client.Clothing.Systems;
|
||||||
|
|
||||||
|
public sealed partial class PilotedByClothingSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<PilotedByClothingComponent, UpdateIsPredictedEvent>(OnUpdatePredicted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpdatePredicted(Entity<PilotedByClothingComponent> entity, ref UpdateIsPredictedEvent args)
|
||||||
|
{
|
||||||
|
args.BlockPrediction = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Body.Systems;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Devour;
|
using Content.Shared.Devour;
|
||||||
@@ -15,6 +16,7 @@ public sealed class DevourSystem : SharedDevourSystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<DevourerComponent, DevourDoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<DevourerComponent, DevourDoAfterEvent>(OnDoAfter);
|
||||||
|
SubscribeLocalEvent<DevourerComponent, BeingGibbedEvent>(OnGibContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDoAfter(EntityUid uid, DevourerComponent component, DevourDoAfterEvent args)
|
private void OnDoAfter(EntityUid uid, DevourerComponent component, DevourDoAfterEvent args)
|
||||||
@@ -45,5 +47,15 @@ public sealed class DevourSystem : SharedDevourSystem
|
|||||||
|
|
||||||
_audioSystem.PlayPvs(component.SoundDevour, uid);
|
_audioSystem.PlayPvs(component.SoundDevour, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnGibContents(EntityUid uid, DevourerComponent component, ref BeingGibbedEvent args)
|
||||||
|
{
|
||||||
|
if (!component.ShouldStoreDevoured)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// For some reason we have two different systems that should handle gibbing,
|
||||||
|
// and for some another reason GibbingSystem, which should empty all containers, doesn't get involved in this process
|
||||||
|
ContainerSystem.EmptyContainer(component.Stomach);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.Popups;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Teleportation.Components;
|
using Content.Shared.Teleportation.Components;
|
||||||
using Content.Shared.Teleportation.Systems;
|
using Content.Shared.Teleportation.Systems;
|
||||||
using Robust.Server.Audio;
|
using Robust.Server.Audio;
|
||||||
@@ -18,6 +20,7 @@ public sealed class HandTeleporterSystem : EntitySystem
|
|||||||
[Dependency] private readonly LinkedEntitySystem _link = default!;
|
[Dependency] private readonly LinkedEntitySystem _link = default!;
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doafter = default!;
|
[Dependency] private readonly SharedDoAfterSystem _doafter = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -92,6 +95,16 @@ public sealed class HandTeleporterSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
else if (Deleted(component.SecondPortal))
|
else if (Deleted(component.SecondPortal))
|
||||||
{
|
{
|
||||||
|
if (xform.ParentUid != xform.GridUid) // Still, don't portal.
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!component.AllowPortalsOnDifferentGrids && xform.ParentUid != Transform(component.FirstPortal!.Value).ParentUid)
|
||||||
|
{
|
||||||
|
// Whoops. Fizzle time. Crime time too because yippee I'm not refactoring this logic right now (I started to, I'm not going to.)
|
||||||
|
FizzlePortals(uid, component, user, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var timeout = EnsureComp<PortalTimeoutComponent>(user);
|
var timeout = EnsureComp<PortalTimeoutComponent>(user);
|
||||||
timeout.EnteredPortal = null;
|
timeout.EnteredPortal = null;
|
||||||
component.SecondPortal = Spawn(component.SecondPortalPrototype, Transform(user).Coordinates);
|
component.SecondPortal = Spawn(component.SecondPortalPrototype, Transform(user).Coordinates);
|
||||||
@@ -101,22 +114,32 @@ public sealed class HandTeleporterSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Logging
|
FizzlePortals(uid, component, user, false);
|
||||||
var portalStrings = "";
|
|
||||||
portalStrings += ToPrettyString(component.FirstPortal!.Value);
|
|
||||||
if (portalStrings != "")
|
|
||||||
portalStrings += " and ";
|
|
||||||
portalStrings += ToPrettyString(component.SecondPortal!.Value);
|
|
||||||
if (portalStrings != "")
|
|
||||||
_adminLogger.Add(LogType.EntityDelete, LogImpact.Low, $"{ToPrettyString(user):player} closed {portalStrings} with {ToPrettyString(uid)}");
|
|
||||||
|
|
||||||
// Clear both portals
|
|
||||||
QueueDel(component.FirstPortal!.Value);
|
|
||||||
QueueDel(component.SecondPortal!.Value);
|
|
||||||
|
|
||||||
component.FirstPortal = null;
|
|
||||||
component.SecondPortal = null;
|
|
||||||
_audio.PlayPvs(component.ClearPortalsSound, uid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FizzlePortals(EntityUid uid, HandTeleporterComponent component, EntityUid user, bool instability)
|
||||||
|
{
|
||||||
|
// Logging
|
||||||
|
var portalStrings = "";
|
||||||
|
portalStrings += ToPrettyString(component.FirstPortal);
|
||||||
|
if (portalStrings != "")
|
||||||
|
portalStrings += " and ";
|
||||||
|
portalStrings += ToPrettyString(component.SecondPortal);
|
||||||
|
if (portalStrings != "")
|
||||||
|
_adminLogger.Add(LogType.EntityDelete, LogImpact.Low, $"{ToPrettyString(user):player} closed {portalStrings} with {ToPrettyString(uid)}");
|
||||||
|
|
||||||
|
// Clear both portals
|
||||||
|
if (!Deleted(component.FirstPortal))
|
||||||
|
QueueDel(component.FirstPortal.Value);
|
||||||
|
if (!Deleted(component.SecondPortal))
|
||||||
|
QueueDel(component.SecondPortal.Value);
|
||||||
|
|
||||||
|
component.FirstPortal = null;
|
||||||
|
component.SecondPortal = null;
|
||||||
|
_audio.PlayPvs(component.ClearPortalsSound, uid);
|
||||||
|
|
||||||
|
if (instability)
|
||||||
|
_popup.PopupEntity(Loc.GetString("handheld-teleporter-instability-fizzle"), uid, user, PopupType.MediumCaution);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Clothing.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables client-side physics prediction for this entity.
|
||||||
|
/// Without this, movement with <see cref="PilotedClothingSystem"/> is very rubberbandy.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class PilotedByClothingComponent : Component
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Clothing.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows an entity stored in this clothing item to pass inputs to the entity wearing it.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class PilotedClothingComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whitelist for entities that are allowed to act as pilots when inside this entity.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntityWhitelist? PilotWhitelist;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should movement input be relayed from the pilot to the target?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool RelayMovement = true;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reference to the entity contained in the clothing and acting as pilot.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? Pilot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reference to the entity wearing this clothing who will be controlled by the pilot.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? Wearer;
|
||||||
|
|
||||||
|
public bool IsActive => Pilot != null && Wearer != null;
|
||||||
|
}
|
||||||
168
Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs
Normal file
168
Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
using Content.Shared.Clothing.Components;
|
||||||
|
using Content.Shared.Inventory.Events;
|
||||||
|
using Content.Shared.Movement.Components;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
|
using Content.Shared.Storage;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Shared.Clothing.EntitySystems;
|
||||||
|
|
||||||
|
public sealed partial class PilotedClothingSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly SharedMoverController _moverController = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<PilotedClothingComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
|
||||||
|
SubscribeLocalEvent<PilotedClothingComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||||
|
SubscribeLocalEvent<PilotedClothingComponent, GotEquippedEvent>(OnEquipped);
|
||||||
|
SubscribeLocalEvent<PilotedClothingComponent, GotUnequippedEvent>(OnUnequipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntInserted(Entity<PilotedClothingComponent> entity, ref EntInsertedIntoContainerMessage args)
|
||||||
|
{
|
||||||
|
// Make sure the entity was actually inserted into storage and not a different container.
|
||||||
|
if (!TryComp(entity, out StorageComponent? storage) || args.Container != storage.Container)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check potential pilot against whitelist, if one exists.
|
||||||
|
if (entity.Comp.PilotWhitelist?.IsValid(args.Entity, EntityManager) is false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.Pilot = args.Entity;
|
||||||
|
Dirty(entity);
|
||||||
|
|
||||||
|
// Attempt to setup control link, if Pilot and Wearer are both present.
|
||||||
|
StartPiloting(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntRemoved(Entity<PilotedClothingComponent> entity, ref EntRemovedFromContainerMessage args)
|
||||||
|
{
|
||||||
|
// Make sure the removed entity is actually the pilot.
|
||||||
|
if (args.Entity != entity.Comp.Pilot)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StopPiloting(entity);
|
||||||
|
entity.Comp.Pilot = null;
|
||||||
|
Dirty(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEquipped(Entity<PilotedClothingComponent> entity, ref GotEquippedEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp(entity, out ClothingComponent? clothing))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Make sure the clothing item was equipped to the right slot, and not just held in a hand.
|
||||||
|
var isCorrectSlot = (clothing.Slots & args.SlotFlags) != Inventory.SlotFlags.NONE;
|
||||||
|
if (!isCorrectSlot)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.Wearer = args.Equipee;
|
||||||
|
Dirty(entity);
|
||||||
|
|
||||||
|
// Attempt to setup control link, if Pilot and Wearer are both present.
|
||||||
|
StartPiloting(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnequipped(Entity<PilotedClothingComponent> entity, ref GotUnequippedEvent args)
|
||||||
|
{
|
||||||
|
StopPiloting(entity);
|
||||||
|
|
||||||
|
entity.Comp.Wearer = null;
|
||||||
|
Dirty(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to establish movement/interaction relay connection(s) from Pilot to Wearer.
|
||||||
|
/// If either is missing, fails and returns false.
|
||||||
|
/// </summary>
|
||||||
|
private bool StartPiloting(Entity<PilotedClothingComponent> entity)
|
||||||
|
{
|
||||||
|
// Make sure we have both a Pilot and a Wearer
|
||||||
|
if (entity.Comp.Pilot == null || entity.Comp.Wearer == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_timing.IsFirstTimePredicted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var pilotEnt = entity.Comp.Pilot.Value;
|
||||||
|
var wearerEnt = entity.Comp.Wearer.Value;
|
||||||
|
|
||||||
|
// Add component to block prediction of wearer
|
||||||
|
EnsureComp<PilotedByClothingComponent>(wearerEnt);
|
||||||
|
|
||||||
|
if (entity.Comp.RelayMovement)
|
||||||
|
{
|
||||||
|
// Establish movement input relay.
|
||||||
|
_moverController.SetRelay(pilotEnt, wearerEnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pilotEv = new StartedPilotingClothingEvent(entity, wearerEnt);
|
||||||
|
RaiseLocalEvent(pilotEnt, ref pilotEv);
|
||||||
|
|
||||||
|
var wearerEv = new StartingBeingPilotedByClothing(entity, pilotEnt);
|
||||||
|
RaiseLocalEvent(wearerEnt, ref wearerEv);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes components from the Pilot and Wearer to stop the control relay.
|
||||||
|
/// Returns false if a connection does not already exist.
|
||||||
|
/// </summary>
|
||||||
|
private bool StopPiloting(Entity<PilotedClothingComponent> entity)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Pilot == null || entity.Comp.Wearer == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Clean up components on the Pilot
|
||||||
|
var pilotEnt = entity.Comp.Pilot.Value;
|
||||||
|
RemCompDeferred<RelayInputMoverComponent>(pilotEnt);
|
||||||
|
|
||||||
|
// Clean up components on the Wearer
|
||||||
|
var wearerEnt = entity.Comp.Wearer.Value;
|
||||||
|
RemCompDeferred<MovementRelayTargetComponent>(wearerEnt);
|
||||||
|
RemCompDeferred<PilotedByClothingComponent>(wearerEnt);
|
||||||
|
|
||||||
|
// Raise an event on the Pilot
|
||||||
|
var pilotEv = new StoppedPilotingClothingEvent(entity, wearerEnt);
|
||||||
|
RaiseLocalEvent(pilotEnt, ref pilotEv);
|
||||||
|
|
||||||
|
// Raise an event on the Wearer
|
||||||
|
var wearerEv = new StoppedBeingPilotedByClothing(entity, pilotEnt);
|
||||||
|
RaiseLocalEvent(wearerEnt, ref wearerEv);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the Pilot when they gain control of the Wearer.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct StartedPilotingClothingEvent(EntityUid Clothing, EntityUid Wearer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the Pilot when they lose control of the Wearer,
|
||||||
|
/// due to the Pilot exiting the clothing or the clothing being unequipped by the Wearer.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct StoppedPilotingClothingEvent(EntityUid Clothing, EntityUid Wearer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the Wearer when the Pilot gains control of them.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct StartingBeingPilotedByClothing(EntityUid Clothing, EntityUid Pilot);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the Wearer when the Pilot loses control of them
|
||||||
|
/// due to the Pilot exiting the clothing or the clothing being unequipped by the Wearer.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct StoppedBeingPilotedByClothing(EntityUid Clothing, EntityUid Pilot);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -20,6 +20,12 @@ public sealed partial class HandTeleporterComponent : Component
|
|||||||
[ViewVariables, DataField("secondPortal")]
|
[ViewVariables, DataField("secondPortal")]
|
||||||
public EntityUid? SecondPortal = null;
|
public EntityUid? SecondPortal = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Portals can't be placed on different grids?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool AllowPortalsOnDifferentGrids;
|
||||||
|
|
||||||
[DataField("firstPortalPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("firstPortalPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string FirstPortalPrototype = "PortalRed";
|
public string FirstPortalPrototype = "PortalRed";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
handheld-teleporter-instability-fizzle = The portal fizzles as you try to place it, destroying both ends!
|
||||||
@@ -250,6 +250,10 @@
|
|||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
storagebase: !type:Container
|
storagebase: !type:Container
|
||||||
|
- type: PilotedClothing
|
||||||
|
pilotWhitelist:
|
||||||
|
tags:
|
||||||
|
- ChefPilot
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- ClothMade
|
- ClothMade
|
||||||
|
|||||||
@@ -1584,6 +1584,7 @@
|
|||||||
tags:
|
tags:
|
||||||
- Trash
|
- Trash
|
||||||
- VimPilot
|
- VimPilot
|
||||||
|
- ChefPilot
|
||||||
- Mouse
|
- Mouse
|
||||||
- Meat
|
- Meat
|
||||||
- type: Respirator
|
- type: Respirator
|
||||||
@@ -3065,6 +3066,7 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- VimPilot
|
- VimPilot
|
||||||
|
- ChefPilot
|
||||||
- Trash
|
- Trash
|
||||||
- Hamster
|
- Hamster
|
||||||
- Meat
|
- Meat
|
||||||
|
|||||||
@@ -594,6 +594,7 @@
|
|||||||
- CannotSuicide
|
- CannotSuicide
|
||||||
- Hamster
|
- Hamster
|
||||||
- VimPilot
|
- VimPilot
|
||||||
|
- ChefPilot
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: Shiva
|
name: Shiva
|
||||||
|
|||||||
@@ -247,6 +247,8 @@
|
|||||||
groups:
|
groups:
|
||||||
Brute: -5
|
Brute: -5
|
||||||
Burn: -5
|
Burn: -5
|
||||||
|
types:
|
||||||
|
Bloodloss: -5
|
||||||
|
|
||||||
|
|
||||||
- type: reagent
|
- type: reagent
|
||||||
|
|||||||
@@ -361,6 +361,10 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
id: Chicken
|
id: Chicken
|
||||||
|
|
||||||
|
# Allowed to control someone wearing a Chef's hat if inside their hat.
|
||||||
|
- type: Tag
|
||||||
|
id: ChefPilot
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: ChemDispensable # container that can go into the chem dispenser
|
id: ChemDispensable # container that can go into the chem dispenser
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user