Files
tbd-station-14/Content.Server/GameObjects/EntitySystems/ConstructionSystem.cs
DrSmugleaf bda5ce655f Add the trash man (#1367)
* Add disposal.rsi

* Rename disposal resource to disposal.rsi and create basic components

* Add disposal nets

* Add pushing entities along the disposal network

* Add disposal unit

* Unregister disposable component

* Add flush and selfinsert verbs to disposal unit

* Add gradual disposals movement

* Fix being able to walk through space for a while after exiting disposals

* Multiply disposals speed by 10

And fix early returns when moving an entity

* Rename Disposable component to InDisposals

* Remove DisposalNet and add on anchor events

* Remove anchored events, moved to interfaces

* Code cleanup

* Fix adjacent tubes' connections when a tube connects

* Fix jittery movement in disposals

* Remove Logger.Debug call

* Move disposals updates to InDisposalsComponent

* Fix adjacent connection valid directions check

* Disposal tubes now throw you out where they are facing

* Add disposal unit exit cooldown

* Set different disposal pipe sprite state depending on anchored value

* Add recycler

* Add recycler animation

* Add bloody texture to the recycler when grinding a living being

* Add PowerDevice component to the disposal unit

* Made the Recycler center on the grid

* Add disposal junction

* Add picking a random direction if junction is entered from the output side

* Add disposal flush and clang sounds

Taken from VGStation

* Move disposal flush and clang sound file names to exposedata

* Add disposalsmap.yml to test with

* Add summaries to DisposalUnit fields

* Add sideDegrees yaml property to disposal junctions

* Fix outdated usings

* Add conveyor resources

* Update RobustToolbox

* More merge fixes

Add conveyor collision masks

* Add ConveyorComponent

* Fix crash when reentering a body

* Merge branch 'master' into disposals-1147

* Reduce recycler bounds, set hard to false, add summary and expose "safe" to yaml

* Move IAnchored and IUnAnchored to AnchorableComponent

* Update power components and remove old disposals map

* Remove redundant sprite layers

* Add tile pry command

* Fix tilepry command

* Fix DisposalJunctionComponent missing a component reference

* Add anchor by radius command

* Add Y-Junctions

* Add disposal bend

* Add unanchor command

* Change DisposalJunction prototypes to specify their angles

* Fix disposal units being hidden below the floor

* Removed IAnhored and IUnAnchored interfaces

* Replace CanBeNull annotation with nullable reference types

* Update showwires command

* Add recycler recycling items

* Added angle and speed properties to ConveyorComponent

* Fix conveyort textures

* Add animation to the disposal unit

* Fix anchor and unanchor commands sometimes not finding any entities

* Fix not reading flush_time from disposal unit prototype

* Fix merge conflict wrong using

* Fix disposal, recycling and conveyor texture paths

Delete diverters

* Update visualizer names

* Add DisposableComponent, change drag and drop to work with multiple components

Ignoreinsideblocker client side for drag and drops, like on the server
Add more comments

* Add conveyor belts properly moving entities on top

* Anchorr wires

* Change conveyor bounds to 0.49

* Anchor catwalks, airlocks, gravity generators, low walls, wires and windows

* Add starting/stopping conveyors

* Add reversed conveyors

* Add conveyor switches

* Move InDisposalsComponent code to DisposableComponent

* Add ExitVector method to tubes

* Fix not updating tube references when disconnecting one

* Replace IoCManager call with dependency

* Add tubes disconnecting if they move too far apart from one another

* Move disposals action blocking to shared

* Add rotating and flipping pipes

* Make conveyor intersection calculations approximate

* Fix 1% chance of the server crashing when initializing the map

Happens when emergency lockers remove themselves

* Add disposal unit interface

* Make disposal units refuse items if not powered

* Make disposal tubes hide only when anchored

* Make disposal junction arrows visible to mere mortals

* Add disposal tubes breaking

* Add tubeconnections command

* Add missing verb attribute

* Add flipped disposal junction

* Add ids and linking to conveyors and switches

* Add conveyor switch prying and placing

* Add anchoring conveyor switches and refactor placing them

* Add missing serializable attributes from DisposableComponentState

* Make conveyor speed VV ReadWrite

* Change drawdepth of conveyors to FloorObjects

* Make conveyor anchored check consistent

* Remove anchoring interaction from switches

* Add conveyor switch id syncing and move switches slightly when pried

* Make entities in containers not able to be moved by conveyors

* Add conveyor and switches loose textures

* Merge conflict fixes

* Add disposal unit test

* Add flushing test to disposal unit test

* Add disposal unit flush fail test

* Add disposals to the saltern map

* Fix saltern disposal junctions

* Add power checks to the recycler

* Fix disposal unit placement in maintenance closet

* Remove disposal junctions from saltern

* Readd junctions to saltern

* Add the chemmaster to saltern at the request of Ike

* Move the chemistry disposal unit

* Fix casing of disposal flush sound

* More merge conflict fixes

* Fix a compiler warning.

* Remove popup invocation from buckle

* Remove showPopup parameter from InteractionChecks

* Remove unnecessary physics components

Fixes the physics system dying

* Replace PhysicsComponent usages with CollidableComponent

* Update existing code for the new controller system

* Change conveyors to use a VirtualController instead of teleporting the entity

* Remove visualizer 2d suffix and update physics code

* Transition code to new controller system

* Fix shuttles not moving

* Fix throwing

* Fix guns

* Change hands to use physics.Stop() and remove item fumble method

* Add syncing conveyor switches states

* Fix the recycler wanting to be a conveyor too hard

* Fix showwires > showsubfloor rename in mapping command

* Fix wifi air conveyors

* Fix test error

* Add showsubfloorforever command

Changes drawdepth of the relevant entities

* Disable opening the disposal unit interface while inside

* Add closing the disposal unit interface when getting inside

* Add closing the interface when the disposal unit component is removed

* Add removing entities on disposal unit component removal

* Delay disposal unit flush and fix serialization

* Implement pressure in disposal units

* Fix chain engaging a disposal unit

* Implement states to the disposal unit

* Fix missing imports from merge conflict

* Update Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* Address some reviews

* Fix za buildo

* Use container helper to detach disposables

* Make conveyors use the construction system

* Make conveyor groups and syncing sane

* Make flip flip

brave

* Add activate interface to conveyor switches

* Fix not removing the switch from its group when it's deleted

* Fix not registering conveyors and switches on initialize

* Stop using 0 as null

* Disconnect conveyors and switches when disposing of a group

* Make disposal units not able to be exited when flushing

* Make disposal units flush after a configurable 30 seconds

* Add handle and light layers to the disposal unit

* Merge engaging and flushing

* Update saltern.yml

* I love using 0 as null

* Make disposal unit visual layers make sense

* Remove duplicate remove method in disposal units and update light

* Replace DisposableComponent with disposal holders

* Fix disposal holders deleting their contents on deletion

* Account for disposal unit pressure in tests and make a failed flush autoengage

* Rename disposable to holder

* Fix junction connections

* Disable self insert and flush verbs when inside a disposal unit

* Fix spamming the engage button making the animation reset

* Make the recycler take materials into account properly

Fix cablestack1 not existing

* Merge conflict fixes

* Fix pipes not being saved anchored

* Change conveyors and groups to not use an id

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2020-07-30 23:45:28 +02:00

584 lines
23 KiB
C#

using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Construction;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Utility;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.Interfaces.GameObjects.Components;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// The server-side implementation of the construction system, which is used for constructing entities in game.
/// </summary>
[UsedImplicitly]
internal class ConstructionSystem : Shared.GameObjects.EntitySystems.ConstructionSystem
{
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly IMapManager _mapManager;
#pragma warning restore 649
private readonly Dictionary<string, ConstructionPrototype> _craftRecipes = new Dictionary<string, ConstructionPrototype>();
public IReadOnlyDictionary<string, ConstructionPrototype> CraftRecipes => _craftRecipes;
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
foreach (var prototype in _prototypeManager.EnumeratePrototypes<ConstructionPrototype>())
{
_craftRecipes.Add(prototype.Result, prototype);
}
SubscribeNetworkEvent<TryStartStructureConstructionMessage>(HandleStartStructureConstruction);
SubscribeNetworkEvent<TryStartItemConstructionMessage>(HandleStartItemConstruction);
SubscribeLocalEvent<AfterInteractMessage>(HandleToolInteraction);
}
private void HandleStartStructureConstruction(TryStartStructureConstructionMessage msg, EntitySessionEventArgs args)
{
var placingEnt = args.SenderSession.AttachedEntity;
var result = TryStartStructureConstruction(placingEnt, msg.Location, msg.PrototypeName, msg.Angle);
if (!result) return;
var responseMsg = new AckStructureConstructionMessage(msg.Ack);
var channel = ((IPlayerSession) args.SenderSession).ConnectedClient;
RaiseNetworkEvent(responseMsg, channel);
}
private void HandleStartItemConstruction(TryStartItemConstructionMessage msg, EntitySessionEventArgs args)
{
var placingEnt = args.SenderSession.AttachedEntity;
TryStartItemConstruction(placingEnt, msg.PrototypeName);
}
private void HandleToolInteraction(AfterInteractMessage msg)
{
if(msg.Handled)
return;
// You can only construct/deconstruct things within reach
if(!msg.CanReach)
return;
var targetEnt = msg.Attacked;
var handEnt = msg.ItemInHand;
// A tool has to interact with an entity.
if(targetEnt is null || handEnt is null)
return;
var interaction = Get<InteractionSystem>();
if(!interaction.InRangeUnobstructed(handEnt.Transform.MapPosition, targetEnt.Transform.MapPosition, ignoredEnt: targetEnt, ignoreInsideBlocker: true))
return;
// Cannot deconstruct an entity with no prototype.
var targetPrototype = targetEnt.MetaData.EntityPrototype;
if (targetPrototype is null)
return;
// the target entity is in the process of being constructed/deconstructed
if (msg.Attacked.TryGetComponent<ConstructionComponent>(out var constructComp))
{
var result = TryConstructEntity(constructComp, handEnt, msg.User);
// TryConstructEntity may delete the existing entity
msg.Handled = result;
}
else // try to start the deconstruction process
{
// A tool was not used on the entity.
if (!handEnt.TryGetComponent<IToolComponent>(out var toolComp))
return;
// no known recipe for entity
if (!_craftRecipes.TryGetValue(targetPrototype.ID, out var prototype))
return;
// there is a recipe, but it can't be deconstructed.
var lastStep = prototype.Stages[^1].Backward;
if (!(lastStep is ConstructionStepTool))
return;
// wrong tool
var caps = ((ConstructionStepTool) lastStep).ToolQuality;
if ((toolComp.Qualities & caps) == 0)
return;
// ask around and see if the deconstruction prerequisites are satisfied
// (remove bulbs, approved access, open panels, etc)
var deconCompMsg = new BeginDeconstructCompMsg(msg.User);
targetEnt.SendMessage(null, deconCompMsg);
if(deconCompMsg.BlockDeconstruct)
return;
var deconEntMsg = new BeginDeconstructEntityMsg(msg.User, handEnt, targetEnt);
RaiseLocalEvent(deconEntMsg);
if(deconEntMsg.BlockDeconstruct)
return;
// --- GOOD TO GO ---
msg.Handled = true;
// pop off the material and switch to frame
var targetEntPos = targetEnt.Transform.MapPosition;
if (prototype.Stages.Count <= 2) // there are no intermediate stages
{
targetEnt.Delete();
SpawnIngredient(targetEntPos, prototype.Stages[(prototype.Stages.Count - 2)].Forward as ConstructionStepMaterial);
}
else // replace ent with intermediate
{
// Spawn frame
var frame = SpawnCopyTransform("structureconstructionframe", targetEnt.Transform);
var construction = frame.GetComponent<ConstructionComponent>();
SetupComponent(construction, prototype);
construction.Stage = prototype.Stages.Count - 2;
SetupDeconIntermediateSprite(construction, prototype);
frame.Transform.LocalRotation = targetEnt.Transform.LocalRotation;
if (targetEnt.Prototype.Components.TryGetValue("Item", out var itemProtoComp))
{
if(frame.HasComponent<ItemComponent>())
frame.RemoveComponent<ItemComponent>();
var itemComp = frame.AddComponent<ItemComponent>();
var serializer = YamlObjectSerializer.NewReader(itemProtoComp);
itemComp.ExposeData(serializer);
}
ReplaceInContainerOrGround(targetEnt, frame);
// remove target
targetEnt.Delete();
// spawn material
SpawnIngredient(targetEntPos, prototype.Stages[(prototype.Stages.Count-2)].Forward as ConstructionStepMaterial);
}
}
}
private IEntity SpawnCopyTransform(string prototypeId, ITransformComponent toReplace)
{
var frame = EntityManager.SpawnEntity(prototypeId, toReplace.MapPosition);
frame.Transform.WorldRotation = toReplace.WorldRotation;
frame.Transform.ParentUid = toReplace.ParentUid;
return frame;
}
private static void SetupDeconIntermediateSprite(ConstructionComponent constructionComponent, ConstructionPrototype prototype)
{
if(!constructionComponent.Owner.TryGetComponent<SpriteComponent>(out var spriteComp))
return;
for (var i = prototype.Stages.Count - 1; i >= 0; i--)
{
if (prototype.Stages[i].Icon != null)
{
spriteComp.AddLayerWithSprite(prototype.Stages[1].Icon);
return;
}
}
spriteComp.AddLayerWithSprite(prototype.Icon);
}
public void SpawnIngredient(MapCoordinates position, ConstructionStepMaterial lastStep)
{
if(lastStep is null)
return;
var material = lastStep.Material;
var quantity = lastStep.Amount;
var matEnt = EntityManager.SpawnEntity(MaterialPrototypes[material], position);
if (matEnt.TryGetComponent<StackComponent>(out var stackComp))
{
stackComp.Count = quantity;
}
else
{
quantity--; // already spawned one above
while (quantity > 0)
{
EntityManager.SpawnEntity(MaterialPrototypes[material], position);
quantity--;
}
}
}
private static readonly Dictionary<ConstructionStepMaterial.MaterialType, string> MaterialPrototypes =
new Dictionary<ConstructionStepMaterial.MaterialType, string>
{
{ ConstructionStepMaterial.MaterialType.Cable, "CableStack1" },
{ ConstructionStepMaterial.MaterialType.Gold, "GoldStack1" },
{ ConstructionStepMaterial.MaterialType.Metal, "SteelSheet1" },
{ ConstructionStepMaterial.MaterialType.Glass, "GlassSheet1" }
};
private bool TryStartStructureConstruction(IEntity placingEnt, GridCoordinates loc, string prototypeName, Angle angle)
{
var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName);
if (!InteractionChecks.InRangeUnobstructed(placingEnt, loc.ToMap(_mapManager),
ignoredEnt: placingEnt, ignoreInsideBlocker: prototype.CanBuildInImpassable))
{
return false;
}
if (prototype.Stages.Count < 2)
{
throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages.");
}
var stage0 = prototype.Stages[0];
if (!(stage0.Forward is ConstructionStepMaterial matStep))
{
throw new NotImplementedException();
}
// Try to find the stack with the material in the user's hand.
if(!placingEnt.TryGetComponent<HandsComponent>(out var hands))
{
return false;
};
var activeHand = hands.GetActiveHand?.Owner;
if (activeHand == null)
{
return false;
}
if (!activeHand.TryGetComponent(out StackComponent stack) || !MaterialStackValidFor(matStep, stack))
{
return false;
}
if (!stack.Use(matStep.Amount))
{
return false;
}
// OK WE'RE GOOD CONSTRUCTION STARTED.
Get<AudioSystem>().PlayAtCoords("/Audio/Items/deconstruct.ogg", loc);
if (prototype.Stages.Count == 2)
{
// Exactly 2 stages, so don't make an intermediate frame.
var ent = EntityManager.SpawnEntity(prototype.Result, loc);
ent.Transform.LocalRotation = angle;
}
else
{
var frame = EntityManager.SpawnEntity("structureconstructionframe", loc);
var construction = frame.GetComponent<ConstructionComponent>();
SetupComponent(construction, prototype);
frame.Transform.LocalRotation = angle;
}
return true;
}
private void TryStartItemConstruction(IEntity placingEnt, string prototypeName)
{
var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName);
if (prototype.Stages.Count < 2)
{
throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages.");
}
var stage0 = prototype.Stages[0];
if (!(stage0.Forward is ConstructionStepMaterial matStep))
{
throw new NotImplementedException();
}
// Try to find the stack with the material in the user's hand.
var hands = placingEnt.GetComponent<HandsComponent>();
var activeHand = hands.GetActiveHand?.Owner;
if (activeHand == null)
{
return;
}
if (!activeHand.TryGetComponent(out StackComponent stack) || !MaterialStackValidFor(matStep, stack))
{
return;
}
if (!stack.Use(matStep.Amount))
{
return;
}
// OK WE'RE GOOD CONSTRUCTION STARTED.
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Items/deconstruct.ogg", placingEnt);
if (prototype.Stages.Count == 2)
{
// Exactly 2 stages, so don't make an intermediate frame.
var ent = SpawnCopyTransform(prototype.Result, placingEnt.Transform);
hands.PutInHandOrDrop(ent.GetComponent<ItemComponent>());
}
else
{
var frame = SpawnCopyTransform("structureconstructionframe", placingEnt.Transform);
var construction = frame.GetComponent<ConstructionComponent>();
SetupComponent(construction, prototype);
var finalPrototype = _prototypeManager.Index<EntityPrototype>(prototype.Result);
if (finalPrototype.Components.TryGetValue("Item", out var itemProtoComp))
{
if(frame.HasComponent<ItemComponent>())
frame.RemoveComponent<ItemComponent>();
var itemComp = frame.AddComponent<ItemComponent>();
var serializer = YamlObjectSerializer.NewReader(itemProtoComp);
itemComp.ExposeData(serializer);
hands.PutInHandOrDrop(itemComp);
}
}
}
private bool TryConstructEntity(ConstructionComponent constructionComponent, IEntity handTool, IEntity user)
{
var constructEntity = constructionComponent.Owner;
var spriteComponent = constructEntity.GetComponent<SpriteComponent>();
var transformComponent = constructEntity.GetComponent<ITransformComponent>();
// default interaction check for AttackBy allows inside blockers, so we will check if its blocked if
// we're not allowed to build on impassable stuff
var constructPrototype = constructionComponent.Prototype;
if (constructPrototype.CanBuildInImpassable == false)
{
if (!InteractionChecks.InRangeUnobstructed(user, constructEntity.Transform.MapPosition))
return false;
}
var stage = constructPrototype.Stages[constructionComponent.Stage];
if (TryProcessStep(constructEntity, stage.Forward, handTool, user, transformComponent.GridPosition))
{
constructionComponent.Stage++;
if (constructionComponent.Stage == constructPrototype.Stages.Count - 1)
{
// Oh boy we get to finish construction!
var ent = SpawnCopyTransform(constructPrototype.Result, transformComponent);
ent.Transform.LocalRotation = transformComponent.LocalRotation;
ReplaceInContainerOrGround(constructEntity, ent);
constructEntity.Delete();
return true;
}
stage = constructPrototype.Stages[constructionComponent.Stage];
if (stage.Icon != null)
{
spriteComponent.LayerSetSprite(0, stage.Icon);
}
}
else if (TryProcessStep(constructEntity, stage.Backward, handTool, user, transformComponent.GridPosition))
{
constructionComponent.Stage--;
stage = constructPrototype.Stages[constructionComponent.Stage];
// If forward needed a material, drop it
SpawnIngredient(constructEntity.Transform.MapPosition, stage.Forward as ConstructionStepMaterial);
if (constructionComponent.Stage == 0)
{
// Deconstruction complete.
constructEntity.Delete();
return true;
}
if (stage.Icon != null)
{
spriteComponent.LayerSetSprite(0, stage.Icon);
}
}
return true;
}
private static void ReplaceInContainerOrGround(IEntity oldEntity, IEntity newEntity)
{
var parentEntity = oldEntity.Transform.Parent?.Owner;
if (!(parentEntity is null) && parentEntity.TryGetComponent<IContainerManager>(out var containerMan))
{
if (containerMan.TryGetContainer(oldEntity, out var container))
{
container.ForceRemove(oldEntity);
container.Insert(newEntity);
}
}
}
private bool TryProcessStep(IEntity constructEntity, ConstructionStep step, IEntity slapped, IEntity user, GridCoordinates gridCoords)
{
if (step == null)
{
return false;
}
var sound = EntitySystemManager.GetEntitySystem<AudioSystem>();
switch (step)
{
case ConstructionStepMaterial matStep:
if (!slapped.TryGetComponent(out StackComponent stack)
|| !MaterialStackValidFor(matStep, stack)
|| !stack.Use(matStep.Amount))
{
return false;
}
if (matStep.Material == ConstructionStepMaterial.MaterialType.Cable)
sound.PlayAtCoords("/Audio/items/zip.ogg", gridCoords);
else
sound.PlayAtCoords("/Audio/items/deconstruct.ogg", gridCoords);
return true;
case ConstructionStepTool toolStep:
if (!slapped.TryGetComponent<ToolComponent>(out var tool))
return false;
// Handle welder manually since tool steps specify fuel amount needed, for some reason.
if (toolStep.ToolQuality.HasFlag(ToolQuality.Welding))
return slapped.TryGetComponent<WelderComponent>(out var welder)
&& welder.UseTool(user, constructEntity, toolStep.ToolQuality, toolStep.Amount);
return tool.UseTool(user, constructEntity, toolStep.ToolQuality);
default:
throw new NotImplementedException();
}
}
// Really this should check the actual materials at play..
private static readonly Dictionary<StackType, ConstructionStepMaterial.MaterialType> StackTypeMap
= new Dictionary<StackType, ConstructionStepMaterial.MaterialType>
{
{ StackType.Cable, ConstructionStepMaterial.MaterialType.Cable },
{ StackType.Gold, ConstructionStepMaterial.MaterialType.Gold },
{ StackType.Glass, ConstructionStepMaterial.MaterialType.Glass },
{ StackType.Metal, ConstructionStepMaterial.MaterialType.Metal }
};
private static bool MaterialStackValidFor(ConstructionStepMaterial step, StackComponent stack)
{
return StackTypeMap.TryGetValue((StackType)stack.StackType, out var should) && should == step.Material;
}
private void SetupComponent(ConstructionComponent constructionComponent, ConstructionPrototype prototype)
{
constructionComponent.Prototype = prototype;
constructionComponent.Stage = 1;
var spriteComp = constructionComponent.Owner.GetComponent<SpriteComponent>();
if(prototype.Stages[1].Icon != null)
{
spriteComp.AddLayerWithSprite(prototype.Stages[1].Icon);
}
else
{
spriteComp.AddLayerWithSprite(prototype.Icon);
}
var frame = constructionComponent.Owner;
var finalPrototype = _prototypeManager.Index<EntityPrototype>(prototype.Result);
frame.Name = $"Unfinished {finalPrototype.Name}";
}
}
/// <summary>
/// A system message that is raised when an entity is trying to be deconstructed.
/// </summary>
public class BeginDeconstructEntityMsg : EntitySystemMessage
{
/// <summary>
/// Entity that initiated the deconstruction.
/// </summary>
public IEntity User { get; }
/// <summary>
/// Tool in the active hand of the user.
/// </summary>
public IEntity Hand { get; }
/// <summary>
/// Target entity that is trying to be deconstructed.
/// </summary>
public IEntity Target { get; }
/// <summary>
/// Set this to true if you would like to block the deconstruction from happening.
/// </summary>
public bool BlockDeconstruct { get; set; }
/// <summary>
/// Constructs a new instance of <see cref="BeginDeconstructEntityMsg"/>.
/// </summary>
/// <param name="user">Entity that initiated the deconstruction.</param>
/// <param name="hand">Tool in the active hand of the user.</param>
/// <param name="target">Target entity that is trying to be deconstructed.</param>
public BeginDeconstructEntityMsg(IEntity user, IEntity hand, IEntity target)
{
User = user;
Hand = hand;
Target = target;
}
}
/// <summary>
/// A component message that is raised when an entity is trying to be deconstructed.
/// </summary>
public class BeginDeconstructCompMsg : ComponentMessage
{
/// <summary>
/// Entity that initiated the deconstruction.
/// </summary>
public IEntity User { get; }
/// <summary>
/// Set this to true if you would like to block the deconstruction from happening.
/// </summary>
public bool BlockDeconstruct { get; set; }
/// <summary>
/// Constructs a new instance of <see cref="BeginDeconstructCompMsg"/>.
/// </summary>
/// <param name="user">Entity that initiated the deconstruction.</param>
public BeginDeconstructCompMsg(IEntity user)
{
User = user;
}
}
}