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:
@@ -4,7 +4,6 @@ using Content.Shared.Emoting;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Movement;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Speech;
|
||||
@@ -148,7 +147,11 @@ namespace Content.Shared.ActionBlocker
|
||||
public bool CanAttack(EntityUid uid, EntityUid? target = null)
|
||||
{
|
||||
if (_container.IsEntityInContainer(uid))
|
||||
return false;
|
||||
{
|
||||
var containerEv = new CanAttackFromContainerEvent(uid, target);
|
||||
RaiseLocalEvent(uid, containerEv);
|
||||
return containerEv.CanAttack;
|
||||
}
|
||||
|
||||
var ev = new AttackAttemptEvent(uid, target);
|
||||
RaiseLocalEvent(uid, ev);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Interaction.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Relays an entities interactions to another entity.
|
||||
/// This doesn't raise the same events, but just relays
|
||||
/// the clicks of the mouse.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(SharedInteractionSystem))]
|
||||
public sealed class InteractionRelayComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity the interactions are being relayed to.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid? RelayEntity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains network state for <see cref="InteractionRelayComponent"/>
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class InteractionRelayComponentState : ComponentState
|
||||
{
|
||||
public EntityUid? RelayEntity;
|
||||
|
||||
public InteractionRelayComponentState(EntityUid? relayEntity)
|
||||
{
|
||||
RelayEntity = relayEntity;
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,20 @@ namespace Content.Shared.Interaction.Events
|
||||
Target = target;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed at an entity to check if they can attack while inside of a container.
|
||||
/// </summary>
|
||||
public sealed class CanAttackFromContainerEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Uid;
|
||||
public EntityUid? Target;
|
||||
public bool CanAttack = false;
|
||||
|
||||
public CanAttackFromContainerEvent(EntityUid uid, EntityUid? target = null)
|
||||
{
|
||||
Uid = uid;
|
||||
Target = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Shared.Interaction
|
||||
{
|
||||
@@ -37,44 +38,57 @@ namespace Content.Shared.Interaction
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InteractNoHandEvent : HandledEntityEventArgs, ITargetedInteractEventArgs
|
||||
/// <summary>
|
||||
/// Low-level interaction event used for entities without hands.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// SHIT IS CURSED.
|
||||
/// </remarks>
|
||||
//TODO: KILLLLLLL
|
||||
public sealed class InteractNoHandEvent : HandledEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity that triggered the interaction.
|
||||
/// </summary>
|
||||
public EntityUid User { get; }
|
||||
public EntityUid User;
|
||||
|
||||
/// <summary>
|
||||
/// Entity that was interacted on.
|
||||
/// </summary>
|
||||
public EntityUid Target { get; }
|
||||
public EntityUid? Target;
|
||||
|
||||
public InteractNoHandEvent(EntityUid user, EntityUid target)
|
||||
public EntityCoordinates ClickLocation;
|
||||
|
||||
public InteractNoHandEvent(EntityUid user, EntityUid? target, EntityCoordinates clickLocation)
|
||||
{
|
||||
User = user;
|
||||
Target = target;
|
||||
ClickLocation = clickLocation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverse of the InteractNoHandEvent - raised on what was interacted on, rather than the other way around.
|
||||
/// </summary>
|
||||
public sealed class InteractedNoHandEvent : HandledEntityEventArgs, ITargetedInteractEventArgs
|
||||
public sealed class InteractedNoHandEvent : HandledEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity that was interacted on
|
||||
/// </summary>
|
||||
public EntityUid Target { get; }
|
||||
public EntityUid Target;
|
||||
|
||||
/// <summary>
|
||||
/// Entity that triggered this interaction
|
||||
/// </summary>
|
||||
public EntityUid User { get; }
|
||||
public EntityUid User;
|
||||
|
||||
public InteractedNoHandEvent(EntityUid target, EntityUid user)
|
||||
public EntityCoordinates ClickLocation;
|
||||
|
||||
public InteractedNoHandEvent(EntityUid target, EntityUid user, EntityCoordinates clickLocation)
|
||||
{
|
||||
Target = target;
|
||||
User = user;
|
||||
ClickLocation = clickLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
Content.Shared/Interaction/SharedInteractionSystem.Relay.cs
Normal file
35
Content.Shared/Interaction/SharedInteractionSystem.Relay.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Interaction;
|
||||
|
||||
public abstract partial class SharedInteractionSystem
|
||||
{
|
||||
public void InitializeRelay()
|
||||
{
|
||||
SubscribeLocalEvent<InteractionRelayComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<InteractionRelayComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, InteractionRelayComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new InteractionRelayComponentState(component.RelayEntity);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, InteractionRelayComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not InteractionRelayComponentState state)
|
||||
return;
|
||||
|
||||
component.RelayEntity = state.RelayEntity;
|
||||
}
|
||||
|
||||
public void SetRelay(EntityUid uid, EntityUid? relayEntity, InteractionRelayComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
component.RelayEntity = relayEntity;
|
||||
Dirty(component);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace Content.Shared.Interaction
|
||||
/// Governs interactions during clicking on entities
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public abstract class SharedInteractionSystem : EntitySystem
|
||||
public abstract partial class SharedInteractionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
@@ -81,6 +81,8 @@ namespace Content.Shared.Interaction
|
||||
.Bind(ContentKeyFunctions.TryPullObject,
|
||||
new PointerInputCmdHandler(HandleTryPullObject))
|
||||
.Register<SharedInteractionSystem>();
|
||||
|
||||
InitializeRelay();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -224,6 +226,11 @@ namespace Content.Shared.Interaction
|
||||
bool checkAccess = true,
|
||||
bool checkCanUse = true)
|
||||
{
|
||||
if (TryComp<InteractionRelayComponent>(user, out var relay) && relay.RelayEntity is not null)
|
||||
{
|
||||
UserInteraction(relay.RelayEntity.Value, coordinates, target, altInteract, checkCanInteract, checkAccess, checkCanUse);
|
||||
}
|
||||
|
||||
if (target != null && Deleted(target.Value))
|
||||
return;
|
||||
|
||||
@@ -255,25 +262,25 @@ namespace Content.Shared.Interaction
|
||||
&& !CanAccessViaStorage(user, target.Value))
|
||||
return;
|
||||
|
||||
var inRangeUnobstructed = target == null
|
||||
? !checkAccess || InRangeUnobstructed(user, coordinates)
|
||||
: !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities
|
||||
|
||||
// Does the user have hands?
|
||||
if (!TryComp(user, out SharedHandsComponent? hands) || hands.ActiveHand == null)
|
||||
{
|
||||
var ev = new InteractNoHandEvent(user, target, coordinates);
|
||||
RaiseLocalEvent(user, ev);
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
var ev = new InteractNoHandEvent(user, target.Value);
|
||||
RaiseLocalEvent(user, ev);
|
||||
|
||||
var interactedEv = new InteractedNoHandEvent(target.Value, user);
|
||||
var interactedEv = new InteractedNoHandEvent(target.Value, user, coordinates);
|
||||
RaiseLocalEvent(target.Value, interactedEv);
|
||||
DoContactInteraction(user, target.Value, ev);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var inRangeUnobstructed = target == null
|
||||
? !checkAccess || InRangeUnobstructed(user, coordinates)
|
||||
: !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities
|
||||
|
||||
// empty-hand interactions
|
||||
if (hands.ActiveHandEntity is not { } held)
|
||||
{
|
||||
|
||||
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
|
||||
{
|
||||
}
|
||||
@@ -17,6 +17,7 @@ using Robust.Shared.Physics.Controllers;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Mech.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
@@ -414,6 +415,12 @@ namespace Content.Shared.Movement.Systems
|
||||
|
||||
mobMover.StepSoundDistance -= distanceNeeded;
|
||||
|
||||
if (TryComp<FootstepModifierComponent>(mover.Owner, out var moverModifier))
|
||||
{
|
||||
sound = moverModifier.Sound;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) &&
|
||||
EntityManager.TryGetComponent<FootstepModifierComponent>(shoes, out var modifier))
|
||||
{
|
||||
|
||||
@@ -130,6 +130,15 @@ public sealed class MeleeWeaponComponent : Component
|
||||
public SoundSpecifier NoDamageSound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/tap.ogg");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on entity in GetWeapon function to allow systems to manually
|
||||
/// specify what the weapon should be.
|
||||
/// </summary>
|
||||
public sealed class GetMeleeWeaponEvent : HandledEntityEventArgs
|
||||
{
|
||||
public EntityUid? Weapon;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MeleeWeaponComponentState : ComponentState
|
||||
{
|
||||
|
||||
@@ -19,7 +19,6 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -227,6 +226,13 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
{
|
||||
MeleeWeaponComponent? melee;
|
||||
|
||||
var ev = new GetMeleeWeaponEvent();
|
||||
RaiseLocalEvent(entity, ev);
|
||||
if (ev.Handled)
|
||||
{
|
||||
return EntityManager.GetComponentOrNull<MeleeWeaponComponent>(ev.Weapon);
|
||||
}
|
||||
|
||||
// Use inhands entity if we got one.
|
||||
if (EntityManager.TryGetComponent(entity, out SharedHandsComponent? hands) &&
|
||||
hands.ActiveHandEntity is { } held)
|
||||
|
||||
Reference in New Issue
Block a user