Exosuit: Ripley (#12668)
* mechs * interaction relay * atmos handling * fuck around with interaction events SPAGHETTI CODE OH MY GOD * more sprites and whatever the hell * more mech shit * more shit for equipment * starting equipment (for nukie mechs and such) * equipment cycling * starting with some of the ui * a fat chunk of ui prototyping * done tinkering with ui * a bunch of ui stuff and what have yous * cleaning up grabber and state handling * make the ui actually functional + watch me port a million icons I swear i'll prune the sprites later blease * start on construction * construction yo mamma * remove some unused files * fix a silly * make the graph sane * make it actually constructible. * print the boards as well, bozo * rebalance part prices * eject action also i appease the russians by remembering to localize * Punch Shit * make mech integrity and repairs work * Make the UI more based STOMP STOMP STOMP STOMP * make equipment even more based * batteries and other such delights * make the ui look pimpin af * make the construction mega based * UI but so epic * equipment * some sweat tweaks * damage rebalancing * restructure tech * fix some shit * mechs inherit access * make icons actually use sprite specifiers * TRAILING COMMAA!!!!! * fix a mild indentation sin * undo this change because it isn't needed * actually fix this * secret webeditting shhhh * place this tech here * comments * foo
This commit is contained in:
26
Content.Shared/Mech/Components/MechPilotComponent.cs
Normal file
26
Content.Shared/Mech/Components/MechPilotComponent.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Mech.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Attached to entities piloting a <see cref="SharedMechComponent"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get in the robot, Shinji
|
||||
/// </remarks>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class MechPilotComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The mech being piloted
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityUid Mech;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MechPilotComponentState : ComponentState
|
||||
{
|
||||
public EntityUid Mech;
|
||||
}
|
||||
132
Content.Shared/Mech/Components/SharedMechComponent.cs
Normal file
132
Content.Shared/Mech/Components/SharedMechComponent.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Mech.Components;
|
||||
|
||||
/// <summary>
|
||||
/// A large, pilotable machine that has equipment that is
|
||||
/// powered via an internal battery.
|
||||
/// </summary>
|
||||
public abstract class SharedMechComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How much "health" the mech has left.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public FixedPoint2 Integrity;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of damage the mech can take.
|
||||
/// </summary>
|
||||
[DataField("maxIntegrity")]
|
||||
public FixedPoint2 MaxIntegrity = 250;
|
||||
|
||||
/// <summary>
|
||||
/// How much energy the mech has.
|
||||
/// Derived from the currently inserted battery.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public FixedPoint2 Energy = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of energy the mech can have.
|
||||
/// Derived from the currently inserted battery.
|
||||
/// </summary>
|
||||
[DataField("maxEnergy")]
|
||||
public FixedPoint2 MaxEnergy = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The slot the battery is stored in.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ContainerSlot BatterySlot = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public readonly string BatterySlotId = "mech-battery-slot";
|
||||
|
||||
/// <summary>
|
||||
/// A multiplier used to calculate how much of the damage done to a mech
|
||||
/// is transfered to the pilot
|
||||
/// </summary>
|
||||
[DataField("mechToPilotDamageMultiplier")]
|
||||
public float MechToPilotDamageMultiplier;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the mech has been destroyed and is no longer pilotable.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Broken = false;
|
||||
|
||||
/// <summary>
|
||||
/// The slot the pilot is stored in.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ContainerSlot PilotSlot = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public readonly string PilotSlotId = "mech-pilot-slot";
|
||||
|
||||
/// <summary>
|
||||
/// The current selected equipment of the mech.
|
||||
/// If null, the mech is using just its fists.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid? CurrentSelectedEquipment;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of equipment items that can be installed in the mech
|
||||
/// </summary>
|
||||
[DataField("maxEquipmentAmount")]
|
||||
public int MaxEquipmentAmount = 3;
|
||||
|
||||
/// <summary>
|
||||
/// A whitelist for inserting equipment items.
|
||||
/// </summary>
|
||||
[DataField("equipmentWhitelist")]
|
||||
public EntityWhitelist? EquipmentWhitelist;
|
||||
|
||||
/// <summary>
|
||||
/// A container for storing the equipment entities.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Container EquipmentContainer = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public readonly string EquipmentContainerId = "mech-equipment-container";
|
||||
|
||||
#region Action Prototypes
|
||||
[DataField("mechCycleAction", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||
public string MechCycleAction = "MechCycleEquipment";
|
||||
[DataField("mechUiAction", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||
public string MechUiAction = "MechOpenUI";
|
||||
[DataField("mechEjectAction", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||
public string MechEjectAction = "MechEject";
|
||||
#endregion
|
||||
|
||||
#region Visualizer States
|
||||
[DataField("baseState")]
|
||||
public string? BaseState;
|
||||
[DataField("openState")]
|
||||
public string? OpenState;
|
||||
[DataField("brokenState")]
|
||||
public string? BrokenState;
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains network state for <see cref="SharedMechComponent"/>.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MechComponentState : ComponentState
|
||||
{
|
||||
public FixedPoint2 Integrity;
|
||||
public FixedPoint2 MaxIntegrity;
|
||||
public FixedPoint2 Energy;
|
||||
public FixedPoint2 MaxEnergy;
|
||||
public EntityUid? CurrentSelectedEquipment;
|
||||
public bool Broken;
|
||||
}
|
||||
463
Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
Normal file
463
Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
Normal file
@@ -0,0 +1,463 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Mech.Components;
|
||||
using Content.Shared.Mech.Equipment.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Mech.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles all of the interactions, UI handling, and items shennanigans for <see cref="SharedMechComponent"/>
|
||||
/// </summary>
|
||||
public abstract class SharedMechSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _access = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly SharedMoverController _mover = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<SharedMechComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<SharedMechComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeLocalEvent<MechPilotComponent, ComponentGetState>(OnPilotGetState);
|
||||
SubscribeLocalEvent<MechPilotComponent, ComponentHandleState>(OnPilotHandleState);
|
||||
|
||||
SubscribeLocalEvent<SharedMechComponent, MechToggleEquipmentEvent>(OnToggleEquipmentAction);
|
||||
SubscribeLocalEvent<SharedMechComponent, MechEjectPilotEvent>(OnEjectPilotEvent);
|
||||
SubscribeLocalEvent<SharedMechComponent, InteractNoHandEvent>(RelayInteractionEvent);
|
||||
SubscribeLocalEvent<SharedMechComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<SharedMechComponent, DestructionEventArgs>(OnDestruction);
|
||||
SubscribeLocalEvent<SharedMechComponent, GetAdditionalAccessEvent>(OnGetAdditionalAccess);
|
||||
|
||||
SubscribeLocalEvent<MechPilotComponent, GetMeleeWeaponEvent>(OnGetMeleeWeapon);
|
||||
SubscribeLocalEvent<MechPilotComponent, CanAttackFromContainerEvent>(OnCanAttackFromContainer);
|
||||
}
|
||||
|
||||
#region State Handling
|
||||
private void OnGetState(EntityUid uid, SharedMechComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new MechComponentState
|
||||
{
|
||||
Integrity = component.Integrity,
|
||||
MaxIntegrity = component.MaxIntegrity,
|
||||
Energy = component.Energy,
|
||||
MaxEnergy = component.MaxEnergy,
|
||||
CurrentSelectedEquipment = component.CurrentSelectedEquipment,
|
||||
Broken = component.Broken
|
||||
};
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, SharedMechComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not MechComponentState state)
|
||||
return;
|
||||
|
||||
component.Integrity = state.Integrity;
|
||||
component.MaxIntegrity = state.MaxIntegrity;
|
||||
component.Energy = state.Energy;
|
||||
component.MaxEnergy = state.MaxEnergy;
|
||||
component.CurrentSelectedEquipment = state.CurrentSelectedEquipment;
|
||||
component.Broken = state.Broken;
|
||||
}
|
||||
|
||||
private void OnPilotGetState(EntityUid uid, MechPilotComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new MechPilotComponentState
|
||||
{
|
||||
Mech = component.Mech
|
||||
};
|
||||
}
|
||||
|
||||
private void OnPilotHandleState(EntityUid uid, MechPilotComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not MechPilotComponentState state)
|
||||
return;
|
||||
|
||||
component.Mech = state.Mech;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void OnToggleEquipmentAction(EntityUid uid, SharedMechComponent component, MechToggleEquipmentEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = true;
|
||||
CycleEquipment(uid);
|
||||
}
|
||||
|
||||
private void OnEjectPilotEvent(EntityUid uid, SharedMechComponent component, MechEjectPilotEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = true;
|
||||
TryEject(uid, component);
|
||||
}
|
||||
|
||||
private void RelayInteractionEvent(EntityUid uid, SharedMechComponent component, InteractNoHandEvent args)
|
||||
{
|
||||
var pilot = component.PilotSlot.ContainedEntity;
|
||||
if (pilot == null)
|
||||
return;
|
||||
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
if (component.CurrentSelectedEquipment != null)
|
||||
{
|
||||
RaiseLocalEvent(component.CurrentSelectedEquipment.Value, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, SharedMechComponent component, ComponentStartup args)
|
||||
{
|
||||
component.PilotSlot = _container.EnsureContainer<ContainerSlot>(uid, component.PilotSlotId);
|
||||
component.EquipmentContainer = _container.EnsureContainer<Container>(uid, component.EquipmentContainerId);
|
||||
component.BatterySlot = _container.EnsureContainer<ContainerSlot>(uid, component.BatterySlotId);
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
private void OnDestruction(EntityUid uid, SharedMechComponent component, DestructionEventArgs args)
|
||||
{
|
||||
BreakMech(uid, component);
|
||||
}
|
||||
|
||||
private void OnGetAdditionalAccess(EntityUid uid, SharedMechComponent component, ref GetAdditionalAccessEvent args)
|
||||
{
|
||||
var pilot = component.PilotSlot.ContainedEntity;
|
||||
if (pilot == null)
|
||||
return;
|
||||
|
||||
args.Entities.Add(pilot.Value);
|
||||
_access.FindAccessItemsInventory(pilot.Value, out var items);
|
||||
args.Entities = args.Entities.Union(items).ToHashSet();
|
||||
}
|
||||
|
||||
private void SetupUser(EntityUid mech, EntityUid pilot, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(mech, ref component))
|
||||
return;
|
||||
|
||||
var rider = EnsureComp<MechPilotComponent>(pilot);
|
||||
var relay = EnsureComp<RelayInputMoverComponent>(pilot);
|
||||
var irelay = EnsureComp<InteractionRelayComponent>(pilot);
|
||||
|
||||
_mover.SetRelay(pilot, mech, relay);
|
||||
_interaction.SetRelay(pilot, mech, irelay);
|
||||
rider.Mech = mech;
|
||||
Dirty(rider);
|
||||
|
||||
_actions.AddAction(pilot, new InstantAction(_prototype.Index<InstantActionPrototype>(component.MechCycleAction)), mech);
|
||||
_actions.AddAction(pilot, new InstantAction(_prototype.Index<InstantActionPrototype>(component.MechUiAction)), mech);
|
||||
_actions.AddAction(pilot, new InstantAction(_prototype.Index<InstantActionPrototype>(component.MechEjectAction)), mech);
|
||||
}
|
||||
|
||||
private void RemoveUser(EntityUid mech, EntityUid pilot)
|
||||
{
|
||||
if (!RemComp<MechPilotComponent>(pilot))
|
||||
return;
|
||||
RemComp<RelayInputMoverComponent>(pilot);
|
||||
RemComp<InteractionRelayComponent>(pilot);
|
||||
|
||||
_actions.RemoveProvidedActions(pilot, mech);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the mech, removing the user and ejecting all installed equipment.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="component"></param>
|
||||
public virtual void BreakMech(EntityUid uid, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
TryEject(uid, component);
|
||||
var equipment = new List<EntityUid>(component.EquipmentContainer.ContainedEntities);
|
||||
foreach (var ent in equipment)
|
||||
{
|
||||
RemoveEquipment(uid, ent, component, forced: true);
|
||||
}
|
||||
|
||||
component.Broken = true;
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cycles through the currently selected equipment.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="component"></param>
|
||||
public void CycleEquipment(EntityUid uid, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var allEquipment = component.EquipmentContainer.ContainedEntities.ToList();
|
||||
|
||||
var equipmentIndex = -1;
|
||||
if (component.CurrentSelectedEquipment != null)
|
||||
{
|
||||
bool StartIndex(EntityUid u) => u == component.CurrentSelectedEquipment;
|
||||
equipmentIndex = allEquipment.FindIndex(StartIndex);
|
||||
}
|
||||
|
||||
equipmentIndex++;
|
||||
component.CurrentSelectedEquipment = equipmentIndex >= allEquipment.Count
|
||||
? null
|
||||
: allEquipment[equipmentIndex];
|
||||
|
||||
var popupString = component.CurrentSelectedEquipment != null
|
||||
? Loc.GetString("mech-equipment-select-popup", ("item", component.CurrentSelectedEquipment))
|
||||
: Loc.GetString("mech-equipment-select-none-popup");
|
||||
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
_popup.PopupEntity(popupString, uid, Filter.Pvs(uid));
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an equipment item into the mech.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="toInsert"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="equipmentComponent"></param>
|
||||
public void InsertEquipment(EntityUid uid, EntityUid toInsert, SharedMechComponent? component = null, MechEquipmentComponent? equipmentComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (!Resolve(toInsert, ref equipmentComponent))
|
||||
return;
|
||||
|
||||
if (component.EquipmentContainer.ContainedEntities.Count >= component.MaxEquipmentAmount)
|
||||
return;
|
||||
|
||||
if (component.EquipmentWhitelist != null && !component.EquipmentWhitelist.IsValid(uid))
|
||||
return;
|
||||
|
||||
equipmentComponent.EquipmentOwner = uid;
|
||||
component.EquipmentContainer.Insert(toInsert, EntityManager);
|
||||
var ev = new MechEquipmentInsertedEvent(uid);
|
||||
RaiseLocalEvent(toInsert, ref ev);
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an equipment item from a mech.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="toRemove"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="equipmentComponent"></param>
|
||||
/// <param name="forced">Whether or not the removal can be cancelled</param>
|
||||
public void RemoveEquipment(EntityUid uid, EntityUid toRemove, SharedMechComponent? component = null, MechEquipmentComponent? equipmentComponent = null, bool forced = false)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (!Resolve(toRemove, ref equipmentComponent))
|
||||
return;
|
||||
|
||||
if (!forced)
|
||||
{
|
||||
var attemptev = new AttemptRemoveMechEquipmentEvent();
|
||||
RaiseLocalEvent(toRemove, ref attemptev);
|
||||
if (attemptev.Cancelled)
|
||||
return;
|
||||
}
|
||||
|
||||
equipmentComponent.EquipmentOwner = null;
|
||||
component.EquipmentContainer.Remove(toRemove, EntityManager);
|
||||
var ev = new MechEquipmentRemovedEvent(uid);
|
||||
RaiseLocalEvent(toRemove, ref ev);
|
||||
|
||||
if (component.CurrentSelectedEquipment == toRemove)
|
||||
CycleEquipment(uid, component);
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to change the amount of energy in the mech.
|
||||
/// </summary>
|
||||
/// <param name="uid">The mech itself</param>
|
||||
/// <param name="delta">The change in energy</param>
|
||||
/// <param name="component"></param>
|
||||
/// <returns>If the energy was successfully changed.</returns>
|
||||
public virtual bool TryChangeEnergy(EntityUid uid, FixedPoint2 delta, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (component.Energy + delta < 0)
|
||||
return false;
|
||||
|
||||
component.Energy = FixedPoint2.Clamp(component.Energy + delta, 0, component.MaxEnergy);
|
||||
Dirty(component);
|
||||
UpdateUserInterface(uid, component);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the integrity of the mech.
|
||||
/// </summary>
|
||||
/// <param name="uid">The mech itself</param>
|
||||
/// <param name="value">The value the integrity will be set at</param>
|
||||
/// <param name="component"></param>
|
||||
public void SetIntegrity(EntityUid uid, FixedPoint2 value, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
component.Integrity = FixedPoint2.Clamp(value, 0, component.MaxIntegrity);
|
||||
|
||||
if (component.Integrity <= 0)
|
||||
{
|
||||
BreakMech(uid, component);
|
||||
}
|
||||
else if (component.Broken)
|
||||
{
|
||||
component.Broken = false;
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
Dirty(component);
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the pilot is present
|
||||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
/// <returns>Whether or not the pilot is present</returns>
|
||||
public bool IsEmpty(SharedMechComponent component)
|
||||
{
|
||||
return component.PilotSlot.ContainedEntity == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an entity can be inserted into the mech.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="toInsert"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <returns></returns>
|
||||
public bool CanInsert(EntityUid uid, EntityUid toInsert, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
return IsEmpty(component) && _actionBlocker.CanMove(toInsert) && HasComp<BodyComponent>(toInsert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the user interface
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is defined here so that UI updates can be accessed from shared.
|
||||
/// </remarks>
|
||||
public virtual void UpdateUserInterface(EntityUid uid, SharedMechComponent? component = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert a pilot into the mech.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="toInsert"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <returns>Whether or not the entity was inserted</returns>
|
||||
public virtual bool TryInsert(EntityUid uid, EntityUid? toInsert, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (toInsert == null || component.PilotSlot.ContainedEntity == toInsert)
|
||||
return false;
|
||||
|
||||
if (!CanInsert(uid, toInsert.Value, component))
|
||||
return false;
|
||||
|
||||
SetupUser(uid, toInsert.Value);
|
||||
component.PilotSlot.Insert(toInsert.Value, EntityManager);
|
||||
UpdateAppearance(uid, component);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to eject the current pilot from the mech
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <returns>Whether or not the pilot was ejected.</returns>
|
||||
public virtual bool TryEject(EntityUid uid, SharedMechComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (component.PilotSlot.ContainedEntity == null)
|
||||
return false;
|
||||
|
||||
var pilot = component.PilotSlot.ContainedEntity.Value;
|
||||
|
||||
RemoveUser(uid, pilot);
|
||||
_container.RemoveEntity(uid, pilot);
|
||||
UpdateAppearance(uid, component);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnGetMeleeWeapon(EntityUid uid, MechPilotComponent component, GetMeleeWeaponEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<SharedMechComponent>(component.Mech, out var mech))
|
||||
return;
|
||||
|
||||
var weapon = mech.CurrentSelectedEquipment ?? component.Mech;
|
||||
args.Weapon = weapon;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnCanAttackFromContainer(EntityUid uid, MechPilotComponent component, CanAttackFromContainerEvent args)
|
||||
{
|
||||
args.CanAttack = true;
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, SharedMechComponent ? component = null, AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, ref appearance, false))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, MechVisuals.Open, IsEmpty(component), appearance);
|
||||
_appearance.SetData(uid, MechVisuals.Broken, component.Broken, appearance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Threading;
|
||||
using Content.Shared.Mech.Components;
|
||||
|
||||
namespace Content.Shared.Mech.Equipment.Components;
|
||||
|
||||
/// <summary>
|
||||
/// A piece of equipment that can be installed into <see cref="SharedMechComponent"/>
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class MechEquipmentComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How long does it take to install this piece of equipment
|
||||
/// </summary>
|
||||
[DataField("installDuration")]
|
||||
public float InstallDuration = 5;
|
||||
|
||||
/// <summary>
|
||||
/// The mech that the equipment is inside of.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid? EquipmentOwner;
|
||||
|
||||
public CancellationTokenSource? TokenSource = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the equipment when the installation is finished successfully
|
||||
/// </summary>
|
||||
public sealed class MechEquipmentInstallFinished : EntityEventArgs
|
||||
{
|
||||
public EntityUid Mech;
|
||||
|
||||
public MechEquipmentInstallFinished(EntityUid mech)
|
||||
{
|
||||
Mech = mech;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the equipment when the installation fails.
|
||||
/// </summary>
|
||||
public sealed class MechEquipmentInstallCancelled : EntityEventArgs
|
||||
{
|
||||
}
|
||||
102
Content.Shared/Mech/MechUI.cs
Normal file
102
Content.Shared/Mech/MechUI.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Mech;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum MechUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised to collect BUI states for each of the mech's equipment items
|
||||
/// </summary>
|
||||
public sealed class MechEquipmentUiStateReadyEvent : EntityEventArgs
|
||||
{
|
||||
public Dictionary<EntityUid, BoundUserInterfaceState> States = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised to relay an equipment ui message
|
||||
/// </summary>
|
||||
public sealed class MechEquipmentUiMessageRelayEvent : EntityEventArgs
|
||||
{
|
||||
public MechEquipmentUiMessage Message;
|
||||
|
||||
public MechEquipmentUiMessageRelayEvent(MechEquipmentUiMessage message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI event raised to remove a piece of equipment from a mech
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MechEquipmentRemoveMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public EntityUid Equipment;
|
||||
|
||||
public MechEquipmentRemoveMessage(EntityUid equipment)
|
||||
{
|
||||
Equipment = equipment;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// base for all mech ui messages
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public abstract class MechEquipmentUiMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public EntityUid Equipment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// event raised for the grabber equipment to eject an item from it's storage
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MechGrabberEjectMessage : MechEquipmentUiMessage
|
||||
{
|
||||
public EntityUid Item;
|
||||
|
||||
public MechGrabberEjectMessage(EntityUid equipment, EntityUid uid)
|
||||
{
|
||||
Equipment = equipment;
|
||||
Item = uid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BUI state for mechs that also contains all equipment ui states.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⢐⠤⢃⢰⠐⡄⣀⠀⠀
|
||||
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⣨⠀⢁⠁⠐⡐⠠⠜⠐⠀
|
||||
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠔⠐⢀⡁⣀⠔⡌⠡⢀⢐⠁⠀
|
||||
/// ⠀⠀⠀⠀⢀⠔⠀⡂⡄⠠⢀⡀⠀⣄⡀⠠⠤⠴⡋⠑⡠⠀⠔⠐⢂⠕⢀⡂⠀⠀
|
||||
/// ⠀⠀⠀⡔⠁⠠⡐⠁⠀⠀⠀⢘⠀⠀⠀⠀⠠⠀⠈⠪⠀⠑⠡⣃⠈⠤⡈⠀⠀⠀
|
||||
/// ⠀⠀⠨⠀⠄⡒⠀⡂⢈⠀⣀⢌⠀⠀⠁⡈⠀⢆⢀⠀⡀⠉⠒⢆⠑⠀⠀⠀⠀⠀
|
||||
/// ⠀⠀⠀⡁⠐⠠⠐⡀⠀⢀⣀⠣⡀⠢⡀⠀⢀⡃⠰⠀⠈⠠⢁⠎⠀⠀⠀⠀⠀⠀
|
||||
/// ⠀⠀⠀⠅⠒⣈⢣⠠⠈⠕⠁⠱⠄⢤⠈⠪⠡⠎⢘⠈⡁⢙⠈⠀⠀⠀⠀⠀⠀⠀
|
||||
/// ⠀⠀⠀⠃⠀⢡⠀⠧⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢕⡈⠌⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
/// ⠀⠀⠀⠀⠀⠀⠈⡀⡀⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠀⡐⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
/// ⠀⠀⠀⠀⠀⠀⠀⢈⢂⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠀⡃⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
/// ⠀⠀⠀⠀⠀⠀⠀⠎⠐⢅⠀⠀⠀⠀⠀⠀⠀⠀⠀⢐⠅⠚⠄⠀⠀⠀⠀⠀⠀⠀
|
||||
/// ⠀⠀⢈⠩⠈⠀⠐⠁⠀⢀⠀⠄⡂⠒⠐⠀⠆⠁⠰⠠⠀⢅⠈⠐⠄⢁⢡⠀⠀⠀
|
||||
/// ⠀⠀⢈⡀⠰⡁⠀⠁⠴⠁⠔⠀⠀⠄⠄⡁⠀⠂⠀⠢⠠⠁⠀⠠⠈⠂⠬⠀⠀⠀
|
||||
/// ⠀⠀⠠⡂⢄⠤⠒⣁⠐⢕⢀⡈⡐⡠⠄⢐⠀⠈⠠⠈⡀⠂⢀⣀⠰⠁⠠⠀⠀
|
||||
/// trojan horse bui state⠀
|
||||
/// </remarks>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MechBoundUiState : BoundUserInterfaceState
|
||||
{
|
||||
public Dictionary<EntityUid, BoundUserInterfaceState> EquipmentStates = new();
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MechGrabberUiState : BoundUserInterfaceState
|
||||
{
|
||||
public List<EntityUid> Contents = new();
|
||||
public int MaxContents;
|
||||
}
|
||||
62
Content.Shared/Mech/SharedMech.cs
Normal file
62
Content.Shared/Mech/SharedMech.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Mech;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum MechVisuals : byte
|
||||
{
|
||||
Open, //whether or not it's open and has a rider
|
||||
Broken //if it broke and no longer works.
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum MechAssemblyVisuals : byte
|
||||
{
|
||||
State
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum MechVisualLayers : byte
|
||||
{
|
||||
Base
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on equipment when it is inserted into a mech
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct MechEquipmentInsertedEvent(EntityUid Mech)
|
||||
{
|
||||
public readonly EntityUid Mech = Mech;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on equipment when it is removed from a mech
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct MechEquipmentRemovedEvent(EntityUid Mech)
|
||||
{
|
||||
public readonly EntityUid Mech = Mech;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the mech equipment before it is going to be removed.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct AttemptRemoveMechEquipmentEvent()
|
||||
{
|
||||
public bool Cancelled = false;
|
||||
}
|
||||
|
||||
public sealed class MechToggleEquipmentEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class MechOpenUiEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class MechEjectPilotEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user