Set up surgical augments infrastructure & add tool panel augments (#3059)

* Set up _Shitmed groundwork for organ slot manipulation

* Groundwork for surgical augments

* Add tool panel augments

* sprite gaming

* code review gaming

* i think the textures got swapped

* localization gaming

* surgery localization gaming

* crafting gaming

* fix tests

* fix tests actually

* now we deal with ouroboros

* she feed on my back til i

* relocation

* nuke felinid

* augment fixes

* event-based battery search

* descriptionned

---------

Co-authored-by: Lyndomen <49795619+Lyndomen@users.noreply.github.com>
This commit is contained in:
pathetic meowmeow 2025-03-16 17:18:53 -04:00 committed by GitHub
parent 54d7ebb94c
commit a40f0be6fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 1757 additions and 21 deletions

View File

@ -0,0 +1,10 @@
<ui:RadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False"/>
</ui:RadialMenu>

View File

@ -0,0 +1,72 @@
using Content.Client.UserInterface.Controls;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.UserInterface;
using Content.Shared.Storage;
using System.Numerics;
using Robust.Shared.GameObjects;
namespace Content.Client._DV.Augments;
[GenerateTypedNameReferences]
public sealed partial class AugmentToolPanelMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entManager = default!;
public event Action<EntityUid?>? SendAugmentToolPanelSystemMessageAction;
private EntityUid _owner;
public AugmentToolPanelMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
}
public void SetEntity(EntityUid uid)
{
_owner = uid;
Refresh();
}
public void Refresh()
{
if (!_entManager.TryGetComponent<StorageComponent>(_owner, out var storage))
return;
foreach (var (entity, _) in storage.StoredItems)
{
var button = new RadialMenuTextureButtonWithSector()
{
SetSize = new Vector2(64f, 64f),
};
button.AddChild(new SpriteView(entity, _entManager)
{
Scale = new Vector2(3f, 3f),
});
Main.AddChild(button);
button.OnButtonUp += _ =>
{
SendAugmentToolPanelSystemMessageAction?.Invoke(entity);
Close();
};
}
var nilButton = new RadialMenuTextureButtonWithSector()
{
SetSize = new Vector2(64f, 64f),
};
Main.AddChild(nilButton);
nilButton.OnButtonUp += _ =>
{
SendAugmentToolPanelSystemMessageAction?.Invoke(null);
Close();
};
}
}

View File

@ -0,0 +1,39 @@
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using Content.Shared._DV.Augments;
namespace Content.Client._DV.Augments;
public sealed class AugmentToolPanelMenuBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
private AugmentToolPanelMenu? _menu;
public AugmentToolPanelMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<AugmentToolPanelMenu>();
_menu.SetEntity(Owner);
_menu.SendAugmentToolPanelSystemMessageAction += SendAugmentToolPanelSystemMessage;
// Open the menu, centered on the mouse
var vpSize = _displayManager.ScreenSize;
_menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize);
}
public void SendAugmentToolPanelSystemMessage(EntityUid? desiredTool)
{
SendMessage(new AugmentToolPanelSystemMessage(_entMan.GetNetEntity(desiredTool)));
}
}

View File

@ -11,6 +11,7 @@ using System.Diagnostics.CodeAnalysis;
using Content.Shared.Storage.Components;
using Robust.Server.Containers;
using Content.Shared.Whitelist;
using Content.Server._DV.Augments; // DeltaV - bodies can have an augment power cell
namespace Content.Server.Power.EntitySystems;
@ -22,6 +23,7 @@ internal sealed class ChargerSystem : EntitySystem
[Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly AugmentPowerCellSystem _augments = default!; // DeltaV - bodies can have an augment power cell
public override void Initialize()
{
@ -250,15 +252,45 @@ internal sealed class ChargerSystem : EntitySystem
UpdateStatus(uid, component);
}
private bool SearchForBattery(EntityUid uid, [NotNullWhen(true)] out EntityUid? batteryUid, [NotNullWhen(true)] out BatteryComponent? component)
// Begin DeltaV - event-based search for battery
public bool SearchForBattery(EntityUid uid, [NotNullWhen(true)] out EntityUid? batteryUid, [NotNullWhen(true)] out BatteryComponent? component)
{
// try get a battery directly on the inserted entity
if (!TryComp(uid, out component))
if (TryComp(uid, out component))
{
// or by checking for a power cell slot on the inserted entity
return _powerCell.TryGetBatteryFromSlot(uid, out batteryUid, out component);
batteryUid = uid;
return true;
}
batteryUid = uid;
return true;
var evt = new SearchForBatteryEvent();
RaiseLocalEvent(uid, ref evt);
if (evt.Handled)
{
batteryUid = evt.Uid;
component = evt.Component;
return evt.Handled;
}
batteryUid = null;
component = null;
return false;
}
// End DeltaV - event-based search for battery
}
// Begin DeltaV - event-based search for battery
/// <summary>
/// Event raised to search for batteries within an entity
/// </summary>
[ByRefEvent]
public struct SearchForBatteryEvent
{
public EntityUid? Uid;
public BatteryComponent? Component;
public bool Handled;
}
// End DeltaV - event-based search for battery

View File

@ -42,8 +42,16 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem
SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
// funny
SubscribeLocalEvent<PowerCellSlotComponent, BeingMicrowavedEvent>(OnSlotMicrowaved);
SubscribeLocalEvent<PowerCellSlotComponent, SearchForBatteryEvent>(OnSearchForBattery); // DeltaV - event-based search for battery
}
// Begin DeltaV - event-based search for battery
private void OnSearchForBattery(Entity<PowerCellSlotComponent> ent, ref SearchForBatteryEvent args)
{
args.Handled = TryGetBatteryFromSlot(ent, out args.Uid, out args.Component);
}
// End DeltaV - event-based search for battery
private void OnSlotMicrowaved(EntityUid uid, PowerCellSlotComponent component, BeingMicrowavedEvent args)
{
if (!_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out var slot))

View File

@ -0,0 +1,128 @@
using Content.Server.Power.Components; // ough BatteryComponent why are you in server
using Content.Server.Power.EntitySystems;
using Content.Server.PowerCell;
using Content.Shared._DV.Augments;
using Content.Shared.Alert;
using Content.Shared.Body.Organ;
using Content.Shared.Body.Systems;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.PowerCell.Components;
namespace Content.Server._DV.Augments;
public sealed class AugmentPowerCellSystem : EntitySystem
{
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly SharedBodySystem _body = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<HasAugmentPowerCellSlotComponent, SearchForBatteryEvent>(OnSearchForBattery);
}
private void OnSearchForBattery(Entity<HasAugmentPowerCellSlotComponent> ent, ref SearchForBatteryEvent args)
{
// or by checking for an augment power cell
if (TryGetAugmentPowerCell(ent) is (_, var insertedBattery) && insertedBattery is {} battery)
{
args.Uid = battery.Owner;
args.Component = battery.Comp;
args.Handled = true;
return;
}
}
public (Entity<AugmentPowerCellSlotComponent, OrganComponent, PowerCellSlotComponent> Organ, Entity<BatteryComponent>? Battery)? TryGetAugmentPowerCell(EntityUid body)
{
foreach (var organ in _body.GetBodyOrganEntityComps<AugmentPowerCellSlotComponent>(body))
{
if (!TryComp<PowerCellSlotComponent>(organ, out var powerCellSlot))
continue;
var entity = new Entity<AugmentPowerCellSlotComponent, OrganComponent, PowerCellSlotComponent>(organ.Owner, organ.Comp1, organ.Comp2, powerCellSlot);
if (_powerCell.TryGetBatteryFromSlot(organ, out var batteryUid, out var batteryComp))
{
return (entity, new(batteryUid.Value, batteryComp));
}
return (entity, null);
}
return null;
}
public (Entity<AugmentPowerCellSlotComponent, OrganComponent, PowerCellSlotComponent> Organ, Entity<BatteryComponent>? Battery)? TryGetAugmentPowerCellFromAugment(EntityUid augment)
{
if (!TryComp<OrganComponent>(augment, out var organ) || organ.Body is not {} uid)
return null;
return TryGetAugmentPowerCell(uid);
}
public bool TryDrawPower(EntityUid augment, float amount)
{
if (!TryComp<OrganComponent>(augment, out var organ) || organ.Body is not {} body)
return false;
if (TryGetAugmentPowerCellFromAugment(augment) is not (_, var battery))
{
_popup.PopupEntity(Loc.GetString("augments-no-power-cell-slot"), body, body);
return false;
}
if (battery is not {} insertedBattery)
{
_popup.PopupEntity(Loc.GetString("power-cell-no-battery"), body, body);
return false;
}
if (!_battery.TryUseCharge(insertedBattery.Owner, amount))
{
_popup.PopupEntity(Loc.GetString("power-cell-insufficient"), body, body);
return false;
}
return true;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<HasAugmentPowerCellSlotComponent>();
while (query.MoveNext(out var owner, out _))
{
if (_mobState.IsDead(owner))
continue;
var powerCell = TryGetAugmentPowerCell(owner);
if (powerCell is not {} power)
continue;
var augment = power.Organ;
if (power.Battery is not {} insertedBattery)
{
if (_alerts.IsShowingAlert(owner, augment.Comp1.BatteryAlert))
{
_alerts.ClearAlert(owner, augment.Comp1.BatteryAlert);
_alerts.ShowAlert(owner, augment.Comp1.NoBatteryAlert);
}
continue;
}
if (_alerts.IsShowingAlert(owner, augment.Comp1.NoBatteryAlert))
{
_alerts.ClearAlert(owner, augment.Comp1.NoBatteryAlert);
}
var chargePercent = (short) MathF.Round(insertedBattery.Comp.CurrentCharge / insertedBattery.Comp.MaxCharge * 10f);
_alerts.ShowAlert(owner, augment.Comp1.BatteryAlert, chargePercent);
}
}
}

View File

@ -0,0 +1,93 @@
using System.Linq;
using Content.Server.Power.EntitySystems;
using Content.Shared._DV.Augments;
using Content.Shared.Body.Organ;
using Content.Shared.Body.Part;
using Content.Shared.Body.Systems;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Containers;
namespace Content.Server._DV.Augments;
public sealed class AugmentToolPanelSystem : EntitySystem
{
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedBodySystem _body = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedStorageSystem _storage = default!;
[Dependency] private readonly AugmentPowerCellSystem _augmentPowerCell = default!;
public override void Initialize()
{
base.Initialize();
Subs.BuiEvents<AugmentToolPanelComponent>(AugmentToolPanelUiKey.Key, subs =>
{
subs.Event<AugmentToolPanelSystemMessage>(OnSwitchTool);
});
}
private void OnSwitchTool(Entity<AugmentToolPanelComponent> augment, ref AugmentToolPanelSystemMessage args)
{
if (!TryComp<OrganComponent>(augment, out var organ) || organ.Body is not {} body)
return;
if (!TryComp<HandsComponent>(body, out var hands))
return;
if (!_container.TryGetContainingContainer(augment.Owner, out var container))
return;
if (!_augmentPowerCell.TryDrawPower(augment, augment.Comp.PowerDrawOnSwitch))
return;
foreach (var part in _body.GetBodyPartChildren(container.Owner))
{
if (part.Component.PartType != BodyPartType.Hand)
continue;
var handLocation = part.Component.Symmetry switch {
BodyPartSymmetry.None => HandLocation.Middle,
BodyPartSymmetry.Left => HandLocation.Left,
BodyPartSymmetry.Right => HandLocation.Right,
_ => throw new InvalidOperationException(),
};
var desiredHand = hands.Hands.Values.FirstOrDefault(hand => hand.Location == handLocation);
if (desiredHand == null)
continue;
// if we have a tool that's currently out
if (HasComp<AugmentToolPanelActiveItemComponent>(desiredHand.HeldEntity))
{
// deposit it back into the storage
RemComp<AugmentToolPanelActiveItemComponent>(desiredHand.HeldEntity!.Value);
if (!_storage.PlayerInsertEntityInWorld(augment.Owner, body, desiredHand.HeldEntity!.Value))
{
EnsureComp<AugmentToolPanelActiveItemComponent>(desiredHand.HeldEntity!.Value);
return;
}
}
else if (desiredHand.HeldEntity is not null)
{
_popup.PopupCursor(Loc.GetString("augment-tool-panel-hand-full"), body);
return;
}
if (GetEntity(args.DesiredTool) is not {} desiredTool)
return;
if (!_hands.TryPickup(body, desiredTool, desiredHand))
{
_popup.PopupCursor(Loc.GetString("augment-tool-panel-cannot-pick-up"), body);
return;
}
EnsureComp<AugmentToolPanelActiveItemComponent>(desiredTool);
}
}
}

View File

@ -28,6 +28,7 @@ public sealed class BatteryDrinkerSystem : EntitySystem
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ChargerSystem _chargers = default!; // DeltaV - people with augment power cells can drink batteries
public override void Initialize()
{
@ -45,7 +46,8 @@ public sealed class BatteryDrinkerSystem : EntitySystem
if (!TryComp<BatteryDrinkerComponent>(args.User, out var drinkerComp) ||
!TestDrinkableBattery(uid, drinkerComp) ||
!_silicon.TryGetSiliconBattery(args.User, out var drinkerBattery))
// DeltaV - people with augment power cells can drink batteries
!_chargers.SearchForBattery(args.User, out _, out _))
return;
AlternativeVerb verb = new()
@ -97,21 +99,13 @@ public sealed class BatteryDrinkerSystem : EntitySystem
var drinker = uid;
var sourceBattery = Comp<BatteryComponent>(source);
_silicon.TryGetSiliconBattery(drinker, out var drinkerBatteryComponent);
if (!TryComp(uid, out PowerCellSlotComponent? batterySlot))
// DeltaV - people with augment power cells can drink batteries
if (!_chargers.SearchForBattery(drinker, out var drinkerBattery, out var drinkerBatteryComponent))
return;
var container = _container.GetContainer(uid, batterySlot.CellSlotId);
var drinkerBattery = container.ContainedEntities.First();
// DeltaV - people with augment power cells can drink batteries
TryComp<BatteryDrinkerSourceComponent>(source, out var sourceComp);
DebugTools.AssertNotNull(drinkerBattery);
if (drinkerBattery == null)
return;
var amountToDrink = drinkerComp.DrinkMultiplier * 1000;
amountToDrink = MathF.Min(amountToDrink, sourceBattery.CurrentCharge);
@ -127,10 +121,10 @@ public sealed class BatteryDrinkerSystem : EntitySystem
}
if (_battery.TryUseCharge(source, amountToDrink))
_battery.SetCharge(drinkerBattery, drinkerBatteryComponent.CurrentCharge + amountToDrink, drinkerBatteryComponent);
_battery.SetCharge(drinkerBattery.Value, drinkerBatteryComponent.CurrentCharge + amountToDrink, drinkerBatteryComponent); // DeltaV - people with augment power cells can drink batteries
else
{
_battery.SetCharge(drinkerBattery, sourceBattery.CurrentCharge + drinkerBatteryComponent.CurrentCharge, drinkerBatteryComponent);
_battery.SetCharge(drinkerBattery.Value, sourceBattery.CurrentCharge + drinkerBatteryComponent.CurrentCharge, drinkerBatteryComponent); // DeltaV - people with augment power cells can drink batteries
_battery.SetCharge(source, 0);
}

View File

@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Augments;
/// <summary>
/// Component that allows an augment to have actions
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentActionComponent : Component;

View File

@ -0,0 +1,38 @@
using Content.Shared._Shitmed.Body.Organ;
using Content.Shared.Actions;
using Content.Shared.Body.Organ;
namespace Content.Shared._DV.Augments;
public sealed class AugmentActionSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AugmentActionComponent, OrganEnableChangedEvent>(OnOrganEnableChanged);
}
private void OnOrganEnableChanged(Entity<AugmentActionComponent> augment, ref OrganEnableChangedEvent args)
{
if (!TryComp<OrganComponent>(augment, out var organ) || organ.Body is not {} body)
return;
var actionsComponent = EnsureComp<ActionsComponent>(body);
if (args.Enabled)
{
var ev = new GetItemActionsEvent(_actionContainer, body, augment);
RaiseLocalEvent(augment, ev);
_actions.GrantActions(body, ev.Actions, augment, actionsComponent);
}
else
{
_actions.RemoveProvidedActions(body, augment, actionsComponent);
}
}
}

View File

@ -0,0 +1,27 @@
using Content.Shared.Actions;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
namespace Content.Shared._DV.Augments;
/// <summary>
/// Component that allows an augment to have a user interface
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class AugmentActivatableUIComponent : Component
{
[DataField(required: true, customTypeSerializer: typeof(EnumSerializer))]
public Enum? Key;
[DataField]
public EntProtoId OpenAction = "ActionOpenAugmentInterface";
[DataField, AutoNetworkedField]
public EntityUid? OpenActionEntity;
}
/// <summary>
/// Event that should be dispatched by the <see cref="AugmentActivatableUIComponent.OpenAction"/> to open the UI
/// </summary>
public sealed partial class AugmentUIOpenEvent : InstantActionEvent;

View File

@ -0,0 +1,39 @@
using Content.Shared._Shitmed.Body.Organ;
using Content.Shared.Actions;
using Content.Shared.Body.Organ;
using Robust.Shared.GameObjects;
namespace Content.Shared._DV.Augments;
public sealed class AugmentActivatableUISystem : EntitySystem
{
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AugmentActivatableUIComponent, AugmentUIOpenEvent>(OnOpen);
SubscribeLocalEvent<AugmentActivatableUIComponent, GetItemActionsEvent>(OnGetActions);
}
private void OnOpen(Entity<AugmentActivatableUIComponent> augment, ref AugmentUIOpenEvent args)
{
if (!TryComp<OrganComponent>(augment, out var organ) || organ.Body is not {} body)
return;
if (augment.Comp.Key == null || !_uiSystem.HasUi(augment, augment.Comp.Key))
return;
_uiSystem.OpenUi(augment.Owner, augment.Comp.Key, body);
args.Handled = true;
}
private void OnGetActions(Entity<AugmentActivatableUIComponent> ent, ref GetItemActionsEvent args)
{
if (!TryComp<OrganComponent>(ent, out var organ) || organ.Body is not {} body)
return;
args.AddAction(ref ent.Comp.OpenActionEntity, ent.Comp.OpenAction);
}
}

View File

@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Augments;
/// <summary>
/// Marker component to indicate that an entity serves as an AugmentArm organ
/// <summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentArmComponent : Component;

View File

@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Augments;
/// <summary>
/// Marker component to indicate that an entity serves as an AugmentCharger organ
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentChargerComponent : Component;
/// <summary>
/// Marker component to indicate that an entity will recharge its augment power cell from borg charging stations
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentStationRechargeComponent : Component;

View File

@ -0,0 +1,19 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Augments;
/// <summary>
/// Component that indicates an entity is an augment
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentComponent : Component;
/// <summary>
/// Component that tracks which augments are installed on this body
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class InstalledAugmentsComponent : Component
{
[DataField]
public HashSet<NetEntity> InstalledAugments = new();
}

View File

@ -0,0 +1,24 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Content.Shared.Alert;
namespace Content.Shared._DV.Augments;
/// <summary>
/// Component for entitie that serve as AugmentPowerCellSlot organs
/// <summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentPowerCellSlotComponent : Component
{
[DataField]
public ProtoId<AlertPrototype> BatteryAlert = "BorgBattery";
[DataField]
public ProtoId<AlertPrototype> NoBatteryAlert = "BorgBatteryNone";
}
/// <summary>
/// Marker component to indicate that an entity currently has an AugmentPowerCellSlot organ
/// <summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class HasAugmentPowerCellSlotComponent : Component;

View File

@ -0,0 +1,42 @@
using Content.Shared.Body.Events;
using Content.Shared.Body.Organ;
using Content.Shared.Interaction;
namespace Content.Shared._DV.Augments;
public sealed class AugmentSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AugmentComponent, OrganAddedToBodyEvent>(OnOrganOrganAddedToBody);
SubscribeLocalEvent<AugmentComponent, OrganRemovedFromBodyEvent>(OnOrganOrganRemovedFromBody);
SubscribeLocalEvent<InstalledAugmentsComponent, AccessibleOverrideEvent>(OnAccessibleOverride);
}
private void OnOrganOrganAddedToBody(Entity<AugmentComponent> augment, ref OrganAddedToBodyEvent args)
{
var installed = EnsureComp<InstalledAugmentsComponent>(args.Body);
installed.InstalledAugments.Add(GetNetEntity(augment));
}
private void OnOrganOrganRemovedFromBody(Entity<AugmentComponent> augment, ref OrganRemovedFromBodyEvent args)
{
if (!TryComp<InstalledAugmentsComponent>(args.OldBody, out var installed))
return;
installed.InstalledAugments.Remove(GetNetEntity(augment));
if (installed.InstalledAugments.Count == 0)
RemComp<InstalledAugmentsComponent>(args.OldBody);
}
private void OnAccessibleOverride(Entity<InstalledAugmentsComponent> augment, ref AccessibleOverrideEvent args)
{
if (!TryComp<OrganComponent>(args.Target, out var organ))
return;
args.Handled = true;
args.Accessible = organ.Body == args.User;
}
}

View File

@ -0,0 +1,38 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Augments;
/// <summary>
/// Marker component to indicate that an entity will allow access to its storage via a radial menu once implanted
/// <summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentToolPanelComponent : Component
{
[DataField]
public float PowerDrawOnSwitch = 10f;
}
/// <summary>
/// Marker component to indicate that an entity is the active tool of an augment tool panel
/// <summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class AugmentToolPanelActiveItemComponent : Component;
[Serializable, NetSerializable]
public sealed class AugmentToolPanelSystemMessage : BoundUserInterfaceMessage
{
public NetEntity? DesiredTool;
public AugmentToolPanelSystemMessage(NetEntity? desiredTool)
{
DesiredTool = desiredTool;
}
}
[Serializable, NetSerializable]
public enum AugmentToolPanelUiKey : byte
{
Key
}

View File

@ -0,0 +1,19 @@
using Content.Shared.Hands;
using Robust.Shared.Containers;
namespace Content.Shared._DV.Augments;
public sealed class SharedAugmentToolPanelSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AugmentToolPanelActiveItemComponent, ContainerGettingRemovedAttemptEvent>(OnDropAttempt);
}
private void OnDropAttempt(Entity<AugmentToolPanelActiveItemComponent> ent, ref ContainerGettingRemovedAttemptEvent args)
{
args.Cancel();
}
}

View File

@ -0,0 +1,18 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Content.Shared.Body.Prototypes;
namespace Content.Shared._Shitmed.Medical.Surgery.Conditions;
/// <summary>
/// Requires that this surgery is (not) done on one of the provided body prototypes
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryBodyConditionComponent : Component
{
[DataField(required: true)]
public HashSet<ProtoId<BodyPrototype>> Accepted = default!;
[DataField]
public bool Inverse;
}

View File

@ -0,0 +1,13 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Shitmed.Medical.Surgery.Conditions;
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryOrganSlotConditionComponent : Component
{
[DataField(required: true)]
public string OrganSlot = default!;
[DataField]
public bool Inverse;
}

View File

@ -55,6 +55,7 @@ public abstract partial class SharedSurgerySystem
SubSurgery<SurgeryAffixOrganStepComponent>(OnAffixOrganStep, OnAffixOrganCheck);
SubSurgery<SurgeryAddMarkingStepComponent>(OnAddMarkingStep, OnAddMarkingCheck);
SubSurgery<SurgeryRemoveMarkingStepComponent>(OnRemoveMarkingStep, OnRemoveMarkingCheck);
SubSurgery<SurgeryAddOrganSlotStepComponent>(OnAddOrganSlotStep, OnAddOrganSlotCheck);
Subs.BuiEvents<SurgeryTargetComponent>(SurgeryUIKey.Key, subs =>
{
subs.Event<SurgeryStepChosenBuiMsg>(OnSurgeryTargetStepChosen);
@ -449,6 +450,22 @@ public abstract partial class SharedSurgerySystem
}
}
private void OnAddOrganSlotStep(Entity<SurgeryAddOrganSlotStepComponent> ent, ref SurgeryStepEvent args)
{
if (!TryComp(args.Surgery, out SurgeryOrganSlotConditionComponent? condition))
return;
_body.TryCreateOrganSlot(args.Part, condition.OrganSlot, out _);
}
private void OnAddOrganSlotCheck(Entity<SurgeryAddOrganSlotStepComponent> ent, ref SurgeryStepCompleteCheckEvent args)
{
if (!TryComp(args.Surgery, out SurgeryOrganSlotConditionComponent? condition))
return;
args.Cancelled = !_body.CanInsertOrgan(args.Part, condition.OrganSlot);
}
private void OnAffixPartStep(Entity<SurgeryAffixPartStepComponent> ent, ref SurgeryStepEvent args)
{
if (!TryComp(args.Surgery, out SurgeryPartRemovedConditionComponent? removedComp))

View File

@ -79,6 +79,8 @@ public abstract partial class SharedSurgerySystem : EntitySystem
SubscribeLocalEvent<SurgeryOrganConditionComponent, SurgeryValidEvent>(OnOrganConditionValid);
SubscribeLocalEvent<SurgeryWoundedConditionComponent, SurgeryValidEvent>(OnWoundedValid);
SubscribeLocalEvent<SurgeryPartRemovedConditionComponent, SurgeryValidEvent>(OnPartRemovedConditionValid);
SubscribeLocalEvent<SurgeryBodyConditionComponent, SurgeryValidEvent>(OnBodyConditionValid);
SubscribeLocalEvent<SurgeryOrganSlotConditionComponent, SurgeryValidEvent>(OnOrganSlotConditionValid);
SubscribeLocalEvent<SurgeryPartPresentConditionComponent, SurgeryValidEvent>(OnPartPresentConditionValid);
SubscribeLocalEvent<SurgeryMarkingConditionComponent, SurgeryValidEvent>(OnMarkingPresentValid);
SubscribeLocalEvent<SurgeryBodyComponentConditionComponent, SurgeryValidEvent>(OnBodyComponentConditionValid);
@ -278,6 +280,17 @@ public abstract partial class SharedSurgerySystem : EntitySystem
}
}
private void OnBodyConditionValid(Entity<SurgeryBodyConditionComponent> ent, ref SurgeryValidEvent args)
{
if (TryComp<BodyComponent>(args.Body, out var body) && body.Prototype is {} bodyId)
args.Cancelled = ent.Comp.Accepted.Contains(bodyId) ^ !ent.Comp.Inverse;
}
private void OnOrganSlotConditionValid(Entity<SurgeryOrganSlotConditionComponent> ent, ref SurgeryValidEvent args)
{
args.Cancelled = _body.CanInsertOrgan(args.Part, ent.Comp.OrganSlot) ^ !ent.Comp.Inverse;
}
private void OnPartRemovedConditionValid(Entity<SurgeryPartRemovedConditionComponent> ent, ref SurgeryValidEvent args)
{
if (!_body.CanAttachToSlot(args.Part, ent.Comp.Connection))

View File

@ -0,0 +1,6 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Shitmed.Medical.Surgery.Steps;
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryAddOrganSlotStepComponent : Component;

View File

@ -0,0 +1,3 @@
augments-no-power-cell-slot = You don't have a power cell slot installed
augment-tool-panel-hand-full = There's something in your hand
augment-tool-panel-cannot-pick-up = Your hand isn't capable of holding that

View File

@ -0,0 +1,3 @@
surgery-popup-step-SurgeryStepInsertAugmentPowerCellSlot = {$user} is inserting a power cell slot into {$target}'s {$part}.
surgery-popup-step-SurgeryStepInsertAugmentPowerCellCharger = {$user} is inserting a power cell recharger into {$target}'s {$part}.
surgery-popup-step-SurgeryStepInsertAugmentArm = {$user} is inserting an augment into {$target}'s {$part}.

View File

@ -7,6 +7,8 @@ research-technology-cloning = Cloning
# Civilian
research-technology-syringe-gun = Syringe Gun
research-technology-basic-augmentation = Basic Augmentation
research-technology-implanted-tools = Implanted Tools
# Arsenal
research-technology-exotic-ammunition = Exotic Ammunition

View File

@ -35,6 +35,8 @@ surgery-popup-step-SurgeryStepRemoveItem = {$user} is removing something from {$
surgery-popup-step-SurgeryStepRemoveOrgan = {$user} is removing an organ from {$target}'s {$part}!
surgery-popup-step-SurgeryStepInsertOrgan = {$user} is inserting an organ into {$target}'s {$part}!
surgery-popup-step-SurgeryStepOpenOrganSlot = {$user} is opening a cavity in {$target}'s {$part}!
surgery-popup-procedure-SurgeryRemoveBrain-step-SurgeryStepRemoveOrgan = {$user} is removing the brain from {$target}'s {$part}!
surgery-popup-procedure-SurgeryRemoveHeart-step-SurgeryStepRemoveOrgan = {$user} is removing the heart from {$target}'s {$part}!
surgery-popup-procedure-SurgeryRemoveLiver-step-SurgeryStepRemoveOrgan = {$user} is removing the liver from {$target}'s {$part}!

View File

@ -701,6 +701,8 @@
Plastic: 30
- type: StaticPrice
price: 15
- type: Tag # DeltaV - augment construction
tags: [ AugmentAPCBoard ]
- type: entity
id: PowerCageRechargerCircuitboard
@ -744,6 +746,8 @@
Plastic: 30
- type: StaticPrice
price: 15
- type: Tag # DeltaV - augment construction
tags: [ AugmentStationBoard ]
- type: entity
id: WeaponCapacitorRechargerCircuitboard

View File

@ -325,6 +325,8 @@
- MechParts
- MechEquipment
- Cybernetics # Shitmed change
- PowerAugments # DeltaV - augments
- ToolAugments # DeltaV - augments
- type: EmagLatheRecipes # DeltaV
emagStaticPacks:
- RoboticsEmagStatic

View File

@ -248,6 +248,7 @@
components:
- BorgChassis
- Silicon # EE IPCs
- AugmentStationRecharge # DeltaV augment power cells
- type: Construction
containers:
- machine_parts
@ -291,6 +292,8 @@
whitelist:
components:
- BorgChassis
- Silicon # EE IPCs
- AugmentStationRecharge # DeltaV augment power cells
- type: ContainerContainer
containers:
entity_storage: !type:Container

View File

@ -0,0 +1,241 @@
- type: entity
parent: BaseAugment
id: AugmentBaseArm
name: base arm augment
abstract: true
components:
- type: Organ
slotId: armImplant
- type: AugmentArm
- type: entity
parent: ActionOpenAugmentInterface
id: ActionOpenToolsPanel
name: Open tools panel
description: Access the tools implanted within you
components:
- type: InstantAction
icon: { sprite: Clothing/Belt/utility.rsi, state: icon }
- type: entity
parent: AugmentBaseArm
id: AugmentBaseToolPanel
name: base tool panel augment
abstract: true
components:
- type: Sprite
sprite: _DV/Objects/Augments/arm.rsi
state: base
- type: Storage
maxItemSize: Normal
defaultStorageOrientation: Vertical
grid:
- 0,0,9,1
- type: Item
size: Ginormous
- type: ContainerContainer
containers:
storagebase: !type:Container
ents: []
- type: AugmentToolPanel
- type: UserInterface
interfaces:
enum.StorageUiKey.Key:
type: StorageBoundUserInterface
enum.AugmentToolPanelUiKey.Key:
type: AugmentToolPanelMenuBoundUserInterface
interactionRange: 0
- type: AugmentActivatableUI
key: enum.AugmentToolPanelUiKey.Key
openAction: ActionOpenToolsPanel
- type: AugmentAction
- type: Construction
graph: CreateToolsPanel
- type: entity
parent: BaseElectronics
id: AugmentBaseElectronics
abstract: true
components:
- type: PhysicalComposition
materialComposition:
Glass: 100
Steel: 100
chemicalComposition:
Silicon: 20
- type: Sprite
sprite: Objects/Misc/module.rsi
state: mainboard
- type: entity
parent: AugmentBaseElectronics
id: AugmentUtilityPanelElectronics
name: utility tools panel electronics
description: An electronics board that programs a tool panel to hold a specific type of item. This one holds utility tools.
components:
- type: Item
storedRotation: 0
- type: Tag
tags: [ AugmentToolBelt ]
- type: entity
parent: AugmentBaseElectronics
id: AugmentBotanyPanelElectronics
name: botany tools panel electronics
description: An electronics board that programs a tool panel to hold a specific type of item. This one holds botanical tools.
components:
- type: Item
storedRotation: 0
- type: Tag
tags: [ AugmentBotanyToolBelt ]
- type: entity
parent: AugmentBaseElectronics
id: AugmentSurgeryPanelElectronics
name: surgery tools panel electronics
description: An electronics board that programs a tool panel to hold a specific type of item. This one holds surgical tools.
components:
- type: Item
storedRotation: 0
- type: Tag
tags: [ AugmentSurgeryToolBelt ]
- type: entity
parent: AugmentBaseElectronics
id: AugmentPaperworkPanelElectronics
name: paperwork tools panel electronics
description: An electronics board that programs a tool panel to hold a specific type of item. This one holds paperwork tools.
components:
- type: Item
storedRotation: 0
- type: Tag
tags: [ AugmentPaperworkToolBelt ]
- type: entity
parent: BaseItem
id: BaseIncompleteAugmentToolPanel
abstract: true
components:
- type: Sprite
sprite: _DV/Objects/Augments/arm.rsi
- type: Item
size: Ginormous
- type: Construction
graph: CreateToolsPanel
- type: entity
parent: BaseIncompleteAugmentToolPanel
id: AugmentToolPanelCasing
name: tools panel augment casing
description: Can be implanted into a body to provide access to specific tools. This one is incomplete.
components:
- type: Sprite
state: base
- type: Construction
node: casing
- type: Tag
tags: [ AugmentToolPanelCasing ]
- type: entity
parent: BaseIncompleteAugmentToolPanel
id: AugmentToolPanelCasingCables
name: tools panel augment casing with cables
description: Can be implanted into a body to provide access to specific tools. This one is incomplete.
components:
- type: Sprite
state: base_cables
- type: Construction
node: cables
- type: entity
parent: AugmentBaseToolPanel
id: AugmentToolsPanel
name: tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold tools.
components:
- type: Sprite
state: engineering
- type: Storage
whitelist:
tags:
- Wirecutter
- Crowbar
- Screwdriver
- Wrench
- GeigerCounter
- Powerdrill
- JawsOfLife
- HolofanProjector
- Multitool
- AppraisalTool
components:
- StationMap
- SprayPainter
- NetworkConfigurator
- RCD
- RCDAmmo
- Welder
- PowerCell
- Geiger
- TrayScanner
- GasAnalyzer
- type: Construction
node: "utility tools panel"
- type: entity
parent: AugmentBaseToolPanel
id: AugmentBotanyPanel
name: botanical tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold botanical tools.
components:
- type: Sprite
state: botany
- type: Storage
whitelist:
tags:
- PlantSampleTaker
- BotanyShovel
- BotanyHoe
- BotanyHatchet
- Dropper
components:
- HandLabeler
- type: Construction
node: "botany tools panel"
- type: entity
parent: AugmentBaseToolPanel
id: AugmentPaperworkPanel
name: paperwork tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold paperwork tools.
components:
- type: Sprite
state: paperwork
- type: Storage
whitelist:
tags:
- Folder
- HandLabeler
components:
- Stamp
- TapeRecorder
- type: Construction
node: "paperwork tools panel"
- type: entity
parent: AugmentBaseToolPanel
id: AugmentSurgicalPanel
name: surgical tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold surgical tools.
components:
- type: Sprite
state: surgical
- type: Storage
whitelist:
requireAll: true
tags:
- SurgeryTool
components:
- SurgeryTool
- type: Construction
node: "surgery tools panel"

View File

@ -0,0 +1,6 @@
- type: entity
parent: BaseItem
id: BaseAugment
abstract: true
components:
- type: Augment

View File

@ -0,0 +1,137 @@
- type: entity
id: ActionOpenAugmentInterface
abstract: true
# name: Open augment UI
# description: This is a developer placeholder and should not be seen
components:
- type: InstantAction
icon: { sprite: Clothing/Mask/gas.rsi, state: icon }
iconOn: Interface/Default/blocked.png
event: !type:AugmentUIOpenEvent
- type: entity
parent: BaseItem
id: AugmentPowerCellSlotCasing
name: augment power cell slot casing
description: Can be implanted into a body to provide power to other implants. This one is incomplete.
components:
- type: Sprite
sprite: _DV/Objects/Augments/power.rsi
state: incomplete_cell
- type: Construction
graph: CreatePowerCellAugment
node: casing
defaultTarget: augment
- type: Tag
tags: [ AugmentCellCasing ]
- type: entity
parent: BaseItem
id: AugmentPowerCellSlotCasingCables
name: augment power cell slot casing with cables
description: Can be implanted into a body to provide power to other implants. This one is incomplete.
components:
- type: Sprite
sprite: _DV/Objects/Augments/power.rsi
state: incomplete_cell_cables
- type: Construction
graph: CreatePowerCellAugment
node: cables
- type: entity
parent: BaseAugment
id: AugmentPowerCellSlot
name: augment power cell slot
description: Can be implanted into a body to provide power to other implants.
components:
- type: Sprite
sprite: _DV/Objects/Augments/power.rsi
state: cell
- type: Organ
slotId: powerCell
onAdd:
- type: HasAugmentPowerCellSlot
- type: AugmentPowerCellSlot
- type: Construction
graph: CreatePowerCellAugment
node: augment
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot { }
- type: PowerCellSlot
cellSlotId: cell_slot
- type: ItemSlots
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default
- type: entity
parent: BaseAugment
id: AugmentBaseCharger
name: base charger augment
abstract: true
components:
- type: Sprite
sprite: _DV/Objects/Augments/power.rsi
state: cell
- type: Organ
slotId: charger
- type: AugmentCharger
- type: entity
parent: BaseItem
id: AugmentRechargerCasing
name: augment recharger casing
description: Can be implanted into a body to recharge an internal power cell. This one is incomplete.
components:
- type: Sprite
sprite: _DV/Objects/Augments/power.rsi
state: incomplete_charger
- type: Construction
graph: CreatePowerCellChargerAugment
node: casing
- type: Tag
tags: [ AugmentChargerCasing ]
- type: entity
parent: BaseItem
id: AugmentRechargerCasingCables
name: augment recharger casing with cables
description: Can be implanted into a body to recharge an internal power cell. This one is incomplete.
components:
- type: Sprite
sprite: _DV/Objects/Augments/power.rsi
state: incomplete_charger_cables
- type: Construction
graph: CreatePowerCellChargerAugment
node: cables
- type: entity
parent: AugmentBaseCharger
id: AugmentRechargerAPC
name: augment apc recharger
description: Allows the implantee to recharge their power cell via APCs.
components:
- type: Sprite
state: apc
- type: Organ
onAdd:
- type: BatteryDrinker
- type: Construction
graph: CreatePowerCellChargerAugment
node: apc
- type: entity
parent: AugmentBaseCharger
id: AugmentRechargerStation
name: augment station recharger
description: Allows the implantee to recharge their power cell via cyborg recharging stations.
components:
- type: Sprite
state: station
- type: Organ
onAdd:
- type: AugmentStationRecharge
- type: Construction
graph: CreatePowerCellChargerAugment
node: station

View File

@ -0,0 +1,179 @@
- type: entity
parent: SurgeryBase
id: SurgeryInsertPowerCellSlot
name: Insert Power Cell Slot
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenRibcage
steps:
- SurgeryStepSawBones
- SurgeryStepInsertAugmentPowerCellSlot
- SurgeryStepSealOrganWound
- type: SurgeryPartCondition
part: Torso
- type: SurgeryOrganCondition
organ:
- type: AugmentPowerCellSlot
inverse: true
reattaching: true
- type: SurgeryOrganSlotCondition
organSlot: powerCell
- type: entity
parent: SurgeryBase
id: SurgeryOpenPowerCellSlot
name: Open Cavity For Power Cell Slot
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenRibcage
steps:
- SurgeryStepSawBones
- SurgeryStepOpenOrganSlot
- SurgeryStepSealOrganWound
- type: SurgeryPartCondition
part: Torso
- type: SurgeryOrganSlotCondition
organSlot: powerCell
inverse: true
- type: SurgeryBodyCondition
accepted: [ IPC ]
inverse: true
- type: entity
parent: SurgeryBase
id: SurgeryRemovePowerCellSlot
name: Remove Power Cell Slot
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenRibcage
steps:
- SurgeryStepSawBones
- SurgeryStepClampInternalBleeders
- SurgeryStepRemoveOrgan
- type: SurgeryPartCondition
part: Torso
- type: SurgeryOrganCondition
organ:
- type: AugmentPowerCellSlot
- type: entity
parent: SurgeryBase
id: SurgeryInsertPowerCellCharger
name: Insert Internal Power Cell Charger
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenRibcage
steps:
- SurgeryStepSawBones
- SurgeryStepInsertAugmentPowerCellCharger
- SurgeryStepSealOrganWound
- type: SurgeryPartCondition
part: Torso
- type: SurgeryOrganCondition
organ:
- type: AugmentCharger
inverse: true
reattaching: true
- type: SurgeryOrganSlotCondition
organSlot: charger
- type: entity
parent: SurgeryBase
id: SurgeryOpenPowerCellCharger
name: Open Cavity For Power Cell Charger
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenRibcage
steps:
- SurgeryStepSawBones
- SurgeryStepOpenOrganSlot
- SurgeryStepSealOrganWound
- type: SurgeryPartCondition
part: Torso
- type: SurgeryOrganSlotCondition
organSlot: charger
inverse: true
- type: SurgeryBodyCondition
accepted: [ IPC ]
inverse: true
- type: entity
parent: SurgeryBase
id: SurgeryRemovePowerCellCharger
name: Remove Internal Power Cell Charger
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenRibcage
steps:
- SurgeryStepSawBones
- SurgeryStepClampInternalBleeders
- SurgeryStepRemoveOrgan
- type: SurgeryPartCondition
part: Torso
- type: SurgeryOrganCondition
organ:
- type: AugmentCharger
- type: entity
parent: SurgeryBase
id: SurgeryOpenArmAugment
name: Open Cavity For Arm Augments
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenIncision
steps:
- SurgeryStepSawBones
- SurgeryStepOpenOrganSlot
- SurgeryStepSealOrganWound
- type: SurgeryPartCondition
part: Arm
- type: SurgeryOrganSlotCondition
organSlot: armImplant
inverse: true
- type: entity
parent: SurgeryBase
id: SurgeryInsertArmAugment
name: Insert Arm Augment
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenIncision
steps:
- SurgeryStepSawBones
- SurgeryStepInsertAugmentArm
- SurgeryStepSealOrganWound
- type: SurgeryPartCondition
part: Arm
- type: SurgeryOrganCondition
organ:
- type: AugmentArm
inverse: true
reattaching: true
- type: SurgeryOrganSlotCondition
organSlot: armImplant
- type: entity
parent: SurgeryBase
id: SurgeryRemoveArmAugment
name: Remove Arm Augment
categories: [ HideSpawnMenu ]
components:
- type: Surgery
requirement: SurgeryOpenIncision
steps:
- SurgeryStepSawBones
- SurgeryStepClampInternalBleeders
- SurgeryStepRemoveOrgan
- type: SurgeryPartCondition
part: Arm
- type: SurgeryOrganCondition
organ:
- type: AugmentArm

View File

@ -0,0 +1,17 @@
- type: entity
parent: SurgeryStepInsertOrgan
id: SurgeryStepInsertAugmentPowerCellSlot
name: Add power cell slot
categories: [ HideSpawnMenu ]
- type: entity
parent: SurgeryStepInsertOrgan
id: SurgeryStepInsertAugmentPowerCellCharger
name: Add power cell charger
categories: [ HideSpawnMenu ]
- type: entity
parent: SurgeryStepInsertOrgan
id: SurgeryStepInsertAugmentArm
name: Add arm augment
categories: [ HideSpawnMenu ]

View File

@ -0,0 +1,131 @@
- type: constructionGraph
id: CreatePowerCellAugment
start: start
graph:
- node: start
edges:
- to: casing
steps:
- tag: AugmentCellCasing
name: casing
icon:
sprite: _DV/Objects/Augments/power.rsi
state: incomplete_cell
- node: casing
entity: AugmentPowerCellSlotCasing
edges:
- to: cables
steps:
- material: Cable
amount: 1
- node: cables
entity: AugmentPowerCellSlotCasingCables
edges:
- to: augment
steps:
- material: Plastic
amount: 1
- node: augment
entity: AugmentPowerCellSlot
- type: constructionGraph
id: CreatePowerCellChargerAugment
start: start
graph:
- node: start
edges:
- to: casing
steps:
- tag: AugmentChargerCasing
name: casing
icon:
sprite: _DV/Objects/Augments/power.rsi
state: incomplete_charger
- node: casing
entity: AugmentRechargerCasing
edges:
- to: cables
steps:
- material: Cable
amount: 1
- node: cables
entity: AugmentRechargerCasingCables
edges:
- to: apc
steps:
- tag: AugmentAPCBoard
name: "cell recharger machine board"
icon:
sprite: Objects/Misc/module.rsi
state: charger_APC
- to: station
steps:
- tag: AugmentStationBoard
name: "cyborg recharging station machine board"
icon:
sprite: Objects/Misc/module.rsi
state: charger_APC
- node: apc
entity: AugmentRechargerAPC
- node: station
entity: AugmentRechargerStation
- type: constructionGraph
id: CreateToolsPanel
start: start
graph:
- node: start
edges:
- to: casing
steps:
- tag: AugmentToolPanelCasing
name: casing
icon:
sprite: _DV/Objects/Augments/arm.rsi
state: base
- node: casing
entity: AugmentToolPanelCasing
edges:
- to: cables
steps:
- material: Cable
amount: 1
- node: cables
entity: AugmentToolPanelCasingCables
edges:
- to: "botany tools panel"
steps:
- tag: AugmentBotanyToolBelt
name: botany tools panel electronics
icon:
sprite: Objects/Misc/module.rsi
state: mainboard
- to: "paperwork tools panel"
steps:
- tag: AugmentPaperworkToolBelt
name: paperwork tools panel electronics
icon:
sprite: Objects/Misc/module.rsi
state: mainboard
- to: "utility tools panel"
steps:
- tag: AugmentToolBelt
name: utility tools panel electronics
icon:
sprite: Objects/Misc/module.rsi
state: mainboard
- to: "surgery tools panel"
steps:
- tag: AugmentSurgeryToolBelt
name: surgery tools panel electronics
icon:
sprite: Objects/Misc/module.rsi
state: mainboard
- node: "botany tools panel"
entity: AugmentBotanyPanel
- node: "paperwork tools panel"
entity: AugmentPaperworkPanel
- node: "surgery tools panel"
entity: AugmentSurgicalPanel
- node: "utility tools panel"
entity: AugmentToolsPanel

View File

@ -0,0 +1,90 @@
- type: construction
name: augment power cell slot
description: Can be implanted into a body to provide power to other implants.
id: AugmentPowerCellSlot
graph: CreatePowerCellAugment
startNode: start
targetNode: augment
category: construction-category-utilities
canBuildInImpassable: false
icon:
sprite: _DV/Objects/Augments/power.rsi
state: cell
- type: construction
name: augment apc recharger
description: Allows the implantee to recharge their power cell via APCs.
id: AugmentRechargerAPC
graph: CreatePowerCellChargerAugment
startNode: start
targetNode: apc
category: construction-category-utilities
canBuildInImpassable: false
icon:
sprite: _DV/Objects/Augments/power.rsi
state: apc
- type: construction
name: augment station recharger
description: Allows the implantee to recharge their power cell via cyborg recharging stations.
id: AugmentRechargerStation
graph: CreatePowerCellChargerAugment
startNode: start
targetNode: station
category: construction-category-utilities
canBuildInImpassable: false
icon:
sprite: _DV/Objects/Augments/power.rsi
state: station
- type: construction
name: tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold tools.
id: AugmentToolsPanel
graph: CreateToolsPanel
startNode: start
targetNode: "utility tools panel"
category: construction-category-utilities
canBuildInImpassable: false
icon:
sprite: _DV/Objects/Augments/arm.rsi
state: engineering
- type: construction
name: botanical tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold botanical tools.
id: AugmentBotanyPanel
graph: CreateToolsPanel
startNode: start
targetNode: "botany tools panel"
category: construction-category-utilities
canBuildInImpassable: false
icon:
sprite: _DV/Objects/Augments/arm.rsi
state: botany
- type: construction
name: paperwork tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold paperwork tools.
id: AugmentPaperworkPanel
graph: CreateToolsPanel
startNode: start
targetNode: "paperwork tools panel"
category: construction-category-utilities
canBuildInImpassable: false
icon:
sprite: _DV/Objects/Augments/arm.rsi
state: paperwork
- type: construction
name: surgical tools panel augment
description: A panel that can be augmented into a patient's arm to provide them access to its contents. This one can hold surgical tools.
id: AugmentSurgicalPanel
graph: CreateToolsPanel
startNode: start
targetNode: "surgery tools panel"
category: construction-category-utilities
canBuildInImpassable: false
icon:
sprite: _DV/Objects/Augments/arm.rsi
state: surgical

View File

@ -38,4 +38,19 @@
recipes:
- BorgModuleSecurityChase
- BorgModuleSecurityEscalate
- BorgModuleSecurityBastion
- BorgModuleSecurityBastion
- type: latheRecipePack
id: PowerAugments
recipes:
- AugmentPowerCellSlotCasing
- AugmentRechargerCasing
- type: latheRecipePack
id: ToolAugments
recipes:
- AugmentToolPanelCasing
- AugmentUtilityPanelElectronics
- AugmentBotanyPanelElectronics
- AugmentSurgeryPanelElectronics
- AugmentPaperworkPanelElectronics

View File

@ -169,3 +169,38 @@
parent: IdChipSyndie
id: IdChipRoboNeuroticist
result: IdChipRoboNeuroticist
- type: latheRecipe
parent: BaseBorgModuleRecipe
id: AugmentPowerCellSlotCasing
result: AugmentPowerCellSlotCasing
- type: latheRecipe
parent: BaseBorgModuleRecipe
id: AugmentRechargerCasing
result: AugmentRechargerCasing
- type: latheRecipe
parent: BaseBorgModuleRecipe
id: AugmentToolPanelCasing
result: AugmentToolPanelCasing
- type: latheRecipe
parent: BaseBorgModuleRecipe
id: AugmentUtilityPanelElectronics
result: AugmentUtilityPanelElectronics
- type: latheRecipe
parent: BaseBorgModuleRecipe
id: AugmentBotanyPanelElectronics
result: AugmentBotanyPanelElectronics
- type: latheRecipe
parent: BaseBorgModuleRecipe
id: AugmentSurgeryPanelElectronics
result: AugmentSurgeryPanelElectronics
- type: latheRecipe
parent: BaseBorgModuleRecipe
id: AugmentPaperworkPanelElectronics
result: AugmentPaperworkPanelElectronics

View File

@ -10,3 +10,34 @@
recipeUnlocks:
- LauncherSyringe
- MiniSyringe
- type: technology
id: BasicAugmentation
name: research-technology-basic-augmentation
icon:
sprite: _DV/Objects/Augments/power.rsi
state: cell
discipline: CivilianServices
tier: 2
cost: 5000
recipeUnlocks:
- AugmentPowerCellSlotCasing
- AugmentRechargerCasing
- type: technology
id: ImplantedTools
name: research-technology-implanted-tools
icon:
sprite: _DV/Objects/Augments/arm.rsi
state: base
discipline: CivilianServices
tier: 2
cost: 10000
technologyPrerequisites: [ BasicAugmentation ]
recipeUnlocks:
- AugmentToolPanelCasing
- AugmentUtilityPanelElectronics
- AugmentBotanyPanelElectronics
- AugmentSurgeryPanelElectronics
- AugmentPaperworkPanelElectronics

View File

@ -1,5 +1,32 @@
## This is for Nyano and Delta V tags
- type: Tag
id: AugmentAPCBoard
- type: Tag
id: AugmentBotanyToolBelt
- type: Tag
id: AugmentCellCasing
- type: Tag
id: AugmentChargerCasing
- type: Tag
id: AugmentPaperworkToolBelt
- type: Tag
id: AugmentStationBoard
- type: Tag
id: AugmentSurgeryToolBelt
- type: Tag
id: AugmentToolBelt
- type: Tag
id: AugmentToolPanelCasing
- type: Tag
id: Bayonet #Craftable Musket

View File

@ -519,6 +519,30 @@
toolDirtiness: 7.5
gloveDirtiness: 7.5
- type: entity
parent: SurgeryStepBase
id: SurgeryStepOpenOrganSlot
name: Carve out a cavity
categories: [ HideSpawnMenu ]
components:
- type: SurgeryStep
tool:
- type: Scalpel
duration: 6
- type: Sprite
sprite: _Shitmed/Objects/Specific/Medical/Surgery/manipulation.rsi
state: insertion
- type: SurgeryAddOrganSlotStep
- type: SurgeryStepEmoteEffect
- type: SurgeryDamageChangeEffect # DeltaV
sleepModifier: 0.5
damage:
types:
Blunt: 5
- type: SurgeryStepDirtiness # DeltaV - surgery cross contamination
toolDirtiness: 7.5
gloveDirtiness: 7.5
- type: entity
parent: SurgeryStepInsertOrgan
id: SurgeryStepInsertLungs

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

View File

@ -0,0 +1,29 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from /tg/ station at commit https://github.com/tgstation/tgstation/blob/c7e20fe50f0185e7f1898b518ac8715c47ac56b0/icons/obj/medical/organs/organs.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "base"
},
{
"name": "base_cables"
},
{
"name": "botany"
},
{
"name": "paperwork"
},
{
"name": "engineering"
},
{
"name": "surgical"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

View File

@ -0,0 +1,32 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from /tg/ station at commit https://github.com/tgstation/tgstation/commit/02756c2bc2cf3000080d030955e994242bab39b5",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "apc"
},
{
"name": "cell"
},
{
"name": "station"
},
{
"name": "incomplete_charger"
},
{
"name": "incomplete_charger_cables"
},
{
"name": "incomplete_cell"
},
{
"name": "incomplete_cell_cables"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B