* Revert "Buff the AME until somebody fixes engineering" (#28419) * Automatic changelog update * hand teleport portals now may start in the same grid. (#28556) * Microwave recipes now uses stacktype id instead of entity prototype id for stacked entities (#28225) * Automatic changelog update * Add "fill level" sprites to mops and damp rag (#28590) * Automatic changelog update * Reword some criminal records text (#28597) * Update criminal-records.ftl * Update criminal-records.ftl * Update criminal-records.ftl * Remove obsolete VisibilitySystem functions (#28610) Remove obsolete visibility functions Co-authored-by: plykiya <plykiya@protonmail.com> * Prayable datafield typo (#28622) * notifiactionPrefix -> notificationPrefix * notifiactionPrefix -> notificationPrefix * Update engine to v224.1.0 (#28624) * Use dummy sessions in NukeOpsTest (#28549) * Add dummy sessions * Update NukeOpsTest * Fix PvsBenchmark * Update engine to v224.1.1 (#28632) * Add Job preference tests (#28625) * Misc Job related changes * Add JobTest * A * Aa * Lets not confuse the yaml linter * fixes * a * Add logs that provide session to player admin logs (#28628) * Cluster Update (#28627) done here * Automatic changelog update * minor banner changes (#28636) * minor banner changes * Uhrmm actchually it's you're, not your * Update banners.yml props Hyenh * Revenant spell catalog locale (#28638) locale * Make cuff default range again (#28576) * Make cuff default range again * uncuff distance * how about ONE --------- Co-authored-by: plykiya <plykiya@protonmail.com> * Automatic changelog update * Machine-code cleanup (#28489) * Make Projectiles Only Hit a Variety of Station Objects Unless Clicked on (#28571) * Automatic changelog update * Fix Smoke-grenade.ogg not being mono (#28593) * Fix Cigars Sprites + YAML fix for Inhand unlit cigars/cigs (#28641) * Automatic changelog update * Clean up Eva and Hardsuit helm yml + Lets atmos firesuit helm work as a BreathMask (#28602) * Shifts borgs hats to the right a bit (#28600) * Fix mouse inhands (#28623) * Adjust some touch reaction damage levels (#28591) * Automatic changelog update * Add closing storage UIs to StorageInteractionTest (#28633) * Update rules (#28452) * Add support for LocalizedDatasets to RandomMetadata (#28601) * Fix Admin Object tab sorting and search (#28609) * Automatic changelog update * Internals are kept on as long as any breathing tool is on (#28595) * Automatic changelog update * Convert rules to use guidebook parsing (#28647) * Gives Insulation and NoSlip to all bots (#28621) * Gives Insulation and NoSlip to all bots * remove NoSlip from children * Automatic changelog update * Nerfs welderbombing (#28650) nerf welderbombing * Automatic changelog update * Give jobs & antags prototypes a guide field (#28614) * Give jobs & antags prototypes a guide field * A * space Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Add todo * Fix merge errors --------- Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Automatic changelog update * Fix typo in Space Law's restricted gear (#28668) This typo was included in the wiki as well. Reported-by: Bobberson in the Discord * Automatic changelog update * fixed "silicones" typo in new rules (#28671) fixed all appearances of silicone where it should have been silicon * fix janitor not spawning with a survival box (#28669) hi * Automatic changelog update * Fix typo in slime naming conventions (#28682) * Flatpacker fixes (#28417) * Return medicine recipe solid material costs to 1 (#28679) Set material costs of medicine recipes to 1 * Automatic changelog update * Update Core (#28689) add * Fixed the guidebook listing every single rule (#28680) * Well i tried this way * New approach (start) * Did it * makes spacelaw available, put it under sec * Automatic changelog update * rule fixes first pass (#28701) update * Add JobRequirementOverride prototypes (#28607) * Add JobRequirementOverride prototypes * a * invert if * Add override that takes in prototypes directly * Tweak chapel salvage wreck (#28703) add * Fixes client having authority over rules popup cvars (#28655) * Fixes client having authority over rules popup cvars * Delete duplicate migration * Pre-update * Post-update * Add locale support for booze and soda jugs labels (#28708) * Swap some InRangeUnobstructed for InRangeUnoccluded (#28706) Swap InRangeUnobstructed to InRangeUnoccluded Co-authored-by: plykiya <plykiya@protonmail.com> * Automatic changelog update * Ports the singularity's values from vgstation (#28720) * ports the singularity values from vgstation * guidebook fix * 5000 energy level 6 singulo * Fix action icons when dragging an active action to another slot (#28692) * Add DoPopup data field to OnUseTimerTrigger (#28691) * 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> * Automatic changelog update * Improve grammar of various petting messages (#28715) Improve grammar of various petting message * Fix DamageOtherOnHit.OnDoHit when the target is terminating or deleted (#28690) * Update Core (#28735) add * Fix flatpacker (#28736) * Fix flatpacker * a * rn, atp (#28674) * Update speech-chatsan.ftl * Update word_replacements.yml * Atm-at the moment * Atm * Update Resources/Locale/en-US/speech/speech-chatsan.ftl * Update Resources/Prototypes/Accents/word_replacements.yml --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Add loadout group check for role proto (#28731) MFW same bug twice at 2 layers because I'm stupid. * Automatic changelog update * Try fix RGBee (#28741) * Automatic changelog update * Ensure packager creates a release folder (#28426) I was screaming at the github actions runner before i noticed something i had done before caused me to never have a release folder and thus fail. * allow ' in character names (#28652) * Automatic changelog update * Make freeze, freeze & mute, and unmute verbs work on disconnected players. (#28664) * Automatic changelog update * fix singulo decay (#28743) * Automatic changelog update * Don't use invalid defaults for loadouts (#28729) At the time it made more sense but now with species specific stuff it's better to have nothing. * Maybe fix invalid loadout prototypes (#28734) * Maybe fix invalid loadout prototypes So if we have existing data SetDefault is not normally called iirc. So what I think is happening is that if we have old loadout groups that get saved to DB and loaded these get dropped entirely and nothing is used to replace the group unless the person specifically looks at their loadout. Need someone affected to send me their loadout to confirm it's fixed. * Better fix * Automatic changelog update * Fix null ref exception in PrayerSystem (#28712) * Fix null ref exception in PrayerSystem * Also check that prayable ent/comp still exist * Guidebook Updates for the Amateur Spessman (#28603) * Created NewPlayer.xml * Created NewPlayer.yml and added it to guides.ftl * shifted some controls from Space Station 14 to New? Start here!, as well as made a character creation xml to be written later * switched some stuff between the New? entry and the Space Station 14 entry * Made everything so nice!!!!!!!!!!!!!! * fixed formatting inconsistencies * added a How to use this guidebook section * build correction and guidebook clarification for other servers * wrote character creation ig probs fo shizzle * added new terms to the glossary and alphabetized it * meh this seems important enough to add * okay no more shitsec bad idea * I HATED Roleplaying.xml ANYWAY!!! * I REALLY REALLY HATED IT ACTUALLYLLL * Moved Controls and Radio into newplayer.yml, making meta.yml and radio.yml obsolete * Separated the character creation bits that are just cosmetic from the ones that matter * also put all the related new player xml files in their own folder * expanded Radio.xml, kinda fixed survival.xml * removed the line that mighta maybe sorta possibly could encourage self antag * thought about this randomly but ICK OCK * talking is no longer a key part of this game * moves stuff around, a lot of stuff. basically moves everything but the jobs themselves around * ah probably should make sure this works first also me when i lie on the internet * don't be such a grammar nukie Co-authored-by: Tayrtahn <tayrtahn@gmail.com> * okay nevermind that's justified Co-authored-by: Tayrtahn <tayrtahn@gmail.com> * prepare. it is coming. the great reorganization... * yesyes that's all well and goo- THE REORG IS COMING. THE REORG IS COMING. WAKE UP. * rename that real quick * step one begins. first, consolidate existing service entries into their own yaml. this makes botany.yml obsolete. * update shiftandcrew.yml to only have departments as children also fuck it alphabetization * consolidated salvage into cargo * made a new entry for command * gave salvage a home and service an existence * made some XML files to be filled out later * quick rename... * took some stuff from Intro.txt and i think Gameplay.txt and put it in NewPlayer.xml * The Great Writing about Departments (25XX, black and white) * added a bunch of links everywhere * biochemical is no longer a thing * service formatinaaaaaaa * shiny...,,,,,,,,, colo(u)rz..,,,,,,,,,,,,, * let's get that fixed * second time i made a typo there as well * we hate fun around here * grammar?!???/ * various fixes and more linkings * oops * wewlad lol!! --------- Co-authored-by: Tayrtahn <tayrtahn@gmail.com> Co-authored-by: AJCM <AJCM@tutanota.com> * Automatic changelog update * Add suffixes to excap survival boxes (#28755) Update emergency.yml * Add Jani lobby background (#28724) * Add jani lobby background * Actually make new lobby screen functional * Fix license * Automatic changelog update * Strip markdown from silicon laws before saying them (#28596) * saltern update (#28773) Co-authored-by: deltanedas <@deltanedas:kde.org> * revert Tornado regex * Update HumanoidCharacterProfile.cs * Update arenas.yml * test * fix locale * Update options-menu.ftl * Update species-names.ftl * Update debug.yml * aaaaaa --------- Co-authored-by: Moony <moony@hellomouse.net> Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: icekot8 <93311212+icekot8@users.noreply.github.com> Co-authored-by: blueDev2 <89804215+blueDev2@users.noreply.github.com> Co-authored-by: Tayrtahn <tayrtahn@gmail.com> Co-authored-by: TsjipTsjip <19798667+TsjipTsjip@users.noreply.github.com> Co-authored-by: Plykiya <58439124+Plykiya@users.noreply.github.com> Co-authored-by: plykiya <plykiya@protonmail.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Co-authored-by: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Co-authored-by: Hmeister-real <118129069+Hmeister-real@users.noreply.github.com> Co-authored-by: lapatison <100279397+lapatison@users.noreply.github.com> Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Co-authored-by: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Co-authored-by: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Co-authored-by: Ps3Moira <113228053+ps3moira@users.noreply.github.com> Co-authored-by: Verm <32827189+Vermidia@users.noreply.github.com> Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com> Co-authored-by: Repo <47093363+Titian3@users.noreply.github.com> Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com> Co-authored-by: Thomas <87614336+Aeshus@users.noreply.github.com> Co-authored-by: Moomoobeef <62638182+Moomoobeef@users.noreply.github.com> Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> Co-authored-by: Ubaser <134914314+UbaserB@users.noreply.github.com> Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Co-authored-by: lzk <124214523+lzk228@users.noreply.github.com> Co-authored-by: Lyndomen <49795619+Lyndomen@users.noreply.github.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: MerrytheManokit <167581110+MerrytheManokit@users.noreply.github.com> Co-authored-by: Vasilis <vasilis@pikachu.systems> Co-authored-by: Whisper <121047731+QuietlyWhisper@users.noreply.github.com> Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Co-authored-by: UBlueberry <161545003+UBlueberry@users.noreply.github.com> Co-authored-by: AJCM <AJCM@tutanota.com> Co-authored-by: Psychpsyo <60073468+Psychpsyo@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
457 lines
23 KiB
C#
457 lines
23 KiB
C#
using Content.Server.Construction.Components;
|
|
using Content.Server.Containers;
|
|
using Content.Shared.Construction;
|
|
using Content.Shared.Construction.Prototypes;
|
|
using Content.Shared.Construction.Steps;
|
|
using Content.Shared.Containers;
|
|
using Content.Shared.Database;
|
|
using Robust.Server.Containers;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Prototypes;
|
|
using System.Linq;
|
|
|
|
namespace Content.Server.Construction
|
|
{
|
|
public sealed partial class ConstructionSystem
|
|
{
|
|
private void InitializeGraphs()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets a container on an entity as being handled by Construction. This essentially means that it will
|
|
/// be transferred if the entity prototype changes. <seealso cref="ChangeEntity"/>
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="container">The container identifier. This method does not check whether the container exists.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>Whether we could set the container as being handled by construction or not. Also returns false if
|
|
/// the entity does not have a <see cref="ConstructionComponent"/>.</returns>
|
|
public bool AddContainer(EntityUid uid, string container, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction))
|
|
return false;
|
|
|
|
return construction.Containers.Add(container);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current construction graph of an entity, or null.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>The current construction graph of an entity or null if invalid. Also returns null if the entity
|
|
/// does not have a <see cref="ConstructionComponent"/>.</returns>
|
|
/// <remarks>An entity with a valid construction state will always have a valid graph.</remarks>
|
|
public ConstructionGraphPrototype? GetCurrentGraph(EntityUid uid, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction, false))
|
|
return null;
|
|
|
|
// If the set graph prototype does not exist, also return null. This could be due to admemes changing values
|
|
// in ViewVariables, so even though the construction state is invalid, just return null.
|
|
return PrototypeManager.TryIndex(construction.Graph, out ConstructionGraphPrototype? graph) ? graph : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the construction graph node the entity is currently at, or null.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>The current construction graph node the entity is currently at, or null if invalid. Also returns
|
|
/// null if the entity does not have a <see cref="ConstructionComponent"/>.</returns>
|
|
/// <remarks>An entity with a valid construction state will always be at a valid node.</remarks>
|
|
public ConstructionGraphNode? GetCurrentNode(EntityUid uid, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction, false))
|
|
return null;
|
|
|
|
if (construction.Node is not {} nodeIdentifier)
|
|
return null;
|
|
|
|
return GetCurrentGraph(uid, construction) is not {} graph ? null : GetNodeFromGraph(graph, nodeIdentifier);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the construction graph edge the entity is currently at, or null.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>The construction graph edge the entity is currently at, if any. Also returns null if the entity
|
|
/// does not have a <see cref="ConstructionComponent"/>.</returns>
|
|
/// <remarks>An entity with a valid construction state might not always be at an edge.</remarks>
|
|
public ConstructionGraphEdge? GetCurrentEdge(EntityUid uid, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction, false))
|
|
return null;
|
|
|
|
if (construction.EdgeIndex is not {} edgeIndex)
|
|
return null;
|
|
|
|
return GetCurrentNode(uid, construction) is not {} node ? null : GetEdgeFromNode(node, edgeIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Variant of <see cref="GetCurrentEdge"/> that returns both the node and edge.
|
|
/// </summary>
|
|
public (ConstructionGraphNode?, ConstructionGraphEdge?) GetCurrentNodeAndEdge(EntityUid uid, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction, false))
|
|
return (null, null);
|
|
|
|
if (GetCurrentNode(uid, construction) is not { } node)
|
|
return (null, null);
|
|
|
|
if (construction.EdgeIndex is not {} edgeIndex)
|
|
return (node, null);
|
|
|
|
return (node, GetEdgeFromNode(node, edgeIndex));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the construction graph step the entity is currently at, or null.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>The construction graph step the entity is currently at, if any. Also returns null if the entity
|
|
/// does not have a <see cref="ConstructionComponent"/>.</returns>
|
|
/// <remarks>An entity with a valid construction state might not always be at a step or an edge.</remarks>
|
|
public ConstructionGraphStep? GetCurrentStep(EntityUid uid, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction, false))
|
|
return null;
|
|
|
|
if (GetCurrentEdge(uid, construction) is not {} edge)
|
|
return null;
|
|
|
|
return GetStepFromEdge(edge, construction.StepIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the construction graph node the entity's construction pathfinding is currently targeting, if any.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>The construction graph node the entity's construction pathfinding is currently targeting, or null
|
|
/// if it's not currently targeting any node. Also returns null if the entity does not have a
|
|
/// <see cref="ConstructionComponent"/>.</returns>
|
|
/// <remarks>Target nodes are entirely optional and only used for pathfinding purposes.</remarks>
|
|
public ConstructionGraphNode? GetTargetNode(EntityUid uid, ConstructionComponent? construction)
|
|
{
|
|
if (!Resolve(uid, ref construction))
|
|
return null;
|
|
|
|
if (construction.TargetNode is not {} targetNodeId)
|
|
return null;
|
|
|
|
if (GetCurrentGraph(uid, construction) is not {} graph)
|
|
return null;
|
|
|
|
return GetNodeFromGraph(graph, targetNodeId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the construction graph edge the entity's construction pathfinding is currently targeting, if any.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>The construction graph edge the entity's construction pathfinding is currently targeting, or null
|
|
/// if it's not currently targeting any edge. Also returns null if the entity does not have a
|
|
/// <see cref="ConstructionComponent"/>.</returns>
|
|
/// <remarks>Target edges are entirely optional and only used for pathfinding purposes. The targeted edge will
|
|
/// be an edge on the current construction node the entity is at.</remarks>
|
|
public ConstructionGraphEdge? GetTargetEdge(EntityUid uid, ConstructionComponent? construction)
|
|
{
|
|
if (!Resolve(uid, ref construction))
|
|
return null;
|
|
|
|
if (construction.TargetEdgeIndex is not {} targetEdgeIndex)
|
|
return null;
|
|
|
|
if (GetCurrentNode(uid, construction) is not {} node)
|
|
return null;
|
|
|
|
return GetEdgeFromNode(node, targetEdgeIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets both the construction edge and step the entity is currently at, if any.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>A tuple containing the current edge and step the entity's construction state is at.</returns>
|
|
/// <remarks>The edge, step or both could be null. A valid construction state does not necessarily need them.</remarks>
|
|
public (ConstructionGraphEdge? edge, ConstructionGraphStep? step) GetCurrentEdgeAndStep(EntityUid uid,
|
|
ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction, false))
|
|
return default;
|
|
|
|
var edge = GetCurrentEdge(uid, construction);
|
|
|
|
if (edge == null)
|
|
return default;
|
|
|
|
var step = GetStepFromEdge(edge, construction.StepIndex);
|
|
|
|
return (edge, step);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a node from a construction graph given its identifier.
|
|
/// </summary>
|
|
/// <param name="graph">The construction graph where to get the node.</param>
|
|
/// <param name="id">The identifier that corresponds to the node.</param>
|
|
/// <returns>The node that corresponds to the identifier, or null if it doesn't exist.</returns>
|
|
public ConstructionGraphNode? GetNodeFromGraph(ConstructionGraphPrototype graph, string id)
|
|
{
|
|
return graph.Nodes.TryGetValue(id, out var node) ? node : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an edge from a construction node given its index.
|
|
/// </summary>
|
|
/// <param name="node">The construction node where to get the edge.</param>
|
|
/// <param name="index">The index or position of the edge on the node.</param>
|
|
/// <returns>The edge on that index in the construction node, or null if none.</returns>
|
|
public ConstructionGraphEdge? GetEdgeFromNode(ConstructionGraphNode node, int index)
|
|
{
|
|
return node.Edges.Count > index ? node.Edges[index] : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a step from a construction edge given its index.
|
|
/// </summary>
|
|
/// <param name="edge">The construction edge where to get the step.</param>
|
|
/// <param name="index">The index or position of the step on the edge.</param>
|
|
/// <returns>The edge on that index in the construction edge, or null if none.</returns>
|
|
public ConstructionGraphStep? GetStepFromEdge(ConstructionGraphEdge edge, int index)
|
|
{
|
|
return edge.Steps.Count > index ? edge.Steps[index] : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs a node change on a construction entity, optionally performing the actions for the new node.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="userUid">An optional user entity, for actions.</param>
|
|
/// <param name="id">The identifier of the node to change to.</param>
|
|
/// <param name="performActions">Whether the actions for the new node will be performed or not.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>Whether the node change succeeded or not. Also returns false if the entity does not have a <see cref="ConstructionComponent"/>.</returns>
|
|
/// <remarks>This method also updates the construction pathfinding automatically, if the node change succeeds.</remarks>
|
|
public bool ChangeNode(EntityUid uid, EntityUid? userUid, string id, bool performActions = true, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction))
|
|
return false;
|
|
|
|
if (GetCurrentGraph(uid, construction) is not {} graph
|
|
|| GetNodeFromGraph(graph, id) is not {} node)
|
|
return false;
|
|
|
|
var oldNode = construction.Node;
|
|
construction.Node = id;
|
|
|
|
if (userUid != null)
|
|
_adminLogger.Add(LogType.Construction, LogImpact.Low,
|
|
$"{ToPrettyString(userUid.Value):player} changed {ToPrettyString(uid):entity}'s node from \"{oldNode}\" to \"{id}\"");
|
|
|
|
// ChangeEntity will handle the pathfinding update.
|
|
if (node.Entity.GetId(uid, userUid, new(EntityManager)) is {} newEntity
|
|
&& ChangeEntity(uid, userUid, newEntity, construction) != null)
|
|
return true;
|
|
|
|
if(performActions)
|
|
PerformActions(uid, userUid, node.Actions);
|
|
|
|
// An action might have deleted the entity... Account for this.
|
|
if (!Exists(uid))
|
|
return false;
|
|
|
|
UpdatePathfinding(uid, construction);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs an entity prototype change on a construction entity.
|
|
/// The old entity will be removed, and a new one will be spawned in its place. Some values will be kept,
|
|
/// and any containers handled by construction will be transferred to the new entity as well.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="userUid">An optional user entity, for actions.</param>
|
|
/// <param name="newEntity">The entity prototype identifier for the new entity.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <param name="metaData">The metadata component of the target entity. Will be resolved if null.</param>
|
|
/// <param name="transform">The transform component of the target entity. Will be resolved if null.</param>
|
|
/// <param name="containerManager">The container manager component of the target entity. Will be resolved if null,
|
|
/// but it is an optional component and not required for the method to work.</param>
|
|
/// <returns>The new entity, or null if the method did not succeed.</returns>
|
|
private EntityUid? ChangeEntity(EntityUid uid, EntityUid? userUid, string newEntity,
|
|
ConstructionComponent? construction = null,
|
|
MetaDataComponent? metaData = null,
|
|
TransformComponent? transform = null,
|
|
ContainerManagerComponent? containerManager = null)
|
|
{
|
|
if (!Resolve(uid, ref construction, ref metaData, ref transform))
|
|
{
|
|
// Failed resolve logs an error, but we want to actually log information about the failed construction
|
|
// graph. So lets let the UpdateInteractions() try-catch log that info for us.
|
|
throw new Exception("Missing construction components");
|
|
}
|
|
|
|
// Exit if the new entity's prototype is the same as the original, or the prototype is invalid
|
|
if (newEntity == metaData.EntityPrototype?.ID || !PrototypeManager.HasIndex<EntityPrototype>(newEntity))
|
|
return null;
|
|
|
|
// [Optional] Exit if the new entity's prototype is a parent of the original
|
|
// E.g., if an entity with the 'AirlockCommand' prototype was to be replaced with a new entity that
|
|
// had the 'Airlock' prototype, and DoNotReplaceInheritingEntities was true, the code block would
|
|
// exit here because 'AirlockCommand' is derived from 'Airlock'
|
|
if (GetCurrentNode(uid, construction)?.DoNotReplaceInheritingEntities == true &&
|
|
metaData.EntityPrototype?.ID != null)
|
|
{
|
|
var parents = PrototypeManager.EnumerateParents<EntityPrototype>(metaData.EntityPrototype.ID)?.ToList();
|
|
|
|
if (parents != null && parents.Any(x => x.ID == newEntity))
|
|
return null;
|
|
}
|
|
|
|
// Optional resolves.
|
|
Resolve(uid, ref containerManager, false);
|
|
|
|
// We create the new entity.
|
|
var newUid = EntityManager.CreateEntityUninitialized(newEntity, transform.Coordinates);
|
|
|
|
// Construction transferring.
|
|
var newConstruction = EntityManager.EnsureComponent<ConstructionComponent>(newUid);
|
|
|
|
// Transfer all construction-owned containers.
|
|
newConstruction.Containers.UnionWith(construction.Containers);
|
|
|
|
// Prevent MapInitEvent spawned entities from spawning into the containers.
|
|
// Containers created by ChangeNode() actions do not exist until after this function is complete,
|
|
// but this should be fine, as long as the target entity properly declared its managed containers.
|
|
if (TryComp(newUid, out ContainerFillComponent? containerFill) && containerFill.IgnoreConstructionSpawn)
|
|
{
|
|
foreach (var id in newConstruction.Containers)
|
|
{
|
|
containerFill.Containers.Remove(id);
|
|
}
|
|
}
|
|
|
|
// If the new entity has the *same* construction graph, stay on the same node.
|
|
// If not, we effectively restart the construction graph, so the new entity can be completed.
|
|
if (construction.Graph == newConstruction.Graph)
|
|
{
|
|
ChangeNode(newUid, userUid, construction.Node, false, newConstruction);
|
|
|
|
// Retain the target node if an entity change happens in response to deconstruction;
|
|
// in that case, we must continue to move towards the start node.
|
|
if (construction.TargetNode is {} targetNode)
|
|
SetPathfindingTarget(newUid, targetNode, newConstruction);
|
|
}
|
|
|
|
// Transfer all pending interaction events too.
|
|
while (construction.InteractionQueue.TryDequeue(out var ev))
|
|
{
|
|
newConstruction.InteractionQueue.Enqueue(ev);
|
|
}
|
|
|
|
if (newConstruction.InteractionQueue.Count > 0 && _queuedUpdates.Add(newUid))
|
|
_constructionUpdateQueue.Enqueue(newUid);
|
|
|
|
// Transform transferring.
|
|
var newTransform = Transform(newUid);
|
|
newTransform.AttachToGridOrMap(); // in case in hands or a container
|
|
newTransform.LocalRotation = transform.LocalRotation;
|
|
newTransform.Anchored = transform.Anchored;
|
|
|
|
// Container transferring.
|
|
if (containerManager != null)
|
|
{
|
|
// Ensure the new entity has a container manager. Also for resolve goodness.
|
|
var newContainerManager = EntityManager.EnsureComponent<ContainerManagerComponent>(newUid);
|
|
|
|
// Transfer all construction-owned containers from the old entity to the new one.
|
|
foreach (var container in construction.Containers)
|
|
{
|
|
if (!_container.TryGetContainer(uid, container, out var ourContainer, containerManager))
|
|
continue;
|
|
|
|
if (!_container.TryGetContainer(newUid, container, out var otherContainer, newContainerManager))
|
|
{
|
|
// NOTE: Only Container is supported by Construction!
|
|
// todo: one day, the ensured container should be the same type as ourContainer
|
|
otherContainer = _container.EnsureContainer<Container>(newUid, container, newContainerManager);
|
|
}
|
|
|
|
for (var i = ourContainer.ContainedEntities.Count - 1; i >= 0; i--)
|
|
{
|
|
var entity = ourContainer.ContainedEntities[i];
|
|
_container.Remove(entity, ourContainer, reparent: false, force: true);
|
|
_container.Insert(entity, otherContainer);
|
|
}
|
|
}
|
|
}
|
|
|
|
var entChangeEv = new ConstructionChangeEntityEvent(newUid, uid);
|
|
RaiseLocalEvent(uid, entChangeEv);
|
|
RaiseLocalEvent(newUid, entChangeEv, broadcast: true);
|
|
|
|
foreach (var logic in GetCurrentNode(newUid, newConstruction)!.TransformLogic)
|
|
{
|
|
logic.Transform(uid, newUid, userUid, new(EntityManager));
|
|
}
|
|
|
|
EntityManager.InitializeAndStartEntity(newUid);
|
|
|
|
QueueDel(uid);
|
|
|
|
return newUid;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs a construction graph change on a construction entity, also changing the node to a valid one on
|
|
/// the new graph.
|
|
/// </summary>
|
|
/// <param name="uid">The target entity.</param>
|
|
/// <param name="userUid">An optional user entity, for actions.</param>
|
|
/// <param name="graphId">The identifier for the construction graph to switch to.</param>
|
|
/// <param name="nodeId">The identifier for a node on the new construction graph to switch to.</param>
|
|
/// <param name="performActions">Whether actions on the new node will be performed or not.</param>
|
|
/// <param name="construction">The construction component of the target entity. Will be resolved if null.</param>
|
|
/// <returns>Whether the construction graph change succeeded or not. Returns false if the entity does not have
|
|
/// a <see cref="ConstructionComponent"/>.</returns>
|
|
public bool ChangeGraph(EntityUid uid, EntityUid? userUid, string graphId, string nodeId, bool performActions = true, ConstructionComponent? construction = null)
|
|
{
|
|
if (!Resolve(uid, ref construction))
|
|
return false;
|
|
|
|
if (!PrototypeManager.TryIndex<ConstructionGraphPrototype>(graphId, out var graph))
|
|
return false;
|
|
|
|
if(GetNodeFromGraph(graph, nodeId) is not {})
|
|
return false;
|
|
|
|
construction.Graph = graphId;
|
|
return ChangeNode(uid, userUid, nodeId, performActions, construction);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This event gets raised when an entity changes prototype / uid during construction. The event is raised
|
|
/// directed both at the old and new entity.
|
|
/// </summary>
|
|
public sealed class ConstructionChangeEntityEvent : EntityEventArgs
|
|
{
|
|
public readonly EntityUid New;
|
|
public readonly EntityUid Old;
|
|
|
|
public ConstructionChangeEntityEvent(EntityUid newUid, EntityUid oldUid)
|
|
{
|
|
New = newUid;
|
|
Old = oldUid;
|
|
}
|
|
}
|
|
}
|