* Initial: Create Devour componentry, preliminary identity storage and the systems for Devouring * I have genuinely no idea what i'm doing - added the radial menu, it has nothing in it. - trying to get it to populate. the event under the event is broken, i don't know why, but apparently it's not typed right - Added a placeholder transform - oh also fixed up some devour stuff and moved some things around. * Holey moley, Transform, better devour, oh my! - Move DnaComponent into Shared because we need it for the DNA cloning - Make Transform MOSTLY work on the LAST identity devoured. - Fix some issues on devour that involved prediction, canceling and Damage exeucting (Thanks Plykiya for pointing out AttemptFrequency!) * Proper tail stealing and Damage modifier attempt Add check to add a wagging component on the Changeling if the victim's species Prototype had one. attempt to add the Damage mitigation check * MAJOR CLEANUP AND FIXES AUGH 3 DAYS!!! - Nullspaced a clone of a victim - fix audio using server virtualized Pvs (i hate this) - fix the mispredicted doafters - Clean up a wholelotta code - utilize clone systems to clone appearances - Move CloneAppearance from server to shared So we can actually access it * Examine stuff, more cleanup, Jumpsuit ripping - make rotting prevent the action - Add ripping of clothing (guh why is it also server) - add some System stuff for pushing husked corpse inspection - clean up more badcode * Doing things properly, UI sorta kinda works. - Utilize Relayed events for Devour checking - Get a UI that partially works, Says the name of identities, doesn't show their looks - Make use of the New Dynamic BUI assignment - commit the sin of no client prediction cause nullspace entities aren't networked * Got an entity for the Frontend transform Issue with the looks * Stick a camera into a fake mobs forehead - Get the UI to see the net entity in pause space by using a ViewSubscriber to get the Pvs from the initially stored identity entity - Remove all the other parts used to try to get this to work before hand * Raaaaadiallllllls also fix protection coefficents - Change FancyWindow to Radial - Fix Issue where coeffeient checking was the wrong way round * absolutely massive cleanup, no more camera in mobs - cleaned up event variables that are not needed - Removed the use of a Pause space and go back to Nullspace usage - use a PvsOverride rather than ViewSharing - Remove old commented out code and Lots of unused code * Fix "Ui elements" dying on the screen - some minor cleanup - don't start the entities that get cloned * ftl, cleanup, and fixing missing transform details - add replace functionality to TypingIndicatorSystem and BodyEmotesSystem - add placeholder sounds and functions to TransformBodyEmotes - add extra Pvs handling for later use - attributions for the funny straw sound - Sound collections for all of the sounds - various cleanups * Some extra cleanup * Fix some false assumptions about TypingIndicator - Bubbles now transfer on spawned humans rather than used humans - Clean up YET MORE CODE - make it so you can't eat yourself * Oooprs, forgot to add a Husked Corpse Loc * Missing period in the husked corpse loc * bad devour windup placeholder * Husking and WIP Lungs - Husking now will be prevented from Revival fully and will change the appearance of players * Add finalized Sprites for actions and final meta - add devour on and off sprites - add transform action sprite - Add Armblade sprite for future use - Credit obscenelytinyshark for the sprites <3 * Remove ling lungs, Entity<> everything - Remove the ling lungs stuff for now... body system is overly complicated, makes my head hurt - Switch every method to use Entity<> from Uid, Comp format * cleanup, admin logging, WIP Roles * Admin verb, Roundstart, gamerule stuff - add a Admin verb to make Changelingification easy! - Add game rule stuff for admin verb and to tell the hapless goober how to be a changeling... sorta - clean up parts to make VV easy... USE THE VERB!! * Armor Coefficent Check - Remove bespoke changeling armor check and replace it with a generic armor coefficient query. * move to UnrevivableComponent instead of husked - Move UnrevivableComponent to shared - add Analyzable and ReasonMessage to UnrevivableComponent to give granular control of the message and whether or not it shows up in the analyzer - remove the check for HuskedComponent in DefibrillatorSystem * aaaaaaa CopyComp - Some cleanup - make Vocal system shared - make VocalSystem Not make more Actions than it needs - Use some code from ChameleonProjector so we can copy components - partially ungod method the Transform system * Cleanup, Moving more things to CopyComp - TransformBodyEmotes now uses CopyComp (it's a server component so i need to tell the server to deal with it - TypingIndicatorComponent also now uses CopyComp - cleaned up old, now unused "replace" methods in favor of CopyComp - BodyEmotesSystem now has a publically accessable LoadSounds to deal with the same problem Screaming had * WIP * Devour Windup noise, ForensicsSystem cleanup * Revert VocalSystem Changes - Reverted Moving VocalSystem to shared, copy comp acomplishes it - added component.ScreamActionEntity = null; for copy comp * cleanup unneeded comments * revert an accidental line removal * Remove duplicate SharedHumanoidAppearanceSystem * Cleanup Typo's and import Forensics components for Dna * Some more forensics calls * cleanup use CopyComp for now until CopyComps * CR cleanup * Undo some SharedHumanoidAppearanceSystem changes * Confound these spaces * Some Copycomp stuff and fixing some PVS override * use the proper TryCopyComps that are merged * Change TransformMenu with RadialWithSector * All sounds done, Fix lack of typing indicator issue * Updated attributions to include used sound authors * some ftl typos and mind_role text issue * DNA, Screaming, appearance, grammar, wagging - reduced all of the above using ApplyComponentChanges - Issue still remains with bodyEmotes sticking around in the UI * Fix UI stuff, partials, entprotoid, good practices - bunch of partials added - UI now has a predicted message - EntProtoID in the admin verb - RipClothing now uses Entity<ButcherableComponent> - husking is now optional (off by default) for testing/till we have hivemind/when we figure out what were doing with devour - remove TransformGrammarSet * More CR stuff and documentation - Make TargetIsProtected less of a meme, with a prototype set of DamageTypes to check - Documenation everywhere - Move DevourEvents into its own file * Predicted sounds and fix the comp clone list - Made all start and stop sounds shared - Split out the rest of the events and UI stuff into subfiles - Fixed some Clone comp list issues where comments had -'s causing them to be read incorrectly * Damage cap check, Identity Shutdown cleanup, cleanup * Sound stuff (but actually this time) * Missed documentation * Missed Documentation and a EntProtoId * Remove unused dependency * Remove a nullcheck * Some dummy minplayers * CR - Husked now uses a rem/ensure * Update Actions in the Prototype * Fixup mindswap handover - cleanup and handover PVS on mindswap * Fixup Missing meta from accidental "Take-theirs" * Add the Armblade to the roundstart-role * Cleanup, CR (everything but the UI and renames) * missed a spot * missed some more whitespace * Renames * Primary constructor and a space in these trying times * User interface stuff for Slime transformation * popup prediction * Ling devour no longer makes duplicate identities - added a key to identities to the original victim - Add some extra clone settings * add guard statements to OnClones * SentOnlyToOwner additions * fix for sound stoppage error * Move Organ deleter into soon to be atomized husk * clone event inventory * mono sounds * lower sound volume * Fix networked sound warning * Clone comps thing * review * attributions * Fix clobbered changes * I'm gonna weh out - whole bunch of CR changes * fix some very buggy git * okay its fixed * address most review points * fix inventory * we hate entityuids * fix test and more cleanup * move this * fix more stuff * fix validation and rootable * Remove Quickswitch due to some UI quirks * oops left out some better explanation * remove dangling LastConsumed component fields * fix test fail * try this * cleanup cloning subscriptions, add movement speed modifier * fix slime storage * fix cloning setting inheritance * Add session information to transform admin logs * slay the integration test hydra * dwarf size * more volume tweaks * comments * improve comments and unpredict deletion due to errors when shutting down the server * fix displancement cloning --------- Co-authored-by: ScarKy0 <scarky0@onet.eu> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
327 lines
11 KiB
C#
327 lines
11 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using Content.Shared.DisplacementMap;
|
|
using Content.Shared.Inventory.Events;
|
|
using Content.Shared.Storage;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Shared.Inventory;
|
|
|
|
public partial class InventorySystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
[Dependency] private readonly IViewVariablesManager _vvm = default!;
|
|
|
|
private void InitializeSlots()
|
|
{
|
|
SubscribeLocalEvent<InventoryComponent, ComponentInit>(OnInit);
|
|
SubscribeAllEvent<OpenSlotStorageNetworkMessage>(OnOpenSlotStorage);
|
|
|
|
_vvm.GetTypeHandler<InventoryComponent>()
|
|
.AddHandler(HandleViewVariablesSlots, ListViewVariablesSlots);
|
|
|
|
SubscribeLocalEvent<InventoryComponent, AfterAutoHandleStateEvent>(AfterAutoState);
|
|
}
|
|
|
|
private void ShutdownSlots()
|
|
{
|
|
_vvm.GetTypeHandler<InventoryComponent>()
|
|
.RemoveHandler(HandleViewVariablesSlots, ListViewVariablesSlots);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to find an entity in the specified slot with the specified component.
|
|
/// </summary>
|
|
public bool TryGetInventoryEntity<T>(Entity<InventoryComponent?> entity, out Entity<T?> target)
|
|
where T : IComponent, IClothingSlots
|
|
{
|
|
if (TryGetContainerSlotEnumerator(entity.Owner, out var containerSlotEnumerator))
|
|
{
|
|
while (containerSlotEnumerator.NextItem(out var item, out var slot))
|
|
{
|
|
if (!TryComp<T>(item, out var required))
|
|
continue;
|
|
|
|
if ((((IClothingSlots)required).Slots & slot.SlotFlags) == 0x0)
|
|
continue;
|
|
|
|
target = (item, required);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
target = EntityUid.Invalid;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy this component's datafields from one entity to another.
|
|
/// This can't use CopyComp because the template needs to be applied using the API method.
|
|
/// <summary>
|
|
public void CopyComponent(Entity<InventoryComponent?> source, EntityUid target)
|
|
{
|
|
if (!Resolve(source, ref source.Comp))
|
|
return;
|
|
|
|
var targetComp = EnsureComp<InventoryComponent>(target);
|
|
targetComp.SpeciesId = source.Comp.SpeciesId;
|
|
targetComp.Displacements = new Dictionary<string, DisplacementData>(source.Comp.Displacements);
|
|
targetComp.FemaleDisplacements = new Dictionary<string, DisplacementData>(source.Comp.FemaleDisplacements);
|
|
targetComp.MaleDisplacements = new Dictionary<string, DisplacementData>(source.Comp.MaleDisplacements);
|
|
SetTemplateId((target, targetComp), source.Comp.TemplateId);
|
|
Dirty(target, targetComp);
|
|
}
|
|
|
|
private void OnInit(Entity<InventoryComponent> ent, ref ComponentInit args)
|
|
{
|
|
UpdateInventoryTemplate(ent);
|
|
}
|
|
|
|
private void AfterAutoState(Entity<InventoryComponent> ent, ref AfterAutoHandleStateEvent args)
|
|
{
|
|
UpdateInventoryTemplate(ent);
|
|
}
|
|
|
|
protected virtual void UpdateInventoryTemplate(Entity<InventoryComponent> ent)
|
|
{
|
|
if (!_prototypeManager.Resolve(ent.Comp.TemplateId, out var invTemplate))
|
|
return;
|
|
|
|
// Remove any containers that aren't in the new template.
|
|
foreach (var container in ent.Comp.Containers)
|
|
{
|
|
if (invTemplate.Slots.Any(s => s.Name == container.ID))
|
|
continue;
|
|
|
|
// Empty container before deletion so the contents don't get deleted.
|
|
// For cases when we update the template while items are already worn.
|
|
_containerSystem.EmptyContainer(container);
|
|
_containerSystem.ShutdownContainer(container);
|
|
}
|
|
|
|
// Ensure the containers from the template.
|
|
ent.Comp.Slots = invTemplate.Slots;
|
|
ent.Comp.Containers = new ContainerSlot[ent.Comp.Slots.Length];
|
|
for (var i = 0; i < ent.Comp.Containers.Length; i++)
|
|
{
|
|
var slot = ent.Comp.Slots[i];
|
|
var container = _containerSystem.EnsureContainer<ContainerSlot>(ent.Owner, slot.Name);
|
|
container.OccludesLight = false;
|
|
ent.Comp.Containers[i] = container;
|
|
}
|
|
}
|
|
|
|
private void OnOpenSlotStorage(OpenSlotStorageNetworkMessage ev, EntitySessionEventArgs args)
|
|
{
|
|
if (args.SenderSession.AttachedEntity is not { Valid: true } uid)
|
|
return;
|
|
|
|
if (TryGetSlotEntity(uid, ev.Slot, out var entityUid) && TryComp<StorageComponent>(entityUid, out var storageComponent))
|
|
{
|
|
_storageSystem.OpenStorageUI(entityUid.Value, uid, storageComponent, false);
|
|
}
|
|
}
|
|
|
|
public bool TryGetSlotContainer(EntityUid uid, string slot, [NotNullWhen(true)] out ContainerSlot? containerSlot, [NotNullWhen(true)] out SlotDefinition? slotDefinition,
|
|
InventoryComponent? inventory = null, ContainerManagerComponent? containerComp = null)
|
|
{
|
|
containerSlot = null;
|
|
slotDefinition = null;
|
|
if (!Resolve(uid, ref inventory, ref containerComp, false))
|
|
return false;
|
|
|
|
if (!TryGetSlot(uid, slot, out slotDefinition, inventory: inventory))
|
|
return false;
|
|
|
|
if (!_containerSystem.TryGetContainer(uid, slotDefinition.Name, out var container, containerComp))
|
|
{
|
|
if (inventory.LifeStage >= ComponentLifeStage.Initialized)
|
|
Log.Error($"Missing inventory container {slot} on entity {ToPrettyString(uid)}");
|
|
return false;
|
|
}
|
|
|
|
if (container is not ContainerSlot containerSlotChecked)
|
|
return false;
|
|
|
|
containerSlot = containerSlotChecked;
|
|
return true;
|
|
}
|
|
|
|
public bool HasSlot(EntityUid uid, string slot, InventoryComponent? component = null) =>
|
|
TryGetSlot(uid, slot, out _, component);
|
|
|
|
public bool TryGetSlot(EntityUid uid, string slot, [NotNullWhen(true)] out SlotDefinition? slotDefinition, InventoryComponent? inventory = null)
|
|
{
|
|
slotDefinition = null;
|
|
if (!Resolve(uid, ref inventory, false))
|
|
return false;
|
|
|
|
foreach (var slotDef in inventory.Slots)
|
|
{
|
|
if (!slotDef.Name.Equals(slot))
|
|
continue;
|
|
slotDefinition = slotDef;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool TryGetContainerSlotEnumerator(Entity<InventoryComponent?> entity, out InventorySlotEnumerator containerSlotEnumerator, SlotFlags flags = SlotFlags.All)
|
|
{
|
|
if (!Resolve(entity.Owner, ref entity.Comp, false))
|
|
{
|
|
containerSlotEnumerator = default;
|
|
return false;
|
|
}
|
|
|
|
containerSlotEnumerator = new InventorySlotEnumerator(entity.Comp, flags);
|
|
return true;
|
|
}
|
|
|
|
public InventorySlotEnumerator GetSlotEnumerator(Entity<InventoryComponent?> entity, SlotFlags flags = SlotFlags.All)
|
|
{
|
|
if (!Resolve(entity.Owner, ref entity.Comp, false))
|
|
return InventorySlotEnumerator.Empty;
|
|
|
|
return new InventorySlotEnumerator(entity.Comp, flags);
|
|
}
|
|
|
|
public bool TryGetSlots(EntityUid uid, [NotNullWhen(true)] out SlotDefinition[]? slotDefinitions)
|
|
{
|
|
if (!TryComp(uid, out InventoryComponent? inv))
|
|
{
|
|
slotDefinitions = null;
|
|
return false;
|
|
}
|
|
slotDefinitions = inv.Slots;
|
|
return true;
|
|
}
|
|
|
|
private ViewVariablesPath? HandleViewVariablesSlots(EntityUid uid, InventoryComponent comp, string relativePath)
|
|
{
|
|
return TryGetSlotEntity(uid, relativePath, out var entity, comp)
|
|
? ViewVariablesPath.FromObject(entity)
|
|
: null;
|
|
}
|
|
|
|
private IEnumerable<string> ListViewVariablesSlots(EntityUid uid, InventoryComponent comp)
|
|
{
|
|
foreach (var slotDef in comp.Slots)
|
|
{
|
|
yield return slotDef.Name;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change the inventory template ID an entity is using
|
|
/// and drop any item that does not have a slot according to the new template.
|
|
/// This will update the client-side UI accordingly.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// </remarks>
|
|
/// <param name="ent">The entity to update.</param>
|
|
/// <param name="newTemplate">The ID of the new inventory template prototype.</param>
|
|
public void SetTemplateId(Entity<InventoryComponent> ent, ProtoId<InventoryTemplatePrototype> newTemplate)
|
|
{
|
|
if (ent.Comp.TemplateId == newTemplate)
|
|
return;
|
|
|
|
ent.Comp.TemplateId = newTemplate;
|
|
UpdateInventoryTemplate(ent);
|
|
Dirty(ent);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumerator for iterating over an inventory's slot containers. Also has methods that skip empty containers.
|
|
/// It should be safe to add or remove items while enumerating.
|
|
/// </summary>
|
|
public struct InventorySlotEnumerator
|
|
{
|
|
private readonly SlotDefinition[] _slots;
|
|
private readonly ContainerSlot[] _containers;
|
|
private readonly SlotFlags _flags;
|
|
private int _nextIdx = 0;
|
|
public static InventorySlotEnumerator Empty = new(Array.Empty<SlotDefinition>(), Array.Empty<ContainerSlot>());
|
|
|
|
public InventorySlotEnumerator(InventoryComponent inventory, SlotFlags flags = SlotFlags.All)
|
|
: this(inventory.Slots, inventory.Containers, flags)
|
|
{
|
|
}
|
|
|
|
public InventorySlotEnumerator(SlotDefinition[] slots, ContainerSlot[] containers, SlotFlags flags = SlotFlags.All)
|
|
{
|
|
DebugTools.Assert(flags != SlotFlags.NONE);
|
|
DebugTools.AssertEqual(slots.Length, containers.Length);
|
|
_flags = flags;
|
|
_slots = slots;
|
|
_containers = containers;
|
|
}
|
|
|
|
public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container)
|
|
{
|
|
while (_nextIdx < _slots.Length)
|
|
{
|
|
var i = _nextIdx++;
|
|
var slot = _slots[i];
|
|
|
|
if ((slot.SlotFlags & _flags) == 0)
|
|
continue;
|
|
|
|
container = _containers[i];
|
|
return true;
|
|
}
|
|
|
|
container = null;
|
|
return false;
|
|
}
|
|
|
|
public bool NextItem(out EntityUid item)
|
|
{
|
|
while (_nextIdx < _slots.Length)
|
|
{
|
|
var i = _nextIdx++;
|
|
var slot = _slots[i];
|
|
|
|
if ((slot.SlotFlags & _flags) == 0)
|
|
continue;
|
|
|
|
var container = _containers[i];
|
|
if (container.ContainedEntity is { } uid)
|
|
{
|
|
item = uid;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
item = default;
|
|
return false;
|
|
}
|
|
|
|
public bool NextItem(out EntityUid item, [NotNullWhen(true)] out SlotDefinition? slot)
|
|
{
|
|
while (_nextIdx < _slots.Length)
|
|
{
|
|
var i = _nextIdx++;
|
|
slot = _slots[i];
|
|
|
|
if ((slot.SlotFlags & _flags) == 0)
|
|
continue;
|
|
|
|
var container = _containers[i];
|
|
if (container.ContainedEntity is { } uid)
|
|
{
|
|
item = uid;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
item = default;
|
|
slot = null;
|
|
return false;
|
|
}
|
|
}
|
|
}
|