* 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>
548 lines
21 KiB
C#
548 lines
21 KiB
C#
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Content.Server.Construction.Components;
|
|
using Content.Shared.ActionBlocker;
|
|
using Content.Shared.Construction;
|
|
using Content.Shared.Construction.Prototypes;
|
|
using Content.Shared.Construction.Steps;
|
|
using Content.Shared.Coordinates;
|
|
using Content.Shared.Database;
|
|
using Content.Shared.DoAfter;
|
|
using Content.Shared.Hands.Components;
|
|
using Content.Shared.Hands.EntitySystems;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.Storage;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Server.Construction
|
|
{
|
|
public sealed partial class ConstructionSystem
|
|
{
|
|
[Dependency] private readonly IComponentFactory _factory = default!;
|
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
|
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
|
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
|
[Dependency] private readonly EntityLookupSystem _lookupSystem = default!;
|
|
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
|
|
|
// --- WARNING! LEGACY CODE AHEAD! ---
|
|
// This entire file contains the legacy code for initial construction.
|
|
// This is bound to be replaced by a better alternative (probably using dummy entities)
|
|
// but for now I've isolated them in their own little file. This code is largely unchanged.
|
|
// --- YOU HAVE BEEN WARNED! AAAH! ---
|
|
|
|
private readonly Dictionary<ICommonSession, HashSet<int>> _beingBuilt = new();
|
|
|
|
private void InitializeInitial()
|
|
{
|
|
SubscribeNetworkEvent<TryStartStructureConstructionMessage>(HandleStartStructureConstruction);
|
|
SubscribeNetworkEvent<TryStartItemConstructionMessage>(HandleStartItemConstruction);
|
|
}
|
|
|
|
// LEGACY CODE. See warning at the top of the file!
|
|
private IEnumerable<EntityUid> EnumerateNearby(EntityUid user)
|
|
{
|
|
foreach (var item in _handsSystem.EnumerateHeld(user))
|
|
{
|
|
if (TryComp(item, out StorageComponent? storage))
|
|
{
|
|
foreach (var storedEntity in storage.Container.ContainedEntities!)
|
|
{
|
|
yield return storedEntity;
|
|
}
|
|
}
|
|
|
|
yield return item;
|
|
}
|
|
|
|
if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator))
|
|
{
|
|
while (containerSlotEnumerator.MoveNext(out var containerSlot))
|
|
{
|
|
if(!containerSlot.ContainedEntity.HasValue)
|
|
continue;
|
|
|
|
if (EntityManager.TryGetComponent(containerSlot.ContainedEntity.Value, out StorageComponent? storage))
|
|
{
|
|
foreach (var storedEntity in storage.Container.ContainedEntities)
|
|
{
|
|
yield return storedEntity;
|
|
}
|
|
}
|
|
|
|
yield return containerSlot.ContainedEntity.Value;
|
|
}
|
|
}
|
|
|
|
var pos = _transformSystem.GetMapCoordinates(user);
|
|
|
|
foreach (var near in _lookupSystem.GetEntitiesInRange(pos, 2f, LookupFlags.Contained | LookupFlags.Dynamic | LookupFlags.Sundries | LookupFlags.Approximate))
|
|
{
|
|
if (near == user)
|
|
continue;
|
|
if (_interactionSystem.InRangeUnobstructed(pos, near, 2f) && _container.IsInSameOrParentContainer(user, near))
|
|
yield return near;
|
|
}
|
|
}
|
|
|
|
// LEGACY CODE. See warning at the top of the file!
|
|
private async Task<EntityUid?> Construct(
|
|
EntityUid user,
|
|
string materialContainer,
|
|
ConstructionGraphPrototype graph,
|
|
ConstructionGraphEdge edge,
|
|
ConstructionGraphNode targetNode,
|
|
EntityCoordinates coords,
|
|
Angle angle = default)
|
|
{
|
|
// We need a place to hold our construction items!
|
|
var container = _container.EnsureContainer<Container>(user, materialContainer, out var existed);
|
|
|
|
if (existed)
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("construction-system-construct-cannot-start-another-construction"), user, user);
|
|
return null;
|
|
}
|
|
|
|
var containers = new Dictionary<string, Container>();
|
|
|
|
var doAfterTime = 0f;
|
|
|
|
// HOLY SHIT THIS IS SOME HACKY CODE.
|
|
// But I'd rather do this shit than risk having collisions with other containers.
|
|
Container GetContainer(string name)
|
|
{
|
|
if (containers.TryGetValue(name, out var container1))
|
|
return container1;
|
|
|
|
while (true)
|
|
{
|
|
var random = _robustRandom.Next();
|
|
var c = _container.EnsureContainer<Container>(user, random.ToString(), out var exists);
|
|
|
|
if (exists)
|
|
continue;
|
|
|
|
containers[name] = c;
|
|
return c;
|
|
}
|
|
}
|
|
|
|
void FailCleanup()
|
|
{
|
|
foreach (var entity in container.ContainedEntities.ToArray())
|
|
{
|
|
_container.Remove(entity, container);
|
|
}
|
|
|
|
foreach (var cont in containers.Values)
|
|
{
|
|
foreach (var entity in cont.ContainedEntities.ToArray())
|
|
{
|
|
_container.Remove(entity, cont);
|
|
}
|
|
}
|
|
|
|
// If we don't do this, items are invisible for some fucking reason. Nice.
|
|
Timer.Spawn(1, ShutdownContainers);
|
|
}
|
|
|
|
void ShutdownContainers()
|
|
{
|
|
_container.ShutdownContainer(container);
|
|
foreach (var c in containers.Values.ToArray())
|
|
{
|
|
_container.ShutdownContainer(c);
|
|
}
|
|
}
|
|
|
|
var failed = false;
|
|
|
|
var steps = new List<ConstructionGraphStep>();
|
|
var used = new HashSet<EntityUid>();
|
|
|
|
foreach (var step in edge.Steps)
|
|
{
|
|
doAfterTime += step.DoAfter;
|
|
|
|
var handled = false;
|
|
|
|
switch (step)
|
|
{
|
|
case MaterialConstructionGraphStep materialStep:
|
|
foreach (var entity in EnumerateNearby(user))
|
|
{
|
|
if (!materialStep.EntityValid(entity, out var stack))
|
|
continue;
|
|
|
|
if (used.Contains(entity))
|
|
continue;
|
|
|
|
// TODO allow taking from several stacks.
|
|
// Also update crafting steps to check if it works.
|
|
var splitStack = _stackSystem.Split(entity, materialStep.Amount, user.ToCoordinates(0, 0), stack);
|
|
|
|
if (splitStack == null)
|
|
continue;
|
|
|
|
if (string.IsNullOrEmpty(materialStep.Store))
|
|
{
|
|
if (!_container.Insert(splitStack.Value, container))
|
|
continue;
|
|
}
|
|
else if (!_container.Insert(splitStack.Value, GetContainer(materialStep.Store)))
|
|
continue;
|
|
|
|
handled = true;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case ArbitraryInsertConstructionGraphStep arbitraryStep:
|
|
foreach (var entity in new HashSet<EntityUid>(EnumerateNearby(user)))
|
|
{
|
|
if (!arbitraryStep.EntityValid(entity, EntityManager, _factory))
|
|
continue;
|
|
|
|
if (used.Contains(entity))
|
|
continue;
|
|
|
|
// Dump out any stored entities in used entity
|
|
if (TryComp<StorageComponent>(entity, out var storage))
|
|
{
|
|
_container.EmptyContainer(storage.Container);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(arbitraryStep.Store))
|
|
{
|
|
if (!_container.Insert(entity, container))
|
|
continue;
|
|
}
|
|
else if (!_container.Insert(entity, GetContainer(arbitraryStep.Store)))
|
|
continue;
|
|
|
|
handled = true;
|
|
used.Add(entity);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (handled == false)
|
|
{
|
|
failed = true;
|
|
break;
|
|
}
|
|
|
|
steps.Add(step);
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("construction-system-construct-no-materials"), user, user);
|
|
FailCleanup();
|
|
return null;
|
|
}
|
|
|
|
var doAfterArgs = new DoAfterArgs(EntityManager, user, doAfterTime, new AwaitedDoAfterEvent(), null)
|
|
{
|
|
BreakOnDamage = true,
|
|
BreakOnMove = true,
|
|
NeedHand = false,
|
|
// allow simultaneously starting several construction jobs using the same stack of materials.
|
|
CancelDuplicate = false,
|
|
BlockDuplicate = false,
|
|
};
|
|
|
|
if (await _doAfterSystem.WaitDoAfter(doAfterArgs) == DoAfterStatus.Cancelled)
|
|
{
|
|
FailCleanup();
|
|
return null;
|
|
}
|
|
|
|
var newEntityProto = graph.Nodes[edge.Target].Entity.GetId(null, user, new(EntityManager));
|
|
var newEntity = Spawn(newEntityProto, _transformSystem.ToMapCoordinates(coords), rotation: angle);
|
|
|
|
if (!TryComp(newEntity, out ConstructionComponent? construction))
|
|
{
|
|
Log.Error($"Initial construction does not have a valid target entity! It is missing a ConstructionComponent.\nGraph: {graph.ID}, Initial Target: {edge.Target}, Ent. Prototype: {newEntityProto}\nCreated Entity {ToPrettyString(newEntity)} will be deleted.");
|
|
Del(newEntity); // Screw you, make proper construction graphs.
|
|
return null;
|
|
}
|
|
|
|
// We attempt to set the pathfinding target.
|
|
SetPathfindingTarget(newEntity, targetNode.Name, construction);
|
|
|
|
// We preserve the containers...
|
|
foreach (var (name, cont) in containers)
|
|
{
|
|
var newCont = _container.EnsureContainer<Container>(newEntity, name);
|
|
|
|
foreach (var entity in cont.ContainedEntities.ToArray())
|
|
{
|
|
_container.Remove(entity, cont, reparent: false, force: true);
|
|
_container.Insert(entity, newCont);
|
|
}
|
|
}
|
|
|
|
// We now get rid of all them.
|
|
ShutdownContainers();
|
|
|
|
// We have step completed steps!
|
|
foreach (var step in steps)
|
|
{
|
|
foreach (var completed in step.Completed)
|
|
{
|
|
completed.PerformAction(newEntity, user, EntityManager);
|
|
}
|
|
}
|
|
|
|
// And we also have edge completed effects!
|
|
foreach (var completed in edge.Completed)
|
|
{
|
|
completed.PerformAction(newEntity, user, EntityManager);
|
|
}
|
|
|
|
return newEntity;
|
|
}
|
|
|
|
private async void HandleStartItemConstruction(TryStartItemConstructionMessage ev, EntitySessionEventArgs args)
|
|
{
|
|
if (args.SenderSession.AttachedEntity is {Valid: true} user)
|
|
await TryStartItemConstruction(ev.PrototypeName, user);
|
|
}
|
|
|
|
// LEGACY CODE. See warning at the top of the file!
|
|
public async Task<bool> TryStartItemConstruction(string prototype, EntityUid user)
|
|
{
|
|
if (!PrototypeManager.TryIndex(prototype, out ConstructionPrototype? constructionPrototype))
|
|
{
|
|
Log.Error($"Tried to start construction of invalid recipe '{prototype}'!");
|
|
return false;
|
|
}
|
|
|
|
if (!PrototypeManager.TryIndex(constructionPrototype.Graph,
|
|
out ConstructionGraphPrototype? constructionGraph))
|
|
{
|
|
Log.Error(
|
|
$"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{prototype}'!");
|
|
return false;
|
|
}
|
|
|
|
if (_whitelistSystem.IsWhitelistFail(constructionPrototype.EntityWhitelist, user))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("construction-system-cannot-start"), user, user);
|
|
return false;
|
|
}
|
|
|
|
var startNode = constructionGraph.Nodes[constructionPrototype.StartNode];
|
|
var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode];
|
|
var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name);
|
|
|
|
if (!_actionBlocker.CanInteract(user, null))
|
|
return false;
|
|
|
|
if (!HasComp<HandsComponent>(user))
|
|
return false;
|
|
|
|
foreach (var condition in constructionPrototype.Conditions)
|
|
{
|
|
if (!condition.Condition(user, user.ToCoordinates(0, 0), Direction.South))
|
|
return false;
|
|
}
|
|
|
|
if (pathFind == null)
|
|
{
|
|
throw new InvalidDataException(
|
|
$"Can't find path from starting node to target node in construction! Recipe: {prototype}");
|
|
}
|
|
|
|
var edge = startNode.GetEdge(pathFind[0].Name);
|
|
|
|
if (edge == null)
|
|
{
|
|
throw new InvalidDataException(
|
|
$"Can't find edge from starting node to the next node in pathfinding! Recipe: {prototype}");
|
|
}
|
|
|
|
// No support for conditions here!
|
|
|
|
foreach (var step in edge.Steps)
|
|
{
|
|
switch (step)
|
|
{
|
|
case ToolConstructionGraphStep _:
|
|
throw new InvalidDataException("Invalid first step for construction recipe!");
|
|
}
|
|
}
|
|
|
|
if (await Construct(
|
|
user,
|
|
"item_construction",
|
|
constructionGraph,
|
|
edge,
|
|
targetNode,
|
|
Transform(user).Coordinates) is not { Valid: true } item)
|
|
return false;
|
|
|
|
// Just in case this is a stack, attempt to merge it. If it isn't a stack, this will just normally pick up
|
|
// or drop the item as normal.
|
|
_stackSystem.TryMergeToHands(item, user);
|
|
return true;
|
|
}
|
|
|
|
// LEGACY CODE. See warning at the top of the file!
|
|
private async void HandleStartStructureConstruction(TryStartStructureConstructionMessage ev, EntitySessionEventArgs args)
|
|
{
|
|
if (!PrototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype? constructionPrototype))
|
|
{
|
|
Log.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!");
|
|
RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack));
|
|
return;
|
|
}
|
|
|
|
if (!PrototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype? constructionGraph))
|
|
{
|
|
Log.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!");
|
|
RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack));
|
|
return;
|
|
}
|
|
|
|
if (args.SenderSession.AttachedEntity is not {Valid: true} user)
|
|
{
|
|
Log.Error($"Client sent {nameof(TryStartStructureConstructionMessage)} with no attached entity!");
|
|
return;
|
|
}
|
|
|
|
if (_whitelistSystem.IsWhitelistFail(constructionPrototype.EntityWhitelist, user))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("construction-system-cannot-start"), user, user);
|
|
return;
|
|
}
|
|
|
|
if (_container.IsEntityInContainer(user))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("construction-system-inside-container"), user, user);
|
|
return;
|
|
}
|
|
|
|
var startNode = constructionGraph.Nodes[constructionPrototype.StartNode];
|
|
var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode];
|
|
var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name);
|
|
|
|
|
|
if (_beingBuilt.TryGetValue(args.SenderSession, out var set))
|
|
{
|
|
if (!set.Add(ev.Ack))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("construction-system-already-building"), user, user);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var newSet = new HashSet<int> {ev.Ack};
|
|
_beingBuilt[args.SenderSession] = newSet;
|
|
}
|
|
|
|
var location = GetCoordinates(ev.Location);
|
|
|
|
foreach (var condition in constructionPrototype.Conditions)
|
|
{
|
|
if (!condition.Condition(user, location, ev.Angle.GetCardinalDir()))
|
|
{
|
|
Cleanup();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Cleanup()
|
|
{
|
|
_beingBuilt[args.SenderSession].Remove(ev.Ack);
|
|
}
|
|
|
|
if (!_actionBlocker.CanInteract(user, null)
|
|
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.ActiveHandEntity == null)
|
|
{
|
|
Cleanup();
|
|
return;
|
|
}
|
|
|
|
var mapPos = location.ToMap(EntityManager, _transformSystem);
|
|
var predicate = GetPredicate(constructionPrototype.CanBuildInImpassable, mapPos);
|
|
|
|
if (!_interactionSystem.InRangeUnobstructed(user, mapPos, predicate: predicate))
|
|
{
|
|
Cleanup();
|
|
return;
|
|
}
|
|
|
|
if (pathFind == null)
|
|
throw new InvalidDataException($"Can't find path from starting node to target node in construction! Recipe: {ev.PrototypeName}");
|
|
|
|
var edge = startNode.GetEdge(pathFind[0].Name);
|
|
|
|
if(edge == null)
|
|
throw new InvalidDataException($"Can't find edge from starting node to the next node in pathfinding! Recipe: {ev.PrototypeName}");
|
|
|
|
var valid = false;
|
|
|
|
if (hands.ActiveHandEntity is not {Valid: true} holding)
|
|
{
|
|
Cleanup();
|
|
return;
|
|
}
|
|
|
|
// No support for conditions here!
|
|
|
|
foreach (var step in edge.Steps)
|
|
{
|
|
switch (step)
|
|
{
|
|
case EntityInsertConstructionGraphStep entityInsert:
|
|
if (entityInsert.EntityValid(holding, EntityManager, _factory))
|
|
valid = true;
|
|
break;
|
|
case ToolConstructionGraphStep _:
|
|
throw new InvalidDataException("Invalid first step for item recipe!");
|
|
}
|
|
|
|
if (valid)
|
|
break;
|
|
}
|
|
|
|
if (!valid)
|
|
{
|
|
Cleanup();
|
|
return;
|
|
}
|
|
|
|
if (await Construct(user,
|
|
(ev.Ack + constructionPrototype.GetHashCode()).ToString(),
|
|
constructionGraph,
|
|
edge,
|
|
targetNode,
|
|
GetCoordinates(ev.Location),
|
|
constructionPrototype.CanRotate ? ev.Angle : Angle.Zero) is not {Valid: true} structure)
|
|
{
|
|
Cleanup();
|
|
return;
|
|
}
|
|
|
|
RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack, GetNetEntity(structure)));
|
|
_adminLogger.Add(LogType.Construction, LogImpact.Low, $"{ToPrettyString(user):player} has turned a {ev.PrototypeName} construction ghost into {ToPrettyString(structure)} at {Transform(structure).Coordinates}");
|
|
Cleanup();
|
|
}
|
|
}
|
|
}
|