Merge branch 'master' into tabletop-ops

This commit is contained in:
Stxcking 2026-06-09 14:16:38 -04:00
commit 30fe529007
3063 changed files with 195954 additions and 146039 deletions

2
.github/CODEOWNERS vendored
View File

@ -19,7 +19,7 @@
/Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers
# Server rules
/Resources/ServerInfo/Guidebook/DeltaV/Rules/ @DeltaV-Station/head-administrators
/Resources/ServerInfo/Guidebook/_DV/Rules/ @DeltaV-Station/head-administrators
# Wiki
/.wiki/ @DeltaV-Station/direction

View File

@ -289,4 +289,33 @@ Emergency alert status. ''Central Command has ordered the Epsilon security level
| style="border: 1px solid #000000;" | Martial Law is in effect. Do not engage with Central Command Personnel. Await orders from Central Command Personnel.
|}
== <span style="color:#ff8119;>Orange Alert</span> ==
Emergency alert status. ''There is a critical station-wide structural or atmospheric threat and recovery is unlikely. Engineering staff are advised to minimize hazards and secure the Evacuation Dock. Crewmembers are advised to stay away from hazardous areas, and prepare for Evacuation.''
{| class="wikitable" style="width: 80%; border: 1px solid #000000;"
!style="text-align:left; width:25%; padding:5px; padding-left:10px; background-color:#FF8119BF; border: 1px solid #000000;" | '''Condition'''
!style="text-align:left; width:75%; padding:5px; padding-left:10px; background-color:#FF8119BF; border: 1px solid #000000;" | '''Description'''
|-
| style="border: 1px solid #000000;" | Chain of Command
| style="border: 1px solid #000000;" | [[Standard_Operating_Procedure#Captains_Authority|CO]] > [[Chief Engineer]] > Engineering Personnel > Department Heads > Supervisory Roles
|-
| style="border: 1px solid #000000;" | Armoury Policy
| style="border: 1px solid #000000;" | Issuing of lethal weapons disadvised. EVA protection heavily advised for all personnel.
|-
| style="border: 1px solid #000000;" | Discipline
| style="border: 1px solid #000000;" | Report to supervisor for orders. Non-engineering personnel advised to evacuate affected areas. Security should seek to aid engineering staff where possible.
|-
| style="border: 1px solid #000000;" | Secure Areas
| style="border: 1px solid #000000;" | Secure areas unbolted. [[Standard_Operating_Procedure#High_Security_Areas|HSAs]] and areas with an expectation of privacy, such as bedrooms, dorms, or medical treatment facilities, may be bolted as needed.
|-
| style="border: 1px solid #000000;" | Medical
| style="border: 1px solid #000000;" | Full suit sensors heavily advised. EVA suits and functioning internals recommended for emergency responders.
|-
| style="border: 1px solid #000000;" | Engineering
| style="border: 1px solid #000000;" | EVA suits heavily advised for engineers. Distribute emergency internals to crew. Ensure evacuation as soon as possible.
|-
| style="border: 1px solid #000000;" | Rules of Engagement
| style="border: 1px solid #000000;" | Engage with minimal force required; prioritize de-escalation whenever possible.
|}
{{Guides Menu}}

View File

@ -269,7 +269,6 @@ Prisoners have certain rights that must be upheld by law enforcement and Station
* Standard prisoner clothing must always be available for prisoners.
* Confiscated clothing should be returned upon request, except in the case of Extended Confinement or if the clothing in question poses a clear risk to prisoner or crew safety.
* Food and water for organic prisoners must be available and given on request.
* Power for silicon prisoners such as IPCs and cyborgs, at minimum a cyborg recharging station must be provided.
* Should the holding cells or Permabrig become uninhabitable, prisoners must be securely and safely relocated to another area for confinement, until the holding cells or Permabrig are returned to a serviceable state.
* Prisoners should be granted freedom of movement within their holding area unless there is an undue risk to life and limb.

View File

@ -8,12 +8,14 @@ using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Content.Shared.Mind;
using Content.Shared.Roles;
using Content.Shared.SSDIndicator; // DeltaV - SSD time indicator
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing; // DeltaV - SSD time indicator
namespace Content.Client.Administration;
@ -26,6 +28,7 @@ internal sealed class AdminNameOverlay : Overlay
private readonly IUserInterfaceManager _userInterfaceManager;
private readonly SharedRoleSystem _roles;
private readonly IPrototypeManager _prototypeManager;
private readonly IGameTiming _timing; // DeltaV - Add timing
private readonly Font _font;
private readonly Font _fontBold;
private AdminOverlayAntagFormat _overlayFormat;
@ -53,7 +56,8 @@ internal sealed class AdminNameOverlay : Overlay
IUserInterfaceManager userInterfaceManager,
IConfigurationManager config,
SharedRoleSystem roles,
IPrototypeManager prototypeManager)
IPrototypeManager prototypeManager,
IGameTiming timing) // DeltaV - Add timing
{
_system = system;
_entityManager = entityManager;
@ -62,6 +66,7 @@ internal sealed class AdminNameOverlay : Overlay
_userInterfaceManager = userInterfaceManager;
_roles = roles;
_prototypeManager = prototypeManager;
_timing = timing; // DeltaV - Add timing
ZIndex = 200;
// Setting these to a specific ttf would break the antag symbols
_font = resourceCache.NotoStack();
@ -231,6 +236,17 @@ internal sealed class AdminNameOverlay : Overlay
currentOffset += lineoffset;
}
// DeltaV - SSD Time START
if (_entityManager.TryGetComponent<SSDIndicatorComponent>(entity, out var ssdIndicator) && ssdIndicator.IsSSD)
{
color = Color.MediumPurple;
color.A = alpha;
var ssdText = Loc.GetString("admin-overlay-ssd-time", ("time", (_timing.CurTime - ssdIndicator.SsdSince).ToString("%hh':'mm':'ss")));
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, ssdText, uiScale, color);
currentOffset += lineoffset;
}
// DeltaV END
// Determine antag symbol
string? symbol;
switch (_overlaySymbolStyle)

View File

@ -5,6 +5,7 @@ using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing; // DeltaV - SSD time indicator
namespace Content.Client.Administration.Systems
{
@ -19,6 +20,7 @@ namespace Content.Client.Administration.Systems
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IGameTiming _timing = default!; // DeltaV - added for SSD time indicator
private AdminNameOverlay _adminNameOverlay = default!;
@ -36,7 +38,8 @@ namespace Content.Client.Administration.Systems
_userInterfaceManager,
_configurationManager,
_roles,
_proto);
_proto,
_timing); // DeltaV - Add timing
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
}

View File

@ -1,76 +1,7 @@
using Content.Shared.Body.Systems;
// Shitmed Change Start
using Content.Shared._Shitmed.Body.Part;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
using Content.Shared.Body.Components;
// Shitmed Change End
namespace Content.Client.Body.Systems;
public sealed class BodySystem : SharedBodySystem
{
// Shitmed Change Start
[Dependency] private readonly MarkingManager _markingManager = default!;
private void ApplyMarkingToPart(MarkingPrototype markingPrototype,
IReadOnlyList<Color>? colors,
bool visible,
SpriteComponent sprite)
{
for (var j = 0; j < markingPrototype.Sprites.Count; j++)
{
var markingSprite = markingPrototype.Sprites[j];
if (markingSprite is not SpriteSpecifier.Rsi rsi)
continue;
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
if (!sprite.LayerMapTryGet(layerId, out _))
{
var layer = sprite.AddLayer(markingSprite, j + 1);
sprite.LayerMapSet(layerId, layer);
sprite.LayerSetSprite(layerId, rsi);
}
sprite.LayerSetVisible(layerId, visible);
if (!visible)
continue;
// Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
// and we need to check the index is correct. So if that happens just default to white?
if (colors != null && j < colors.Count)
sprite.LayerSetColor(layerId, colors[j]);
else
sprite.LayerSetColor(layerId, Color.White);
}
}
protected override void ApplyPartMarkings(EntityUid target, BodyPartAppearanceComponent component)
{
if (!TryComp(target, out SpriteComponent? sprite))
return;
if (component.Color != null)
sprite.Color = component.Color.Value;
foreach (var (visualLayer, markingList) in component.Markings)
foreach (var marking in markingList)
{
if (!_markingManager.TryGetMarking(marking, out var markingPrototype))
continue;
ApplyMarkingToPart(markingPrototype, marking.MarkingColors, marking.Visible, sprite);
}
}
protected override void RemoveBodyMarkings(EntityUid target, BodyPartAppearanceComponent partAppearance, HumanoidAppearanceComponent bodyAppearance)
{
return;
}
// Shitmed Change End
}

View File

@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.DisplacementMap;
using Content.Client.Inventory;
using Content.Shared._DV.Silicon.IPC; // DeltaV - IPC Snouts
using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
@ -103,12 +102,8 @@ public sealed class ClientClothingSystem : ClothingSystem
List<PrototypeLayerData>? layers = null;
// Begin DeltaV Additions - IPC snouts
var speciesId = inventory.SpeciesId;
if (TryComp(args.Equipee, out SnoutHelmetComponent? helmetComponent) && helmetComponent.EnableAlternateHelmet)
speciesId = helmetComponent.ReplacementRace;
// first attempt to get species specific data.
if (speciesId != null)
item.ClothingVisuals.TryGetValue($"{args.Slot}-{speciesId}", out layers);
@ -120,7 +115,6 @@ public sealed class ClientClothingSystem : ClothingSystem
if (!TryGetDefaultVisuals(uid, item, args.Slot, speciesId, out layers))
return;
}
// End DeltaV Additions
// add each layer to the visuals
var i = 0;

View File

@ -18,8 +18,36 @@ public sealed class DoorSystem : SharedDoorSystem
{
base.Initialize();
SubscribeLocalEvent<DoorComponent, AppearanceChangeEvent>(OnAppearanceChange);
// ES START
// handle open/close state change on client after animation end, not when server says door is open
SubscribeLocalEvent<DoorComponent, AnimationCompletedEvent>(OnAnimationCompleted);
}
private void OnAnimationCompleted(Entity<DoorComponent> entity, ref AnimationCompletedEvent args)
{
if (!TryComp<SpriteComponent>(entity, out var sprite) || args.Key != DoorComponent.OpenCloseKey)
return;
switch (entity.Comp.State)
{
case DoorState.Open:
foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates)
{
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
}
break;
case DoorState.Closed:
foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates)
{
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
}
break;
}
}
// ES END
protected override void OnComponentInit(Entity<DoorComponent> ent, ref ComponentInit args)
{
var comp = ent.Comp;
@ -87,10 +115,13 @@ public sealed class DoorSystem : SharedDoorSystem
state = DoorState.Closed;
if (AppearanceSystem.TryGetData<string>(entity, PaintableVisuals.Prototype, out var prototype, args.Component))
UpdateSpriteLayers((entity.Owner, args.Sprite), prototype);
UpdateSpriteLayers((entity.Owner, args.Sprite), prototype);
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
// ES START
// dont stop all animations for no reason
//if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
// _animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
// ES END
// We are checking beforehand since some doors may not have an emagging visual layer, and we don't want LayerSetVisible to throw an error.
if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false))
@ -106,6 +137,12 @@ public sealed class DoorSystem : SharedDoorSystem
switch (state)
{
case DoorState.Open:
// ES START
// If we are already animating the close just let that do its job
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
return;
// ES END
foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates)
{
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
@ -113,6 +150,12 @@ public sealed class DoorSystem : SharedDoorSystem
return;
case DoorState.Closed:
// ES START
// If we are already animating the close just let that do its job
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
return;
// ES END
foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates)
{
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
@ -123,24 +166,47 @@ public sealed class DoorSystem : SharedDoorSystem
if (entity.Comp.OpeningAnimationTime == TimeSpan.Zero)
return;
_animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.AnimationKey);
// ES START
// since we dont stop them earlier we check here
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
return;
// ES END
_animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.OpenCloseKey);
return;
case DoorState.Closing:
if (entity.Comp.ClosingAnimationTime == TimeSpan.Zero || entity.Comp.CurrentlyCrushing.Count != 0)
return;
_animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.AnimationKey);
// ES START
// since we dont stop them earlier we check here
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
return;
// ES END
_animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.OpenCloseKey);
return;
case DoorState.Denying:
_animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.AnimationKey);
// ES START
// AnimationKey -> DenyKey
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.DenyKey))
return;
_animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.DenyKey);
// ES END
return;
case DoorState.Emagging:
// We are checking beforehand since some doors may not have an emagging visual layer.
// ES START
// AnimationKey -> DenyKey
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.EmagKey))
return;
if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false))
_animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.AnimationKey);
_animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.EmagKey);
// ES END
return;
}

View File

@ -36,6 +36,9 @@ public sealed class EyeLerpingSystem : EntitySystem
UpdatesAfter.Add(typeof(TransformSystem));
UpdatesAfter.Add(typeof(Robust.Client.Physics.PhysicsSystem));
UpdatesBefore.Add(typeof(SharedEyeSystem));
// ES START
UpdatesBefore.Add(typeof(EyeSystem));
// ES END
UpdatesOutsidePrediction = true;
}
@ -68,6 +71,11 @@ public sealed class EyeLerpingSystem : EntitySystem
{
_eye.SetRotation(uid, lerpInfo.TargetRotation, component);
_eye.SetZoom(uid, lerpInfo.TargetZoom, component);
// Starlight begin - fix ES Screenshake triggering on spawn
if (TryComp<ContentEyeComponent>(uid, out var contentEye))
contentEye.BaseRotation = lerpInfo.TargetRotation;
// Starlight end
}
}
@ -175,9 +183,15 @@ public sealed class EyeLerpingSystem : EntitySystem
{
var tickFraction = (float) _gameTiming.TickFraction / ushort.MaxValue;
const double lerpMinimum = 0.00001;
var query = AllEntityQuery<LerpingEyeComponent, EyeComponent, TransformComponent>();
// ES START
// add contenteye bc we only want to modify the fuckin base rotation
var query = AllEntityQuery<LerpingEyeComponent, EyeComponent, ContentEyeComponent, TransformComponent>();
// ES END
while (query.MoveNext(out var entity, out var lerpInfo, out var eye, out var xform))
// ES START
// contenteye
while (query.MoveNext(out var entity, out var lerpInfo, out var eye, out var contentEye, out var xform))
// ES END
{
// Handle zoom
var zoomDiff = Vector2.Lerp(lerpInfo.LastZoom, lerpInfo.TargetZoom, tickFraction);
@ -191,6 +205,14 @@ public sealed class EyeLerpingSystem : EntitySystem
_eye.SetZoom(entity, zoomDiff, eye);
}
// ES START
// UHHHHHHHHHHHh
// shit is fucked idk
// we need to alter baserotation not regular eye rotation and also i added baserotation because uhh
// there was no good way to separate 'how the eye should be oriented because of the grid/turning/etc' and 'the eye rotating on top of that'
// had to add it
// ES END
// Handle Rotation
TryComp<InputMoverComponent>(entity, out var mover);
@ -199,7 +221,10 @@ public sealed class EyeLerpingSystem : EntitySystem
if (!NeedsLerp(mover))
{
_eye.SetRotation(entity, lerpInfo.TargetRotation, eye);
// ES START
// contenteye
contentEye.BaseRotation = lerpInfo.TargetRotation;
// ES END
continue;
}
@ -207,11 +232,17 @@ public sealed class EyeLerpingSystem : EntitySystem
if (Math.Abs(shortest.Theta) < lerpMinimum)
{
_eye.SetRotation(entity, lerpInfo.TargetRotation, eye);
// ES START
// contenteye
contentEye.BaseRotation = lerpInfo.TargetRotation;
// ES END
continue;
}
_eye.SetRotation(entity, shortest * tickFraction + lerpInfo.LastRotation, eye);
// ES START
// contenteye
contentEye.BaseRotation = shortest * tickFraction + lerpInfo.LastRotation;
// ES END
}
}
}

View File

@ -1,11 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared._Shitmed.Body.Events; // Shitmed Change
using Content.Client.DisplacementMap;
using Content.Client.Examine;
using Content.Client.Strip;
using Content.Client.Verbs.UI;
using Content.Shared.Body.Part; // Shitmed Change
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
@ -51,8 +49,6 @@ namespace Content.Client.Hands.Systems
SubscribeLocalEvent<HandsComponent, ComponentShutdown>(OnHandsShutdown);
SubscribeLocalEvent<HandsComponent, ComponentHandleState>(HandleComponentState);
SubscribeLocalEvent<HandsComponent, VisualsChangedEvent>(OnVisualsChanged);
SubscribeLocalEvent<HandsComponent, BodyPartRemovedEvent>(HandleBodyPartRemoved); // Shitmed Change
SubscribeLocalEvent<HandsComponent, BodyPartDisabledEvent>(HandleBodyPartDisabled); // Shitmed Change
OnHandSetActive += OnHandActivated;
}
@ -206,35 +202,6 @@ namespace Content.Client.Hands.Systems
#region visuals
// Shitmed Change Start
private void HideLayers(EntityUid uid, HandsComponent component, Entity<BodyPartComponent> part, SpriteComponent? sprite = null)
{
if (part.Comp.PartType != BodyPartType.Hand || !Resolve(uid, ref sprite, logMissing: false))
return;
var location = part.Comp.Symmetry switch
{
BodyPartSymmetry.None => HandLocation.Middle,
BodyPartSymmetry.Left => HandLocation.Left,
BodyPartSymmetry.Right => HandLocation.Right,
_ => throw new ArgumentOutOfRangeException(nameof(part.Comp.Symmetry))
};
if (component.RevealedLayers.TryGetValue(location, out var revealedLayers))
{
foreach (var key in revealedLayers)
sprite.RemoveLayer(key);
revealedLayers.Clear();
}
}
private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args) => HideLayers(uid, component, args.Part);
private void HandleBodyPartDisabled(EntityUid uid, HandsComponent component, ref BodyPartDisabledEvent args) => HideLayers(uid, component, args.Part);
// Shitmed Change End
protected override void HandleEntityInserted(EntityUid uid, HandsComponent hands, EntInsertedIntoContainerMessage args)
{
base.HandleEntityInserted(uid, hands, args);

View File

@ -1,7 +1,6 @@
using Content.Shared.MedicalScanner;
using Content.Shared._Shitmed.Targeting; // Shitmed Change
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
using JetBrains.Annotations;
using Content.Shared._DV.MedicalRecords;
using JetBrains.Annotations; // DeltaV - Medical Records
using Robust.Client.UserInterface;
namespace Content.Client.HealthAnalyzer.UI
@ -21,13 +20,11 @@ namespace Content.Client.HealthAnalyzer.UI
base.Open();
_window = this.CreateWindow<HealthAnalyzerWindow>();
_window.HealthAnalyzer.OnBodyPartSelected += SendBodyPartMessage; // Shitmed Change
_window.HealthAnalyzer.OnTriageStatusChanged += SendTriageStatusMessage; // DeltaV - Medical Records
_window.HealthAnalyzer.OnClaimPatient += SendTriageClaimMessage; // DeltaV - Medical Records
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
if (_window == null)
@ -39,22 +36,6 @@ namespace Content.Client.HealthAnalyzer.UI
_window.Populate(cast);
}
// Shitmed Change Start
private void SendBodyPartMessage(TargetBodyPart? part, EntityUid target) => SendMessage(new HealthAnalyzerPartMessage(EntMan.GetNetEntity(target), part ?? null));
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
if (_window != null)
_window.HealthAnalyzer.OnBodyPartSelected -= SendBodyPartMessage;
_window?.Dispose();
}
// Shitmed Change End
// Begin DeltaV - Medical Records
private void SendTriageStatusMessage(TriageStatus status)
=> SendMessage(new HealthAnalyzerTriageStatusMessage(status));

View File

@ -6,160 +6,12 @@
Name="NoPatientDataText"
Text="{Loc health-analyzer-window-no-patient-data-text}" />
<!-- Shitmed Change Start -->
<Button Name="ReturnButton"
Text="{Loc 'health-analyzer-window-return-button-text'}"
Margin="0 0 0 10"
HorizontalExpand="False"/>
<!-- Shitmed Change End -->
<BoxContainer
Name="PatientDataContainer"
Margin="0 0 0 5"
Orientation="Vertical">
<BoxContainer Orientation="Horizontal" Margin="0 0 0 5">
<PanelContainer>
<!-- Shitmed Change Start -->
<SpriteView OverrideDirection="South" Name="SpriteView" Access="Public" SetSize="96 96" />
<PanelContainer
Name="PartView"
SetSize="57 96"
Margin="18 0 0 0"
VerticalAlignment="Center"
HorizontalAlignment="Left">
<PanelContainer
SetSize="15 33"
Margin="4 0 0 0"
HorizontalAlignment="Left">
<TextureButton
Name="RightArmButton"
MinSize="15 25"
StyleClasses="TargetDollButtonRightArm"
VerticalAlignment="Top">
</TextureButton>
<TextureButton
Name="RightHandButton"
MinSize="15 15"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonRightHand">
</TextureButton>
</PanelContainer>
<PanelContainer
SetSize="43 75"
Margin="0 0 0 0"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextureButton
Name="HeadButton"
MinSize="28 23"
VerticalAlignment="Top"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonHead">
<!--<PanelContainer
SetSize="14 15"
Margin="-1 9 0 0"
HorizontalAlignment="Center">
<TextureButton
Name="EyesButton"
MinSize="14 9"
VerticalAlignment="Top"
StyleClasses="TargetDollButtonEyes">
<TextureRect
TexturePath="/Textures/Interface/Targeting/Doll/eyes.png"
Stretch="KeepAspectCentered"
SetSize="14 9"/>
</TextureButton>
<TextureButton
Name="MouthButton"
SetSize="8 6"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonMouth">
<TextureRect
TexturePath="/Textures/Interface/Targeting/Doll/mouth.png"
Stretch="KeepAspectCentered"
SetSize="8 6"/>
</TextureButton>
</PanelContainer>-->
</TextureButton>
<TextureButton
Name="ChestButton"
SetSize="28 30"
Margin="0 18 0 0"
VerticalAlignment="Top"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonChest">
</TextureButton>
<PanelContainer
MinSize="38 35"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
<TextureButton
Name="GroinButton"
MinSize="28 15"
VerticalAlignment="Top"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonGroin">
</TextureButton>
<PanelContainer
MinSize="20 30"
VerticalAlignment="Bottom"
HorizontalAlignment="Right">
<TextureButton
Name="LeftLegButton"
MinSize="15 28"
VerticalAlignment="Top"
HorizontalAlignment="Left"
StyleClasses="TargetDollButtonLeftLeg">
</TextureButton>
<TextureButton
Name="LeftFootButton"
MinSize="20 10"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonLeftFoot">
</TextureButton>
</PanelContainer>
<PanelContainer
MinSize="20 30"
VerticalAlignment="Bottom"
HorizontalAlignment="Left">
<TextureButton
Name="RightLegButton"
MinSize="15 28"
VerticalAlignment="Top"
HorizontalAlignment="Right"
StyleClasses="TargetDollButtonRightLeg">
</TextureButton>
<TextureButton
Name="RightFootButton"
MinSize="20 10"
VerticalAlignment="Bottom"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonRightFoot">
</TextureButton>
</PanelContainer>
</PanelContainer>
</PanelContainer>
<PanelContainer
SetSize="15 33"
Margin="0 0 4 0"
HorizontalAlignment="Right">
<TextureButton
Name="LeftArmButton"
MinSize="15 25"
StyleClasses="TargetDollButtonLeftArm"
VerticalAlignment="Top">
</TextureButton>
<TextureButton
Name="LeftHandButton"
MinSize="15 15"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonLeftHand">
</TextureButton>
</PanelContainer>
</PanelContainer>
</PanelContainer>
<!-- Shitmed Change Start -->
<TextureRect Name="NoDataTex" Access="Public" SetSize="96 96" Visible="false" Stretch="KeepAspectCentered" TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/> <!-- DeltaV - Size changed to 96 96 to be consistent with shitmed -->
<TextureRect Name="NoDataTex" Access="Public" SetSize="96 96" Visible="false" Stretch="KeepAspectCentered" TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/> <!-- DeltaV - Size changed to 96-->
<BoxContainer Margin="5 0 0 0" Orientation="Vertical" VerticalAlignment="Top">
<RichTextLabel Name="NameLabel" SetWidth="150" />
<Label Name="SpeciesLabel" VerticalAlignment="Top" StyleClasses="LabelSubText" />

View File

@ -24,7 +24,6 @@ using Content.Client._DV.Traits.Assorted; // DeltaV
using Content.Shared._DV.Traits.Assorted; // DeltaV
using Content.Shared._DV.Medical; // DeltaV - Uncloneable
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
using Content.Shared._Shitmed.Targeting; // Shitmed
// Health analyzer UI is split from its window because it's used by both the
// health analyzer item and the cryo pod UI.
@ -41,16 +40,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
private readonly RedshirtSystem _redshirt; // DeltaV
private readonly UncloneableSystem _uncloneable; // DeltaV
// Shitmed Change Start
public event Action<TargetBodyPart?, EntityUid>? OnBodyPartSelected;
private EntityUid _spriteViewEntity;
private readonly EntProtoId _bodyView = "AlertSpriteView";
private readonly Dictionary<TargetBodyPart, TextureButton> _bodyPartControls;
private EntityUid? _target;
// Shitmed Change End
// Begin DeltaV - Medical Records
private readonly ButtonGroup _triageStatusGroup = new();
private readonly Dictionary<TriageStatus, Button> _triageControls = new();
@ -72,29 +61,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
_unborgable = _entityManager.System<UnborgableSystem>(); // DeltaV
_redshirt = _entityManager.System<RedshirtSystem>(); // DeltaV
_uncloneable = _entityManager.System<UncloneableSystem>(); // DeltaV
// Shitmed Change Start
_bodyPartControls = new Dictionary<TargetBodyPart, TextureButton>
{
{ TargetBodyPart.Head, HeadButton },
{ TargetBodyPart.Torso, ChestButton },
{ TargetBodyPart.Groin, GroinButton },
{ TargetBodyPart.LeftArm, LeftArmButton },
{ TargetBodyPart.LeftHand, LeftHandButton },
{ TargetBodyPart.RightArm, RightArmButton },
{ TargetBodyPart.RightHand, RightHandButton },
{ TargetBodyPart.LeftLeg, LeftLegButton },
{ TargetBodyPart.LeftFoot, LeftFootButton },
{ TargetBodyPart.RightLeg, RightLegButton },
{ TargetBodyPart.RightFoot, RightFootButton },
};
foreach (var bodyPartButton in _bodyPartControls)
{
bodyPartButton.Value.MouseFilter = MouseFilterMode.Stop;
bodyPartButton.Value.OnPressed += _ => SetActiveBodyPart(bodyPartButton.Key, bodyPartButton.Value);
}
ReturnButton.OnPressed += _ => ResetBodyPart();
// Shitmed Change End
// Begin DeltaV - Medical Records
foreach (var item in Enum.GetValues<TriageStatus>())
@ -120,29 +86,14 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
public void Populate(HealthAnalyzerUiState state)
{
var target = _entityManager.GetEntity(state.TargetEntity);
// Begin Shitmed
_target = target;
EntityUid? part = state.Part != null ? _entityManager.GetEntity(state.Part.Value) : null;
var isPart = part != null;
// End Shitmed
if (target == null
|| !_entityManager.TryGetComponent<DamageableComponent>(isPart ? part : target, out var damageable)) // Shitmed
|| !_entityManager.TryGetComponent<DamageableComponent>(target, out var damageable))
{
NoPatientDataText.Visible = true;
return;
}
// Begin Shitmed
ReturnButton.Visible = isPart;
PartNameLabel.Visible = isPart;
if (part != null)
PartNameLabel.Text = _entityManager.HasComponent<MetaDataComponent>(part)
? Identity.Name(part.Value, _entityManager)
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
// End Shitmed
NoPatientDataText.Visible = false;
// Scan Mode
@ -157,11 +108,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
// Patient Information
SpriteView.SetEntity(SetupIcon(state.Body) ?? target.Value); // Shitmed Change
SpriteView.Visible = state.ScanMode.HasValue && state.ScanMode.Value;
PartView.Visible = SpriteView.Visible; // Shitmed Change
NoDataTex.Visible = !SpriteView.Visible;
var name = new FormattedMessage();
name.PushColor(Color.White);
name.AddText(_entityManager.HasComponent<MetaDataComponent>(target.Value)
@ -338,65 +284,6 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
}
}
// Shitmed Change Start
public void SetActiveBodyPart(TargetBodyPart part, TextureButton button)
{
if (_target == null)
return;
// Bit of the ole shitcode until we have Groins in the prototypes.
OnBodyPartSelected?.Invoke(part == TargetBodyPart.Groin ? TargetBodyPart.Torso : part, _target.Value);
}
public void ResetBodyPart()
{
if (_target == null)
return;
OnBodyPartSelected?.Invoke(null, _target.Value);
}
public void SetActiveButtons(bool isHumanoid)
{
foreach (var button in _bodyPartControls)
button.Value.Visible = isHumanoid;
}
/// <summary>
/// Sets up the Body Doll using Alert Entity to use in Health Analyzer.
/// </summary>
private EntityUid? SetupIcon(Dictionary<TargetBodyPart, TargetIntegrity>? body)
{
if (body is null)
return null;
if (!_entityManager.Deleted(_spriteViewEntity))
_entityManager.QueueDeleteEntity(_spriteViewEntity);
_spriteViewEntity = _entityManager.Spawn(_bodyView);
if (!_entityManager.TryGetComponent<SpriteComponent>(_spriteViewEntity, out var sprite))
return null;
int layer = 0;
foreach (var (bodyPart, integrity) in body)
{
// TODO: PartStatusUIController and make it use layers instead of TextureRects when EE refactors alerts.
string enumName = Enum.GetName(typeof(TargetBodyPart), bodyPart) ?? "Unknown";
int enumValue = (int) integrity;
var rsi = new SpriteSpecifier.Rsi(new ResPath($"/Textures/_Shitmed/Interface/Targeting/Status/{enumName.ToLowerInvariant()}.rsi"), $"{enumName.ToLowerInvariant()}_{enumValue}");
// Shitcode with love from Russia :)
if (!_spriteSystem.TryGetLayer(_spriteViewEntity, layer, out _, false))
_spriteSystem.AddTextureLayer(_spriteViewEntity, _spriteSystem.Frame0(rsi));
else
_spriteSystem.LayerSetTexture(_spriteViewEntity, layer, _spriteSystem.Frame0(rsi));
_spriteSystem.LayerSetScale(_spriteViewEntity, layer, new Vector2(3f, 3f));
layer++;
}
return _spriteViewEntity;
}
// Shitmed Change End
private Texture GetTexture(string texture)
{
var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");

View File

@ -3,7 +3,7 @@
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:ui="clr-namespace:Content.Client.HealthAnalyzer.UI"
MaxHeight="525"
MinWidth="350"> <!-- Shitmed Change -->
MinWidth="300">
<ScrollContainer
Margin="5 5 5 5"
ReturnMeasure="True"
@ -11,6 +11,6 @@
<ui:HealthAnalyzerControl
Name="HealthAnalyzer"
Access="Public" /> <!-- DeltaV / Shitmed Change - Make Public-->
Access="Public" /> <!-- DeltaV -->
</ScrollContainer>
</controls:FancyWindow>

View File

@ -88,8 +88,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
foreach (var (key, info) in component.CustomBaseLayers)
{
oldLayers.Remove(key);
// Shitmed Change: For whatever reason these weren't actually ignoring the skin color as advertised.
SetLayerData(entity, key, info.Id, sexMorph: false, color: info.Color, overrideSkin: true);
SetLayerData(entity, key, info.Id, sexMorph: false, color: info.Color);
}
// hide old layers
@ -106,8 +105,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
HumanoidVisualLayers key,
string? protoId,
bool sexMorph = false,
Color? color = null,
bool overrideSkin = false) // Shitmed Change
Color? color = null)
{
var component = entity.Comp1;
var sprite = entity.Comp2;
@ -128,7 +126,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
var proto = _prototypeManager.Index<HumanoidSpeciesSpriteLayer>(protoId);
component.BaseLayers[key] = proto;
if (proto.MatchSkin && !overrideSkin) // Shitmed Change
if (proto.MatchSkin)
layer.Color = component.SkinColor.WithAlpha(proto.LayerAlpha);
if (proto.BaseSprite != null)

View File

@ -98,18 +98,6 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.Arcade1);
human.AddFunction(ContentKeyFunctions.Arcade2);
human.AddFunction(ContentKeyFunctions.Arcade3);
// Shitmed Change Start - TODO: Add hands, feet and groin targeting.
human.AddFunction(ContentKeyFunctions.TargetHead);
human.AddFunction(ContentKeyFunctions.TargetTorso);
human.AddFunction(ContentKeyFunctions.TargetLeftArm);
human.AddFunction(ContentKeyFunctions.TargetLeftHand);
human.AddFunction(ContentKeyFunctions.TargetRightArm);
human.AddFunction(ContentKeyFunctions.TargetRightHand);
human.AddFunction(ContentKeyFunctions.TargetLeftLeg);
human.AddFunction(ContentKeyFunctions.TargetLeftFoot);
human.AddFunction(ContentKeyFunctions.TargetRightLeg);
human.AddFunction(ContentKeyFunctions.TargetRightFoot);
// Shitmed Change End
// actions should be common (for ghosts, mobs, etc)
common.AddFunction(ContentKeyFunctions.OpenActionsMenu);

View File

@ -6,7 +6,6 @@ using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Storage;
using Content.Shared._Shitmed.Targeting.Events; // Shitmed
using JetBrains.Annotations;
using Robust.Client.Player;
using Robust.Client.UserInterface;

View File

@ -60,6 +60,9 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
{
UpdateEyeOffset((entity, eyeComponent));
// ES START
UpdateEyeRotation((entity, eyeComponent));
// ES END
}
}
@ -71,6 +74,9 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent))
{
UpdateEyeOffset((entity, eyeComponent));
// ES START
UpdateEyeRotation((entity, eyeComponent));
// ES END
}
}
}

View File

@ -7,6 +7,7 @@
<Label Text="{Loc 'ui-options-accessability-header-visuals'}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="ReducedMotionCheckBox" Text="{Loc 'ui-options-reduced-motion'}" />
<CheckBox Name="DisableScreenshakeCheckBox" Text="{Loc 'ui-options-es-screen-shake-toggle'}" /> <!-- DeltaV - ES Screenshake Port -->
<CheckBox Name="EnableColorNameCheckBox" Text="{Loc 'ui-options-enable-color-name'}" />
<CheckBox Name="ColorblindFriendlyCheckBox" Text="{Loc 'ui-options-colorblind-friendly'}" />
<ui:OptionSlider Name="ScreenShakeIntensitySlider" Title="{Loc 'ui-options-screen-shake-intensity'}" />

View File

@ -1,3 +1,4 @@
using Content.Shared._DV.CCVars; // DeltaV - ES Screenshake Port
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
@ -15,6 +16,7 @@ public sealed partial class AccessibilityTab : Control
Control.AddOptionCheckBox(CCVars.ChatEnableColorName, EnableColorNameCheckBox);
Control.AddOptionCheckBox(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox);
Control.AddOptionCheckBox(CCVars.ReducedMotion, ReducedMotionCheckBox);
Control.AddOptionCheckBox(DCCVars.EsScreenshakeDisabled, DisableScreenshakeCheckBox); // DeltaV - ES Screenshake Port
Control.AddOptionPercentSlider(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider);
Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider);
Control.AddOptionPercentSlider(CCVars.SpeechBubbleTextOpacity, SpeechBubbleTextOpacitySlider);

View File

@ -1,6 +1,5 @@
using System.Numerics;
using Content.Client.Stylesheets;
using Content.Shared._Goobstation.CCVar; // GoobStation
using Content.Shared.CCVar;
using Content.Shared.Input;
using Robust.Client.AutoGenerated;
@ -244,20 +243,6 @@ namespace Content.Client.Options.UI.Tabs
AddButton(ContentKeyFunctions.NanoChatNavigateDownUnread);
// DeltaV - End NanoChat keybinds
// Shitmed Change Start - TODO: Add hands, feet and groin targeting.
AddHeader("ui-options-header-targeting");
AddButton(ContentKeyFunctions.TargetHead);
AddButton(ContentKeyFunctions.TargetTorso);
AddButton(ContentKeyFunctions.TargetLeftArm);
AddButton(ContentKeyFunctions.TargetLeftHand);
AddButton(ContentKeyFunctions.TargetRightArm);
AddButton(ContentKeyFunctions.TargetRightHand);
AddButton(ContentKeyFunctions.TargetLeftLeg);
AddButton(ContentKeyFunctions.TargetLeftFoot);
AddButton(ContentKeyFunctions.TargetRightLeg);
AddButton(ContentKeyFunctions.TargetRightFoot);
// Shitmed Change End
AddHeader("ui-options-header-misc");
AddButton(ContentKeyFunctions.TakeScreenshot);
AddButton(ContentKeyFunctions.TakeScreenshotNoUI);

View File

@ -1,4 +1,5 @@
using Content.Client.CartridgeLoader;
using Content.Shared._DV.Pager; // DeltaV - pagers
using Content.Shared.CartridgeLoader;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.PDA;
@ -72,6 +73,13 @@ namespace Content.Client.PDA
SendMessage(new PdaLockUplinkMessage());
};
// Begin DeltaV - pagers
_menu.OnUnlinkDevicePressed += address =>
{
SendMessage(new PagerRemoveAddressMessage(address));
};
// End DeltaV - pagers
_menu.OnProgramItemPressed += ActivateCartridge;
_menu.OnInstallButtonPressed += InstallCartridge;
_menu.OnUninstallButtonPressed += UninstallCartridge;
@ -100,6 +108,7 @@ namespace Content.Client.PDA
}
_menu.UpdateState(updateState);
UpdateLinkedDevices(); // DeltaV - pagers
}
protected override void AttachCartridgeUI(Control cartridgeUIFragment, string? title)
@ -127,5 +136,13 @@ namespace Content.Client.PDA
{
return EntMan.GetComponentOrNull<PdaBorderColorComponent>(Owner);
}
// Begin DeltaV - pagers
public void UpdateLinkedDevices()
{
if (EntMan.TryGetComponent<PagerComponent>(Owner, out var receiver))
_menu?.UpdateLinkedDevices(receiver.Devices);
}
// End DeltaV - pagers
}
}

View File

@ -70,6 +70,12 @@
Access="Public"
Text="{Loc 'comp-pda-ui-ringtone-button'}"
Description="{Loc 'comp-pda-ui-ringtone-button-description'}"/>
<!-- Begin DeltaV Additions: Pagers -->
<pda:PdaSettingsButton Name="LinkedDevicesButton"
Access="Public"
Text="{Loc 'comp-pda-ui-linked-devices-button'}"
Description="{Loc 'comp-pda-ui-linked-devices-description'}"/>
<!-- End DeltaV Additions: Pagers -->
<pda:PdaSettingsButton Name="ActivateMusicButton"
Access="Public"
Visible="False"
@ -92,6 +98,13 @@
HorizontalExpand="True"
Name="ProgramView"
Access="Public">
<!-- Begin DeltaV Additions: Pagers -->
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True"
Name="LinkedDeviceList"
Visible="False"/>
<!-- End DeltaV Additions: Pagers -->
</BoxContainer>
</BoxContainer>
<BoxContainer Name="ContentFooter" HorizontalExpand="True" SetHeight="28" Margin="1 0 2 1">

View File

@ -131,6 +131,12 @@ namespace Content.Client.PDA
{
_clipboard.SetText(_currentDate);
};
LinkedDevicesButton.OnPressed += _ =>
{
LinkedDeviceList.Visible = true;
ToProgramView(Loc.GetString("comp-pda-ui-linked-devices-title"));
};
// End DeltaV additions
@ -300,6 +306,7 @@ namespace Content.Client.PDA
ProgramCloseButton.Visible = false;
ProgramListButton.Visible = true;
SettingsButton.Visible = true;
LinkedDeviceList.Visible = false; // DeltaV - pagers
}
/// <summary>

View File

@ -19,6 +19,7 @@ public sealed class SSDIndicatorSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly Shared.SSDIndicator.SSDIndicatorSystem _shared = default!; // DeltaV - SSD Recency, don't want to rename the upstream class
public override void Initialize()
{
@ -35,13 +36,26 @@ public sealed class SSDIndicatorSystem : EntitySystem
!HasComp<ActiveNPCComponent>(uid) &&
HasComp<MindExaminableComponent>(uid))
{
// Begin DeltaV Addition
// Begin DeltaV Additions
var ev = new ShowSSDIndicatorEvent();
RaiseLocalEvent(uid, ref ev);
if (ev.Hidden)
return;
// End DeltaV Addition
args.StatusIcons.Add(_prototype.Index(component.Icon));
// SSD Recency Indicator
var stage = _shared.GetStage(new Entity<SSDIndicatorComponent>(uid, component));
var icon = stage switch
{
SsdStage.VeryRecent => component.VeryRecentIcon,
SsdStage.Recent => component.RecentIcon,
SsdStage.Cryoable => component.Icon,
_ => throw new InvalidOperationException($"{ToPrettyString(uid)} has an invalid SSD stage {stage}."),
};
args.StatusIcons.Add(_prototype.Index(icon));
// End DeltaV Additions
// args.StatusIcons.Add(_prototype.Index(component.Icon)); // DeltaV - commented out. status icon now added above
}
}
}

View File

@ -1,4 +1,5 @@
using Content.Shared.Movement.Components;
using Content.Client.PDA; // DeltaV
using Content.Shared.Movement.Components;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Client.GameObjects;
@ -66,6 +67,15 @@ public sealed partial class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeS
}
}
// DeltaV - borg pdas
if (TryComp<PdaBorderColorComponent>(entity, out var pdaBorders))
{
pdaBorders.BorderColor = prototype.PdaBorderColor ?? pdaBorders.BorderColor;
pdaBorders.AccentHColor = prototype.PdaAccentHorizontalColor ?? pdaBorders.AccentHColor;
pdaBorders.AccentVColor = prototype.PdaAccentVerticalColor ?? pdaBorders.AccentVColor;
}
// DeltaV - borg pdas
base.UpdateEntityAppearance(entity, prototype);
}
}

View File

@ -1,10 +1,12 @@
using System.Numerics;
using Content.Shared._DV.CCVars; // DeltaV - Enable Stars via CVar.
using Content.Shared.CombatMode;
using Content.Shared.Interaction;
using Content.Shared.Stunnable;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using Robust.Shared.Configuration; // DeltaV - Enable Stars via CVar.
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Random;
@ -13,6 +15,7 @@ namespace Content.Client.Stunnable;
public sealed class StunSystem : SharedStunSystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!; // DeltaV - Enable Stars via CVar.
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SpriteSystem _spriteSystem = default!;
@ -78,6 +81,9 @@ public sealed class StunSystem : SharedStunSystem
var visible = Appearance.TryGetData<bool>(entity, StunVisuals.SeeingStars, out var stars) && stars;
if (visible && !_cfg.GetCVar(DCCVars.ShowStunVisuals)) // DeltaV - Enable Stars via CVar.
visible = false;
_spriteSystem.LayerSetVisible((entity, entity.Comp), index, visible);
_spriteSystem.LayerSetRsiState((entity, entity.Comp), index, state);
}

View File

@ -1759,60 +1759,6 @@ namespace Content.Client.Stylesheets
.Prop(Control.StylePropertyModulateSelf, JusticeColorHovered),
// End DeltaV
// Shitmed Change Start
Element<TextureButton>().Class("TargetDollButtonHead")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/head_hover.png")),
Element<TextureButton>().Class("TargetDollButtonChest")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/torso_hover.png")),
Element<TextureButton>().Class("TargetDollButtonGroin")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/groin_hover.png")),
Element<TextureButton>().Class("TargetDollButtonLeftArm")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/leftarm_hover.png")),
Element<TextureButton>().Class("TargetDollButtonLeftHand")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/lefthand_hover.png")),
Element<TextureButton>().Class("TargetDollButtonRightArm")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/rightarm_hover.png")),
Element<TextureButton>().Class("TargetDollButtonRightHand")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/righthand_hover.png")),
Element<TextureButton>().Class("TargetDollButtonLeftLeg")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/leftleg_hover.png")),
Element<TextureButton>().Class("TargetDollButtonLeftFoot")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/leftfoot_hover.png")),
Element<TextureButton>().Class("TargetDollButtonRightLeg")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/rightleg_hover.png")),
Element<TextureButton>().Class("TargetDollButtonRightFoot")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/rightfoot_hover.png")),
Element<TextureButton>().Class("TargetDollButtonEyes")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/eyes_hover.png")),
Element<TextureButton>().Class("TargetDollButtonMouth")
.Pseudo(TextureButton.StylePseudoClassHover)
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/_Shitmed/Interface/Targeting/Doll/mouth_hover.png")),
// Shitmed Change End
// Silicon law edit ui
Element<Label>().Class(SiliconLawContainer.StyleClassSiliconLawPositionLabel)
.Prop(Label.StylePropertyFontColor, NanoGold),

View File

@ -9,7 +9,6 @@
xmlns:hotbar="clr-namespace:Content.Client.UserInterface.Systems.Hotbar.Widgets"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Widgets"
xmlns:targeting="clr-namespace:Content.Client._Shitmed.UserInterface.Systems.Targeting.Widgets"
Name="DefaultHud"
VerticalExpand="False"
VerticalAlignment="Bottom"
@ -29,7 +28,6 @@
<widgets:GhostGui Name="Ghost" Access="Protected" />
<inventory:InventoryGui Name="Inventory" Access="Protected" />
<hotbar:HotbarGui Name="Hotbar" Access="Protected" />
<targeting:TargetingControl Name="Targeting" Access="Protected"/> <!-- Shitmed Change -->
<chat:ResizableChatBox Name="Chat" Access="Protected" />
<alerts:AlertsUI Name="Alerts" Access="Protected" />
</screens:DefaultGameScreen>

View File

@ -22,12 +22,11 @@ public sealed partial class DefaultGameScreen : InGameScreen
SetAnchorAndMarginPreset(Hotbar, LayoutPreset.BottomWide, margin: 5);
SetAnchorAndMarginPreset(Chat, LayoutPreset.TopRight, margin: 10);
SetAnchorAndMarginPreset(Alerts, LayoutPreset.TopRight, margin: 10);
SetAnchorAndMarginPreset(Targeting, LayoutPreset.BottomRight, margin: 5); // Shitmed Change
Chat.OnResized += ChatOnResized;
Chat.OnChatResizeFinish += ChatOnResizeFinish;
MainViewport.OnResized += ResizeActionContainer;
MainViewport.OnResized += ResizeAlertsContainer;
Inventory.OnResized += ResizeActionContainer;
}
@ -37,12 +36,6 @@ public sealed partial class DefaultGameScreen : InGameScreen
Actions.ActionsContainer.MaxGridHeight = MainViewport.Size.Y - indent;
}
private void ResizeAlertsContainer()
{
float indent = Chat.Size.Y + Targeting.Size.Y + 120;
Alerts.AlertContainer.MaxGridHeight = Math.Max(MainViewport.Size.Y - indent, 1);
}
private void ChatOnResizeFinish(Vector2 _)
{
var marginBottom = Chat.GetValue<float>(MarginBottomProperty);

View File

@ -10,7 +10,6 @@
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Widgets"
xmlns:targeting="clr-namespace:Content.Client._Shitmed.UserInterface.Systems.Targeting.Widgets"
Name="SeparatedChatHud"
VerticalExpand="False"
VerticalAlignment="Bottom"
@ -21,9 +20,8 @@
<widgets:GhostGui Name="Ghost" Access="Protected" />
<inventory:InventoryGui Name="Inventory" Access="Protected"/>
<hotbar:HotbarGui Name="Hotbar" Access="Protected"/>
<targeting:TargetingControl Name="Targeting" Access="Protected"/> <!-- Shitmed Change -->
<BoxContainer Name="TopLeftContainer" Orientation="Horizontal"> <!--goobstation-->
<actions:ActionsBar Name="Actions" Access="Protected" />
<BoxContainer Name="TopLeftContainer" Orientation="Vertical">
<actions:ActionsBar Name="Actions" Access="Protected" />
<BoxContainer Name="VoteMenu" Access="Public" Orientation="Vertical"/>
</BoxContainer>
<alerts:AlertsUI Name="Alerts" Access="Protected" />

View File

@ -27,8 +27,7 @@ public sealed partial class SeparatedChatGameScreen : InGameScreen
SetAnchorAndMarginPreset(Ghost, LayoutPreset.BottomWide, margin: 80);
SetAnchorAndMarginPreset(Hotbar, LayoutPreset.BottomWide, margin: 5);
SetAnchorAndMarginPreset(Alerts, LayoutPreset.CenterRight, margin: 10);
SetAnchorAndMarginPreset(Targeting, LayoutPreset.BottomRight, margin: 5);
ScreenContainer.OnSplitResizeFinished += () =>
OnChatResized?.Invoke(new Vector2(ScreenContainer.SplitFraction, 0));

View File

@ -1,13 +1,7 @@
<widgets:AlertsUI xmlns="https://spacestation14.io"
xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Alerts.Widgets"
xmlns:partStatus="clr-namespace:Content.Client._Shitmed.UserInterface.Systems.PartStatus.Widgets"
MinSize="64 64">
<!-- Shitmed Change Start - TODO: Refactor this godforsaken abomination -->
<GridContainer Columns="1" HorizontalAlignment="Right" VerticalAlignment="Top">
<PanelContainer >
<GridContainer Name="AlertContainer" Columns="1" HorizontalAlignment="Right" VerticalAlignment="Center" Access="Public" />
</PanelContainer>
<partStatus:PartStatusControl Name="PartStatus" Access="Protected"/>
</GridContainer>
<!-- Shitmed Change End -->
<PanelContainer HorizontalAlignment="Right" VerticalAlignment="Top">
<BoxContainer Name="AlertContainer" Access="Public" Orientation="Vertical" />
</PanelContainer>
</widgets:AlertsUI>

View File

@ -280,7 +280,10 @@ public sealed partial class GunSystem : SharedGunSystem
if (!Timing.IsFirstTimePredicted || user == null || recoil == Vector2.Zero || recoilScalar == 0)
return;
_recoil.KickCamera(user.Value, recoil.Normalized() * 0.5f * recoilScalar);
// ES START
// reduce recoil even further
_recoil.KickCamera(user.Value, recoil.Normalized() * 0.13f * recoilScalar);
// ES END
}
protected override void Popup(string message, EntityUid? uid, EntityUid? user)

View File

@ -23,16 +23,20 @@ public sealed class AACBoundUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
_window?.Close();
if (_window is { Disposed: false })
_window.Close();
_window = this.CreateWindow<AACWindow>();
_window.OnClose += Close;
_window.PhraseButtonPressed += OnPhraseButtonPressed;
_window.Typing += OnTyping;
_window.SubmitPressed += OnSubmit;
}
private void OnPhraseButtonPressed(List<ProtoId<QuickPhrasePrototype>> phraseId)
private void OnPhraseButtonPressed(List<ProtoId<QuickPhrasePrototype>> phraseId, string prefix)
{
SendMessage(new AACTabletSendPhraseMessage(phraseId));
SendMessage(new AACTabletSendPhraseMessage(phraseId, prefix));
}
private void OnTyping()
@ -46,4 +50,14 @@ public sealed class AACBoundUserInterface : BoundUserInterface
_typing ??= EntMan.System<TypingIndicatorSystem>();
_typing?.ClientSubmittedChatText();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not AACTabletBuiState msg)
return;
_window?.Update(msg);
}
}

View File

@ -18,7 +18,14 @@
StyleClasses="AacCheckbox"
Margin="0 0 8 0"/>
<Control HorizontalExpand="True"/>
</BoxContainer>
<Control SetWidth="170" Margin="8 0 0 0"/>
<OptionButton Name="RadioChannels"
StyleClasses="OpenBoth"
SetWidth="120"
Margin="8 0 0 0"/>
</BoxContainer>
<!-- Buffer Display and Controls -->
<BoxContainer Orientation="Horizontal">
@ -29,7 +36,6 @@
StyleClasses="AacBufferText"
Margin="8 4"/>
</PanelContainer>
<Button Name="ClearButton"
Text="{Loc 'aac-tablet-backspace'}"
TextAlign="Center"

View File

@ -2,6 +2,9 @@ using System.Linq;
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared._DV.QuickPhrase;
using Content.Shared._DV.AACTablet;
using Content.Shared.Chat;
using Content.Shared.Radio;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@ -16,7 +19,7 @@ public sealed partial class AACWindow : FancyWindow
[Dependency] private readonly IPrototypeManager _prototype = default!;
private readonly List<QuickPhrasePrototype> _phrases;
private readonly Dictionary<string, List<QuickPhrasePrototype>> _filteredPhrases = new();
public event Action<List<ProtoId<QuickPhrasePrototype>>>? PhraseButtonPressed;
public event Action<List<ProtoId<QuickPhrasePrototype>>, string>? PhraseButtonPressed;
public event Action? Typing;
public event Action? SubmitPressed;
@ -42,10 +45,16 @@ public sealed partial class AACWindow : FancyWindow
SearchBar.OnTextChanged += FilterSearch;
SendButton.OnPressed += SendBuffer;
ClearButton.OnPressed += BackspaceBuffer;
RadioChannels.OnItemSelected += OnChannelSelected;
PopulateGui();
FilterSearch(null);
}
private void OnChannelSelected(OptionButton.ItemSelectedEventArgs args)
{
RadioChannels.SelectId(args.Id);
}
private void BackspaceBuffer(BaseButton.ButtonEventArgs obj)
{
if (_phraseBuffer.Count == 0)
@ -68,12 +77,14 @@ public sealed partial class AACWindow : FancyWindow
private void SendBuffer(BaseButton.ButtonEventArgs obj)
{
PhraseButtonPressed?.Invoke(_phraseBuffer);
PhraseButtonPressed?.Invoke(_phraseBuffer, Prefix);
_phraseBuffer.Clear();
BufferedString.Text = string.Empty;
SubmitPressed?.Invoke();
}
private string Prefix => (string?)RadioChannels.SelectedMetadata ?? SharedChatSystem.LocalPrefix.ToString();
private void FilterSearch(LineEdit.LineEditEventArgs? obj)
{
SearchResults.RemoveAllChildren();
@ -82,7 +93,8 @@ public sealed partial class AACWindow : FancyWindow
var emptySearch = string.IsNullOrEmpty(SearchBar.Text);
foreach (var phrase in _phrases)
{
if (!emptySearch && !Loc.GetString(phrase.Text).Contains(SearchBar.Text, StringComparison.CurrentCultureIgnoreCase))
if (!emptySearch && !Loc.GetString(phrase.Text)
.Contains(SearchBar.Text, StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
@ -236,8 +248,24 @@ public sealed partial class AACWindow : FancyWindow
{
_phraseSingle.Clear();
_phraseSingle.Add(phraseId);
PhraseButtonPressed?.Invoke(_phraseSingle);
PhraseButtonPressed?.Invoke(_phraseSingle, Prefix);
SubmitPressed?.Invoke();
}
}
internal void Update(AACTabletBuiState msg)
{
RadioChannels.Clear();
var id = 0;
RadioChannels.AddItem("Local", id);
RadioChannels.SetItemMetadata(RadioChannels.GetIdx(id), SharedChatSystem.LocalPrefix.ToString());
foreach (var channel in msg.RadioChannels)
{
var channelProto = _prototype.Index<RadioChannelPrototype>(channel);
RadioChannels.AddItem(channelProto.LocalizedName, ++id);
RadioChannels.SetItemMetadata(RadioChannels.GetIdx(id), string.Concat(SharedChatSystem.RadioChannelPrefix, channelProto.KeyCode));
}
}
}

View File

@ -0,0 +1,8 @@
using Content.Shared._DV.Access.Systems;
namespace Content.Client._DV.Access.Systems;
/// <summary>
/// Client side handling of the subdermal ID system.
/// </summary>
public sealed class SubdermalIdCardSystem : SharedSubdermalIdCardSystem;

View File

@ -1,10 +0,0 @@
<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

@ -1,72 +0,0 @@
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 RadialMenuButtonWithSector()
{
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 RadialMenuButtonWithSector()
{
SetSize = new Vector2(64f, 64f),
};
Main.AddChild(nilButton);
nilButton.OnButtonUp += _ =>
{
SendAugmentToolPanelSystemMessageAction?.Invoke(null);
Close();
};
}
}

View File

@ -1,39 +0,0 @@
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

@ -0,0 +1,41 @@
using Content.Shared._DV.Body.Components;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Robust.Client.GameObjects;
namespace Content.Client._DV.Body;
/// <summary>
/// Colors feathers.
/// </summary>
public sealed class FeatherVisualizer : VisualizerSystem<FeatherComponent>
{
[Dependency] private readonly ClothingSystem _clothing = default!;
protected override void OnAppearanceChange(EntityUid uid, FeatherComponent component, ref AppearanceChangeEvent args)
{
if (!AppearanceSystem.TryGetData<Color>(uid, FeatherVisuals.FeatherColor, out var featherColor, args.Component))
return;
SpriteSystem.LayerSetColor(uid, FeatherVisualLayers.Feather, featherColor);
if (TryComp<ClothingComponent>(uid, out var clothing))
{
foreach (var slotPair in clothing.ClothingVisuals)
{
_clothing.SetLayerColor(clothing, slotPair.Key, "feather", featherColor);
}
}
if (!AppearanceSystem.TryGetData<Color>(uid, FeatherVisuals.BloodColor, out var bloodColor, args.Component))
return;
SpriteSystem.LayerSetColor(uid, FeatherVisualLayers.Blood, bloodColor);
}
}
public enum FeatherVisualLayers : byte
{
Feather,
Blood,
}

View File

@ -9,6 +9,7 @@
StyleClasses="LabelKeyText"/>
<CheckBox Name="DisableFiltersCheckBox" Text="{Loc 'ui-options-no-filters'}" />
<CheckBox Name="DisableGlimmerEffectCheckBox" Text="{Loc 'ui-options-disable-glimmer-effect'}" />
<CheckBox Name="EnableStunVisualsCheckBox" Text="{Loc 'ui-options-enable-stun-visuals'}" />
<Label Text="{Loc 'ui-options-delta-tips'}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="DisableTipsCheckBox" Text="{Loc 'ui-options-delta-disable-tips'}" />

View File

@ -18,6 +18,7 @@ public sealed partial class DeltaTab : Control
Control.AddOptionCheckBox(DCCVars.NoVisionFilters, DisableFiltersCheckBox);
Control.AddOptionCheckBox(DCCVars.DisableGlimmerShader, DisableGlimmerEffectCheckBox);
Control.AddOptionCheckBox(DCCVars.ShowStunVisuals, EnableStunVisualsCheckBox);
Control.AddOptionCheckBox(DCCVars.DisableTips, DisableTipsCheckBox);
ResetTipsButton.OnPressed += _ => OnResetTipsPressed();

View File

@ -0,0 +1,25 @@
using Content.Client.PDA;
using Content.Shared._DV.Pager;
using Content.Shared.PDA;
namespace Content.Client._DV.PDA;
public sealed class ClientEventNotificationReceiverSystem : EntitySystem
{
[Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PagerComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
}
private void OnAfterAutoHandleState(Entity<PagerComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (!_userInterface.TryGetOpenUi<PdaBoundUserInterface>(ent.Owner, PdaUiKey.Key, out var bui))
return;
bui.UpdateLinkedDevices();
}
}

View File

@ -0,0 +1,6 @@
<PanelContainer HorizontalExpand="True" xmlns="https://spacestation14.io">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="4">
<Label Name="DeviceName" Access="Public" HorizontalExpand="True" />
<Button Name="Unlink" Access="Public" StyleClasses="ButtonSmall" Text="{Loc 'comp-pda-ui-linked-devices-unlink'}"/>
</BoxContainer>
</PanelContainer>

View File

@ -0,0 +1,14 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._DV.PDA;
[GenerateTypedNameReferences]
public sealed partial class LinkedDeviceItem : PanelContainer
{
public LinkedDeviceItem()
{
RobustXamlLoader.Load(this);
}
}

View File

@ -0,0 +1,28 @@
using Content.Client._DV.PDA;
using Content.Client.Stylesheets;
using System.Linq;
namespace Content.Client.PDA;
public sealed partial class PdaMenu
{
public event Action<string>? OnUnlinkDevicePressed;
public void UpdateLinkedDevices(Dictionary<string, string> devices)
{
LinkedDeviceList.RemoveAllChildren();
var even = false;
foreach (var (address, name) in devices.OrderBy(kvp => kvp.Value))
{
var item = new LinkedDeviceItem()
{
DeviceName = { Text = name },
};
item.Unlink.OnPressed += _ => OnUnlinkDevicePressed?.Invoke(address);
LinkedDeviceList.AddChild(item);
LinkedDeviceList.StyleClasses.Add(even ? StyleClass.PanelDark : StyleClass.PanelLight);
even = !even;
}
}
}

View File

@ -0,0 +1,41 @@
using Content.Shared._DV.Pager;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
namespace Content.Client._DV.Pager;
public sealed class PagerButtonSystem : SharedPagerButtonSystem
{
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
private const string AnimateKey = "pager-press";
protected override void AfterActivated(Entity<PagerButtonComponent> ent)
{
if (!_animationPlayer.HasRunningAnimation(ent.Owner, AnimateKey))
_animationPlayer.Play(ent.Owner, GetAnimation(ent), AnimateKey);
}
private Animation GetAnimation(Entity<PagerButtonComponent> ent)
{
const float flickDuration = 20f / 60f;
return new Animation()
{
Length = TimeSpan.FromSeconds(flickDuration),
AnimationTracks =
{
new AnimationTrackSpriteFlick()
{
LayerKey = PagerButtonVisualLayers.Base,
KeyFrames =
{
new AnimationTrackSpriteFlick.KeyFrame(ent.Comp.PressState, 0f),
new AnimationTrackSpriteFlick.KeyFrame(ent.Comp.UnpressedState, flickDuration),
},
},
},
};
}
}

View File

@ -1,28 +0,0 @@
using Content.Shared._DV.Silicon.IPC;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
namespace Content.Client._DV.Silicon.IPC;
public sealed class SnoutHelmetSystem : EntitySystem
{
private const MarkingCategories MarkingToQuery = MarkingCategories.Snout;
private const int MaximumMarkingCount = 0;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SnoutHelmetComponent, ComponentStartup>(OnComponentStartup);
}
private void OnComponentStartup(EntityUid uid, SnoutHelmetComponent component, ComponentStartup args)
{
if (TryComp(uid, out HumanoidAppearanceComponent? humanoidAppearanceComponent) &&
humanoidAppearanceComponent.ClientOldMarkings.Markings.TryGetValue(MarkingToQuery, out var markings) &&
markings.Count > MaximumMarkingCount)
{
component.EnableAlternateHelmet = true;
}
}
}

View File

@ -1,31 +0,0 @@
using Content.Client.Alerts;
using Content.Shared._EE.Silicon.Components;
using Content.Shared._EE.Silicon.Systems;
namespace Content.Client._DV.Silicon.Charge;
public sealed class SiliconChargeSystem : SharedSiliconChargeSystem
{
[Dependency] private readonly ClientAlertsSystem _alerts = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SiliconComponent, ComponentInit>(OnSiliconInit);
SubscribeLocalEvent<SiliconComponent, SiliconChargeStateUpdateEvent>(OnSiliconChargeStateUpdate);
}
private void OnSiliconInit(EntityUid uid, SiliconComponent component, ComponentInit args)
{
if (!component.BatteryPowered)
return;
_alerts.ShowAlert(uid, component.BatteryAlert, component.ChargeState);
}
private void OnSiliconChargeStateUpdate(EntityUid uid, SiliconComponent component, SiliconChargeStateUpdateEvent ev)
{
_alerts.ShowAlert(uid, component.BatteryAlert, ev.ChargePercent);
}
}

View File

@ -1,29 +0,0 @@
using Content.Client.Alerts;
using Content.Shared._EE.Silicon.Components;
using Content.Shared._DV.Silicons.Charge;
namespace Content.Client._DV.Silicon.Charge;
public sealed class SiliconDrainSystem : SharedSiliconDrainSystem
{
[Dependency] private readonly ClientAlertsSystem _alerts = default!;
protected override void UpdateChargeIcon(Entity<SiliconComponent> ent, short chargePercent)
{
// If you can't find a battery, set the indicator and skip it.
if (!TryGetSiliconBattery(ent, out _))
{
if (_alerts.IsShowingAlert(ent.Owner, ent.Comp.BatteryAlert))
{
_alerts.ClearAlert(ent.Owner, ent.Comp.BatteryAlert);
_alerts.ShowAlert(ent.Owner, ent.Comp.NoBatteryAlert);
}
}
// If the battery was replaced and the no battery indicator is showing, replace the indicator
else if (_alerts.IsShowingAlert(ent.Owner, ent.Comp.NoBatteryAlert))
{
_alerts.ClearAlert(ent.Owner, ent.Comp.NoBatteryAlert);
_alerts.ShowAlert(ent.Owner, ent.Comp.BatteryAlert, chargePercent);
}
}
}

View File

@ -1,22 +0,0 @@
using Content.Client.Items;
using Content.Shared._DV.Surgery;
using Content.Shared.Inventory;
using Robust.Shared.Containers;
namespace Content.Client._DV.Surgery;
/// <summary>
/// This shows the item status for dirty surgery tools.
/// </summary>
public sealed class SurgeryCleanStatusSystem : EntitySystem
{
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
public override void Initialize()
{
base.Initialize();
Subs.ItemStatus<SurgeryDirtinessComponent>(ent => new SurgeryDirtinessItemStatus(ent, EntityManager, _inventory, _container));
}
}

View File

@ -1,99 +0,0 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared._DV.Surgery;
using Content.Shared._Shitmed.Medical.Surgery.Tools;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Robust.Shared.Containers;
using Robust.Shared.Timing;
namespace Content.Client._DV.Surgery;
public sealed class SurgeryDirtinessItemStatus : SplitBar
{
private readonly IEntityManager _entMan;
private readonly EntityUid _uid;
private readonly InventorySystem _inventory;
private readonly SharedContainerSystem _container;
private FixedPoint2? _dirtiness = null;
private FixedPoint2? _gloveDirtiness = null;
private static readonly Color SelfCleanColor = new Color(0xD1, 0xD5, 0xD9);
private static readonly Color SelfDirtyColor = new Color(0xE9, 0x3D, 0x58);
private static readonly Color GloveCleanColor = new Color(0xAB, 0xE9, 0xFB);
private static readonly Color GloveDirtyColor = new Color(0xE9, 0x64, 0x3A);
public SurgeryDirtinessItemStatus(EntityUid uid, IEntityManager entMan, InventorySystem inventory, SharedContainerSystem container)
{
_uid = uid;
_entMan = entMan;
_inventory = inventory;
_container = container;
MinBarSize = new Vector2(10, 0);
Margin = new Thickness(4);
MinHeight = 16;
MaxHeight = 16;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (!_entMan.TryGetComponent<SurgeryDirtinessComponent>(_uid, out var comp))
return;
var isTool = _entMan.HasComponent<SurgeryToolComponent>(_uid);
if (_dirtiness == comp.Dirtiness && !isTool)
return;
_dirtiness = comp.Dirtiness;
if (isTool && _container.TryGetContainingContainer((_uid, null, null), out var container))
{
var user = container.Owner;
FixedPoint2? glovesDirtiness =
_inventory.TryGetSlotEntity(user, "gloves", out var gloves)
? _entMan.TryGetComponent<SurgeryDirtinessComponent>(_uid, out var glovesComp)
? glovesComp.Dirtiness
: FixedPoint2.Zero
: null;
if (glovesDirtiness is not null && _gloveDirtiness == glovesDirtiness)
return;
_gloveDirtiness = glovesDirtiness;
Clear();
var toolAmount = FixedPoint2.Min(_dirtiness.Value / 100, 1);
var gloveAmount = FixedPoint2.Min((_gloveDirtiness ?? 0) / 100, 1);
var remaining = 1 - toolAmount - gloveAmount;
var clean = (_dirtiness + (_gloveDirtiness ?? 0)) < 50;
var maskOn = _inventory.TryGetSlotEntity(user, "mask", out var _);
var isFine = clean && maskOn && _gloveDirtiness is not null;
if (toolAmount != FixedPoint2.Zero)
AddEntry(toolAmount.Float() * 100, isFine ? SelfCleanColor : SelfDirtyColor);
if (gloveAmount != FixedPoint2.Zero)
AddEntry(gloveAmount.Float() * 100, isFine ? GloveCleanColor : GloveDirtyColor);
if (remaining > FixedPoint2.Zero)
AddEntry(remaining.Float() * 100, Color.SlateGray);
}
else
{
Clear();
var amount = FixedPoint2.Min(_dirtiness.Value / 100, 1);
var remaining = 1 - amount;
if (amount != FixedPoint2.Zero)
AddEntry(amount.Float(), amount < 0.5 ? SelfCleanColor : SelfDirtyColor);
if (remaining != FixedPoint2.Zero)
AddEntry(remaining.Float(), Color.SlateGray);
}
}
}

View File

@ -0,0 +1,99 @@
using System.Numerics;
using Content.Shared._DV.CCVars;
using Content.Shared._ES.Camera;
using Content.Shared.Camera;
using Robust.Shared.Configuration;
using Robust.Shared.Noise;
using Robust.Shared.Timing;
namespace Content.Client._ES;
// DeltaV - Anything not marked DeltaV is Ephemeral Space code moved from SharedESScreenshakeSystem
public sealed class ESScreenshakeSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _config = default!; // DeltaV
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedESScreenshakeSystem _shared = default!;
private bool _disabled; // DeltaV
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ESScreenshakeComponent, GetEyeOffsetEvent>(OnGetEyeOffset);
SubscribeLocalEvent<ESScreenshakeComponent, ESGetEyeRotationEvent>(OnGetEyeRotation);
_config.OnValueChanged(DCCVars.EsScreenshakeDisabled, OnDisabledChanged, true); // DeltaV
}
private void OnDisabledChanged(bool obj)
{
_disabled = obj;
}
private void OnGetEyeOffset(Entity<ESScreenshakeComponent> ent, ref GetEyeOffsetEvent args)
{
if (!TryComp<EyeComponent>(ent, out var eye) || _disabled) // DeltaV - check if disabled
return;
var noise = new FastNoiseLite(67);
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
var accumulatedOffset = Vector2.Zero;
var maxOffset = new Vector2(0.15f, 0.15f);
foreach (var command in ent.Comp.Commands)
{
if (command.Translational == null)
continue;
var trauma =
_shared.CalculateTraumaValueForCurrentTime(command.Translational, command.Start);
if (trauma <= 0)
continue;
noise.SetFrequency(command.Translational.Frequency);
// using the starst c ommand for y pos kinda doesnt work in the case where multiple shakes get sent at the same time
// and the shakes are identical otherwise. but like dont do that or something idk
var offsetX = (maxOffset.X * trauma) * noise.GetNoise((float)_timing.RealTime.TotalMilliseconds,
(float)command.Start.TotalMilliseconds);
noise.SetSeed(68);
var offsetY = (maxOffset.Y * trauma) * noise.GetNoise((float)_timing.RealTime.TotalMilliseconds,
(float)command.Start.TotalMilliseconds);
noise.SetSeed(67);
accumulatedOffset += new Vector2(offsetX, offsetY);
}
args.Offset += accumulatedOffset;
}
private void OnGetEyeRotation(Entity<ESScreenshakeComponent> ent, ref ESGetEyeRotationEvent args)
{
if (!TryComp<EyeComponent>(ent, out var eye) || _disabled) // DeltaV - check if disabled
return;
var noise = new FastNoiseLite(67 + 420); // Epic bacon
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
// 20deg max
var accumulatedAngle = Angle.Zero;
var maxAngleDegrees = 20f;
foreach (var command in ent.Comp.Commands)
{
if (command.Rotational == null)
continue;
var trauma =
_shared.CalculateTraumaValueForCurrentTime(command.Rotational, command.Start);
if (trauma <= 0)
continue;
noise.SetFrequency(command.Rotational.Frequency);
var angle = (maxAngleDegrees * trauma) * noise.GetNoise((float)_timing.RealTime.TotalMilliseconds, (float)command.Start.TotalMilliseconds);
accumulatedAngle += Angle.FromDegrees(angle);
}
// TODO ughhh this shit breaks with something idk
args.Rotation += accumulatedAngle;
}
}

View File

@ -0,0 +1,14 @@
using Content.Shared._ES.Core.Timer.Components;
namespace Content.Client._ES.Lighting.Components;
/// <summary>
/// Handles a point light that fades out while synced to a <see cref="ESTimedDespawnComponent"/>
/// </summary>
[RegisterComponent]
[Access(typeof(ESTimedDespawnLightFadeSystem))]
public sealed partial class ESTimedDespawnLightFadeComponent : Component
{
[DataField]
public TimeSpan FadeTime = TimeSpan.FromSeconds(1);
}

View File

@ -0,0 +1,52 @@
using Content.Client._ES.Lighting.Components;
using Content.Shared._ES.Core.Timer.Components;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using Robust.Shared.Timing;
namespace Content.Client._ES.Lighting;
public sealed class ESTimedDespawnLightFadeSystem : VisualizerSystem<ESTimedDespawnLightFadeComponent>
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
private const string FadeTrack = "light-fade";
protected override void OnAppearanceChange(EntityUid uid, ESTimedDespawnLightFadeComponent component, ref AppearanceChangeEvent args)
{
base.OnAppearanceChange(uid, component, ref args);
if (_animationPlayer.HasRunningAnimation(uid, FadeTrack))
return;
if (!AppearanceSystem.TryGetData<TimeSpan>(uid, ESTimedDespawnVisuals.DespawnTime, out var time, args.Component) ||
!TryComp<PointLightComponent>(uid, out var light))
return;
var duration = time - _timing.CurTime;
var animation = new Animation
{
Length = duration,
AnimationTracks =
{
new AnimationTrackComponentProperty
{
Property = nameof(PointLightComponent.Energy),
ComponentType = typeof(PointLightComponent),
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(light.Energy, 0f),
new AnimationTrackProperty.KeyFrame(light.Energy, (float) (duration - component.FadeTime).TotalSeconds),
new AnimationTrackProperty.KeyFrame(0f, (float) component.FadeTime.TotalSeconds, Easings.OutSine),
}
}
}
};
_animationPlayer.Play(uid, animation, FadeTrack);
}
}

View File

@ -0,0 +1,14 @@
using Content.Shared._ES.Core.Timer.Components;
namespace Content.Client._ES.Sprite.Components;
/// <summary>
/// Handles a sprite that fades out while synced to a <see cref="ESTimedDespawnComponent"/>
/// </summary>
[RegisterComponent]
[Access(typeof(ESTimedDespawnSpriteFadeSystem))]
public sealed partial class ESTimedDespawnSpriteFadeComponent : Component
{
[DataField]
public TimeSpan FadeTime = TimeSpan.FromSeconds(1);
}

View File

@ -0,0 +1,57 @@
using Content.Client._ES.Sprite.Components;
using Content.Shared._ES.Core.Timer.Components;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using Robust.Shared.Timing;
namespace Content.Client._ES.Sprite;
/// <summary>
/// This handles <see cref="ESTimedDespawnSpriteFadeComponent"/>
/// </summary>
public sealed class ESTimedDespawnSpriteFadeSystem : VisualizerSystem<ESTimedDespawnSpriteFadeComponent>
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
private const string FadeTrack = "es-sprite-fade";
protected override void OnAppearanceChange(EntityUid uid, ESTimedDespawnSpriteFadeComponent component, ref AppearanceChangeEvent args)
{
base.OnAppearanceChange(uid, component, ref args);
if (args.Sprite is not { } sprite)
return;
if (_animationPlayer.HasRunningAnimation(uid, FadeTrack))
return;
if (!AppearanceSystem.TryGetData<TimeSpan>(uid, ESTimedDespawnVisuals.DespawnTime, out var time, args.Component))
return;
var duration = time - _timing.CurTime;
var animation = new Animation
{
Length = duration,
AnimationTracks =
{
new AnimationTrackComponentProperty
{
Property = nameof(SpriteComponent.Color),
ComponentType = typeof(SpriteComponent),
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(sprite.Color, 0f),
new AnimationTrackProperty.KeyFrame(sprite.Color, MathF.Max((float) (duration - component.FadeTime).TotalSeconds, 0f)),
new AnimationTrackProperty.KeyFrame(sprite.Color.WithAlpha(0f), (float) component.FadeTime.TotalSeconds, Easings.OutSine),
},
},
},
};
_animationPlayer.Play(uid, animation, FadeTrack);
}
}

View File

@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2026 Janet Blackquill <uhhadd@gmail.com>
//
// SPDX-License-Identifier: MIT
using System.Numerics;
using Content.Shared._ST.Interaction;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Client._ST.Interaction;
public sealed class StellarInteractionParticleSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
private const string AnimateKey = "particle-animation";
private static readonly EntProtoId InteractionParticleId = "StellarInteractionParticle";
public override void Initialize()
{
base.Initialize();
SubscribeAllEvent<StellarInteractionParticleEvent>(OnInteractionParticle);
}
private void OnInteractionParticle(StellarInteractionParticleEvent ev)
{
var performer = GetEntity(ev.Performer);
var used = GetEntity(ev.Used);
var target = GetEntity(ev.Target);
if (!Exists(performer) || !Exists(target))
return;
var performerXform = Transform(performer);
var targetXform = Transform(target);
if (performerXform.MapID == MapId.Nullspace || targetXform.MapID == MapId.Nullspace)
return;
if (performerXform.ParentUid != targetXform.ParentUid)
return;
var performerTargetDelta = targetXform.LocalPosition - performerXform.LocalPosition;
var particle = Spawn(InteractionParticleId, performerXform.Coordinates);
if (used is { } usedEntity && Exists(usedEntity) && TryComp<SpriteComponent>(usedEntity, out var usedSprite))
{
_sprite.CopySprite((usedEntity, usedSprite), particle);
// ES START
_sprite.SetDrawDepth(particle, (int) Shared.DrawDepth.DrawDepth.Effects);
// ES END
}
var spriteColor = Comp<SpriteComponent>(particle).Color;
_animation.Play(particle, GetAnimation(performerTargetDelta, spriteColor), AnimateKey);
}
private Animation GetAnimation(Vector2 endOffset, Color color)
{
var startRotation = _random.NextAngle(Angle.FromDegrees(-40), Angle.FromDegrees(40));
var endRotation = Angle.Zero;
var startScale = new Vector2(0.3f, 0.3f);
var endScale = new Vector2(1f, 1f);
var rotationLength = TimeSpan.FromMilliseconds(600);
var startOffset = new Vector2();
var offsetLength = TimeSpan.FromMilliseconds(250);
var startColor = color.WithAlpha(color.A * 0.7f);
var endColor = color.WithAlpha(0f);
var colorLength = rotationLength + offsetLength;
return new Animation()
{
Length = colorLength,
AnimationTracks =
{
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Rotation),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(startRotation, 0f),
new AnimationTrackProperty.KeyFrame(endRotation, (float)rotationLength.TotalSeconds, Easings.OutBack),
},
},
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Scale),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(startScale, 0f),
new AnimationTrackProperty.KeyFrame(endScale, (float)rotationLength.TotalSeconds, Easings.OutBack),
},
},
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Offset),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(startOffset, 0f),
new AnimationTrackProperty.KeyFrame(endOffset, (float)offsetLength.TotalSeconds, Easings.OutBack),
},
},
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Color),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(startColor, 0f),
new AnimationTrackProperty.KeyFrame(startColor, (float)rotationLength.TotalSeconds),
new AnimationTrackProperty.KeyFrame(endColor, (float)offsetLength.TotalSeconds, Easings.InOutCirc),
},
},
},
};
}
}

View File

@ -1,14 +0,0 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'autodoc-add-step'}">
<BoxContainer Orientation="Vertical">
<Button Name="SurgeryButton" Text="{Loc 'autodoc-add-step-surgery'}"/>
<Button Name="GrabItemButton" Text="{Loc 'autodoc-add-step-grab-item'}"/>
<Button Name="GrabOrganButton" Text="{Loc 'autodoc-add-step-grab-organ'}"/>
<Button Name="GrabPartButton" Text="{Loc 'autodoc-add-step-grab-part'}"/>
<Button Name="StoreItemButton" Text="{Loc 'autodoc-add-step-store-item'}"/>
<Button Name="SetLabelButton" Text="{Loc 'autodoc-add-step-set-label'}"/>
<Button Name="WaitButton" Text="{Loc 'autodoc-add-step-wait'}"/>
</BoxContainer>
</controls:FancyWindow>

View File

@ -1,138 +0,0 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Administration;
using Content.Shared._Shitmed.Autodoc;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._Shitmed.Autodoc;
[GenerateTypedNameReferences]
public sealed partial class AddStepWindow : FancyWindow
{
public event Action<IAutodocStep>? OnAddStep;
private PickSurgeryWindow? _surgery;
private DialogWindow? _grab;
private DialogWindow? _label;
private DialogWindow? _wait;
public AddStepWindow()
{
RobustXamlLoader.Load(this);
// close the window once any step is added
OnAddStep += _ => Close();
OnClose += () =>
{
_surgery?.Close();
_grab?.Close();
_label?.Close();
_wait?.Close();
};
// dedicated ui to pick the enum values and surgery type
SurgeryButton.OnPressed += _ =>
{
if (_surgery is {} window)
{
window.MoveToFront();
return;
}
_surgery = new PickSurgeryWindow();
_surgery.OnAddStep += step => OnAddStep?.Invoke(step);
_surgery.OnClose += () => _surgery = null;
_surgery.OpenCentered();
};
// just picking a string, use a dialog
GrabItemButton.OnPressed += _ =>
{
if (_grab is {} dialog)
{
dialog.MoveToFront();
return;
}
var field = "name";
var prompt = Loc.GetString("autodoc-add-step-grab-item-prompt");
var placeholder = Loc.GetString("autodoc-add-step-grab-item-placeholder");
var entry = new QuickDialogEntry(field, QuickDialogEntryType.ShortText, prompt, placeholder);
var entries = new List<QuickDialogEntry> { entry };
_grab = new DialogWindow(GrabItemButton.Text!, entries);
_grab.OnConfirmed += responses =>
{
var name = responses[field].Trim();
if (name.Length < 1 || name.Length > 100)
return;
OnAddStep?.Invoke(new GrabItemAutodocStep()
{
Name = name
});
};
_grab.OnClose += () => _grab = null;
};
// no arguments so these are trivial
GrabOrganButton.OnPressed += _ => OnAddStep?.Invoke(new GrabAnyOrganAutodocStep());
GrabPartButton.OnPressed += _ => OnAddStep?.Invoke(new GrabAnyBodyPartAutodocStep());
StoreItemButton.OnPressed += _ => OnAddStep?.Invoke(new StoreItemAutodocStep());
SetLabelButton.OnPressed += _ =>
{
if (_label is {} dialog)
{
dialog.MoveToFront();
return;
}
var field = "label";
var prompt = Loc.GetString("autodoc-add-step-set-label-prompt");
var entry = new QuickDialogEntry(field, QuickDialogEntryType.ShortText, prompt);
var entries = new List<QuickDialogEntry> { entry };
_label = new DialogWindow(SetLabelButton.Text!, entries);
_label.OnConfirmed += responses =>
{
var label = responses[field].Trim();
if (label.Length < 1 || label.Length > 20)
return;
OnAddStep?.Invoke(new SetLabelAutodocStep()
{
Label = label
});
};
_label.OnClose += () => _label = null;
};
WaitButton.OnPressed += _ =>
{
if (_wait is {} dialog)
{
dialog.MoveToFront();
return;
}
var field = "length";
var prompt = Loc.GetString("autodoc-add-step-wait-prompt");
var entry = new QuickDialogEntry(field, QuickDialogEntryType.Integer, prompt);
var entries = new List<QuickDialogEntry> { entry };
_wait = new DialogWindow(WaitButton.Text!, entries);
_wait.OnConfirmed += responses =>
{
var length = int.Parse(responses[field].Trim());
if (length < 1 || length > 30)
return;
OnAddStep?.Invoke(new WaitAutodocStep()
{
Length = length
});
};
_wait.OnClose += () => _wait = null;
};
}
}

View File

@ -1,42 +0,0 @@
using Content.Shared._Shitmed.Autodoc;
using Robust.Client.GameObjects;
using Robust.Client.Player;
namespace Content.Client._Shitmed.Autodoc;
public sealed class AutodocBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[ViewVariables]
private AutodocWindow? _window;
public AutodocBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_window = new AutodocWindow(owner, _entMan, _player);
_window.OnCreateProgram += title => SendMessage(new AutodocCreateProgramMessage(title));
_window.OnToggleProgramSafety += program => SendMessage(new AutodocToggleProgramSafetyMessage(program));
_window.OnRemoveProgram += program => SendMessage(new AutodocRemoveProgramMessage(program));
_window.OnAddStep += (program, step, index) => SendMessage(new AutodocAddStepMessage(program, step, index));
_window.OnRemoveStep += (program, stepIndex) => SendMessage(new AutodocRemoveStepMessage(program, stepIndex));
_window.OnImportProgram += (program) => SendMessage(new AutodocImportProgramMessage(program));
_window.OnStart += program => SendMessage(new AutodocStartMessage(program));
_window.OnStop += () => SendMessage(new AutodocStopMessage());
_window.OnClose += () => Close();
_window.OpenCentered();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_window?.Dispose();
}
}

View File

@ -1,20 +0,0 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'autodoc-view-program-title'}"
SetSize="600 500">
<BoxContainer Orientation="Horizontal">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5">
<Label Name="ProgramTitle"/> <!-- Set to Program.Title -->
<Button Name="SafetyButton" Text="{Loc 'autodoc-safety-enabled'}"/>
<Button Name="RemoveButton" StyleClasses="Caution" Text="{Loc 'autodoc-remove-program'}"/>
<Button Name="AddStepButton" Text="{Loc 'autodoc-add-step'}"/>
<Button Name="RemoveStepButton" StyleClasses="Caution" Text="{Loc 'autodoc-remove-step'}" Disabled="True"/>
<Button Name="StartButton" StyleClasses="Caution" Text="{Loc 'autodoc-start-program'}"/>
<Button Name="ExportProgramButton" Text="{Loc 'autodoc-export-program'}"/>
</BoxContainer>
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
<ItemList Name="Steps"/> <!-- Set to Program.Steps -->
</ScrollContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@ -1,202 +0,0 @@
using Content.Client.UserInterface.Controls;
using Content.Shared._Shitmed.Autodoc;
using Content.Shared._Shitmed.Autodoc.Components;
using Content.Shared._Shitmed.Autodoc.Systems;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Timing;
using System.IO;
namespace Content.Client._Shitmed.Autodoc;
[GenerateTypedNameReferences]
public sealed partial class AutodocProgramWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IFileDialogManager _dialogManager = default!;
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly ISerializationManager _serMan = default!;
private SharedAutodocSystem _autodoc = default!;
public event Action? OnToggleSafety;
public event Action? OnRemoveProgram;
public event Action<IAutodocStep, int>? OnAddStep;
public event Action<int>? OnRemoveStep;
public event Action? OnStart;
private EntityUid _owner;
private AutodocProgram _program;
private int _steps;
private bool _safety = true;
private ISawmill _sawmill;
private int? _selected;
private AddStepWindow? _addStep;
public AutodocProgramWindow(EntityUid owner, AutodocProgram program)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_autodoc = _entMan.System<SharedAutodocSystem>();
_owner = owner;
_program = program;
_sawmill = _logMan.GetSawmill("autodoc-ui");
OnClose += () => _addStep?.Close();
SafetyButton.OnPressed += _ =>
{
OnToggleSafety?.Invoke();
program.SkipFailed ^= true;
UpdateSafety();
};
UpdateSafety();
RemoveButton.OnPressed += _ =>
{
OnRemoveProgram?.Invoke();
Close();
};
AddStepButton.OnPressed += _ =>
{
if (_addStep is {} window)
{
window.MoveToFront();
return;
}
_addStep = new AddStepWindow();
_addStep.OnAddStep += step =>
{
// if nothing is selected add it to the end
// if something is selected, insert just before it
var index = _selected ?? program.Steps.Count;
OnAddStep?.Invoke(step, index);
_selected = null;
RemoveButton.Disabled = true;
program.Steps.Insert(index, step);
UpdateSteps();
};
_addStep.OnClose += () => _addStep = null;
_addStep.OpenCentered();
};
RemoveStepButton.OnPressed += _ =>
{
if (_selected is not {} index)
return;
_selected = null;
RemoveStepButton.Disabled = true;
OnRemoveStep?.Invoke(index);
// Steps.RemoveChild throws for no fucking reason so rebuild it
program.Steps.RemoveAt(index);
UpdateSteps();
};
StartButton.OnPressed += _ =>
{
OnStart?.Invoke();
Close();
};
ExportProgramButton.OnPressed += _ =>
{
ExportProgram();
};
Steps.OnItemSelected += args =>
{
_selected = args.ItemIndex;
RemoveStepButton.Disabled = false;
};
Steps.OnItemDeselected += _ =>
{
_selected = null;
RemoveStepButton.Disabled = true;
};
UpdateSteps();
UpdateSafety();
}
private async void ExportProgram()
{
if (await _dialogManager.SaveFile(new FileDialogFilters(new FileDialogFilters.Group("yml"))) is not {} file)
return;
try
{
var node = _serMan.WriteValue(_program.GetType(), _program);
await using var writer = new StreamWriter(file.fileStream);
node.Write(writer);
}
catch (Exception e)
{
_sawmill.Error($"Error when exporting program: {e}");
}
finally
{
await file.fileStream.DisposeAsync();
}
}
private void UpdateSafety()
{
var safety = !_program.SkipFailed;
if (safety == _safety)
return;
_safety = safety;
SafetyButton.Text = Loc.GetString("autodoc-safety-" + (safety ? "enabled" : "disabled"));
if (safety)
SafetyButton.RemoveStyleClass("Caution");
else
SafetyButton.AddStyleClass("Caution");
}
private void UpdateSteps()
{
var count = _program.Steps.Count;
if (_steps == count)
return;
_steps = count;
Steps.Clear();
for (int i = 0; i < count; i++)
{
Steps.AddItem(_program.Steps[i].Title);
}
if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
return;
AddStepButton.Disabled = count >= comp.MaxProgramSteps;
}
private void UpdateStart()
{
if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
return;
StartButton.Disabled = _autodoc.GetPatient((_owner, comp)) == null;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdateSteps();
UpdateSafety();
UpdateStart();
}
}

View File

@ -1,15 +0,0 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'autodoc-title'}">
<BoxContainer Orientation="Vertical">
<ScrollContainer MinWidth="600" MinHeight="400" VerticalExpand="True">
<BoxContainer Name="Programs" Orientation="Vertical"/> <!-- Populated at runtime -->
</ScrollContainer>
<BoxContainer Orientation="Horizontal" Margin="5 5 5 5" Align="Center">
<Button Name="CreateProgramButton" StyleClasses="OpenRight" Text="{Loc 'autodoc-create-program'}"/>
<Button Name="ImportProgramButton" StyleClasses="OpenLeft" Text="{Loc 'autodoc-import-program'}"/>
</BoxContainer>
<Button Name="AbortButton" MaxWidth="350" Text="{Loc 'autodoc-abort-program'}" Disabled="True"/>
</BoxContainer>
</controls:FancyWindow>

View File

@ -1,222 +0,0 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Administration;
using Content.Shared._Shitmed.Autodoc;
using Content.Shared._Shitmed.Autodoc.Components;
using Content.Shared._Shitmed.Autodoc.Systems;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
using Robust.Client.UserInterface;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Utility;
using System.IO;
using YamlDotNet.RepresentationModel;
using Robust.Shared.Serialization.Markdown;
namespace Content.Client._Shitmed.Autodoc;
[GenerateTypedNameReferences]
public sealed partial class AutodocWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IFileDialogManager _dialogMan = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly ISerializationManager _serMan = default!;
[Dependency] private readonly ILogManager _logMan = default!;
private SharedAutodocSystem _autodoc;
private EntityUid _owner;
private bool _active;
private int _programCount = 0;
private ISawmill _sawmill;
public event Action<string>? OnCreateProgram;
public event Action<int>? OnToggleProgramSafety;
public event Action<int>? OnRemoveProgram;
public event Action<int, IAutodocStep, int>? OnAddStep;
public event Action<int, int>? OnRemoveStep;
public event Action<int>? OnStart;
public event Action? OnStop;
public event Action<AutodocProgram>? OnImportProgram;
private DialogWindow? _dialog;
private AutodocProgramWindow? _currentProgram;
public AutodocWindow(EntityUid owner, IEntityManager entMan, IPlayerManager player)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sawmill = _logMan.GetSawmill("autodoc-ui");
_autodoc = entMan.System<SharedAutodocSystem>();
_owner = owner;
OnClose += () =>
{
_dialog?.Close();
_currentProgram?.Close();
};
ImportProgramButton.OnPressed += _ =>
{
ImportProgram();
};
CreateProgramButton.OnPressed += _ =>
{
if (_dialog != null)
{
_dialog.MoveToFront();
return;
}
if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
return;
var field = "title";
var prompt = Loc.GetString("autodoc-program-title");
var placeholder = Loc.GetString("autodoc-program-title-placeholder", ("number", comp.Programs.Count + 1));
var entry = new QuickDialogEntry(field, QuickDialogEntryType.ShortText, prompt, placeholder);
var entries = new List<QuickDialogEntry> { entry };
_dialog = new DialogWindow(CreateProgramButton.Text!, entries);
_dialog.OnConfirmed += responses =>
{
var title = responses[field].Trim();
if (title.Length < 1 || title.Length > comp.MaxProgramTitleLength)
return;
OnCreateProgram?.Invoke(title);
};
// prevent MoveToFront being called on a closed window and double closing
_dialog.OnClose += () => _dialog = null;
};
AbortButton.AddStyleClass("Caution");
AbortButton.OnPressed += _ => OnStop?.Invoke();
UpdateActive();
UpdatePrograms();
}
public void UpdateActive()
{
if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
return;
// UI must be in the inactive state by default, since this wont run when inactive at startup
var active = _entMan.HasComponent<ActiveAutodocComponent>(_owner);
if (active == _active)
return;
_active = active;
CreateProgramButton.Disabled = active || _programCount >= comp.MaxPrograms;
AbortButton.Disabled = !active;
foreach (var button in Programs.Children)
{
((Button) button).Disabled = active;
}
if (!active)
return;
// close windows that can only be open when inactive
_dialog?.Close();
_currentProgram?.Close();
}
private void UpdatePrograms()
{
if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
return;
var count = comp.Programs.Count;
if (count == _programCount)
return;
_programCount = count;
CreateProgramButton.Disabled = _active || _programCount >= comp.MaxPrograms;
Programs.RemoveAllChildren();
for (int i = 0; i < comp.Programs.Count; i++)
{
var button = new Button()
{
Text = comp.Programs[i].Title
};
var index = i;
button.OnPressed += _ => OpenProgram(index);
button.Disabled = _active;
Programs.AddChild(button);
}
}
private async void ImportProgram()
{
if (await _dialogMan.OpenFile(new FileDialogFilters(new FileDialogFilters.Group("yml"))) is not {} file)
return;
try
{
using var reader = new StreamReader(file, EncodingHelpers.UTF8);
var yamlStream = new YamlStream();
yamlStream.Load(reader);
var root = yamlStream.Documents[0].RootNode;
var program = _serMan.Read<AutodocProgram>(root.ToDataNode(), notNullableOverride: true);
OnImportProgram?.Invoke(program);
}
catch (Exception e)
{
_sawmill.Error($"Error when importing program: {e}");
}
}
private void OpenProgram(int index)
{
if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
return;
// no editing multiple programs at once
if (_currentProgram is {} existing)
existing.Close();
var window = new AutodocProgramWindow(_owner, comp.Programs[index]);
window.OnToggleSafety += () => OnToggleProgramSafety?.Invoke(index);
window.OnRemoveProgram += () =>
{
OnRemoveProgram?.Invoke(index);
Programs.RemoveChild(index);
};
window.OnAddStep += (step, stepIndex) => OnAddStep?.Invoke(index, step, stepIndex);
window.OnRemoveStep += step => OnRemoveStep?.Invoke(index, step);
window.OnStart += () =>
{
if (_active)
return;
OnStart?.Invoke(index);
// predict it starting the program
_entMan.EnsureComponent<ActiveAutodocComponent>(_owner);
};
window.OnClose += () => _currentProgram = null;
_currentProgram = window;
window.OpenCentered();
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdateActive();
UpdatePrograms();
}
}

View File

@ -1,20 +0,0 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'autodoc-add-step-surgery'}"
MinSize="600 400">
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
<BoxContainer Orientation="Vertical" MinWidth="200" HorizontalAlignment="Center">
<BoxContainer Name="Symmetry" Orientation="Horizontal" HorizontalExpand="True"/> <!-- Populated with a radio option of BodyPartSymmetry values -->
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
<ScrollContainer VerticalExpand="True" HorizontalExpand="True">
<ItemList Name="Parts"/> <!-- Populated with BodyPartType values -->
</ScrollContainer>
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
<Button Name="SubmitButton" Text="{Loc 'autodoc-submit'}" Disabled="True"/>
</BoxContainer>
<ScrollContainer HorizontalExpand="True">
<ItemList Name="Surgeries"/> <!-- Populated with SharedSurgerySystem.AllSurgeries -->
</ScrollContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@ -1,147 +0,0 @@
using Content.Client.UserInterface.Controls;
using Content.Shared._Shitmed.Autodoc;
using Content.Shared._Shitmed.Medical.Surgery;
using Content.Shared._Shitmed.Medical.Surgery.Conditions;
using Content.Shared.Body.Part;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._Shitmed.Autodoc;
[GenerateTypedNameReferences]
public sealed partial class PickSurgeryWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly SharedSurgerySystem _surgery;
public event Action<IAutodocStep>? OnAddStep;
private BodyPartType? _part;
private BodyPartSymmetry? _symmetry;
private EntProtoId<SurgeryComponent>? _surgeryId;
public PickSurgeryWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_surgery = _entMan.System<SharedSurgerySystem>();
OnAddStep += _ => Close();
var options = new RadioOptions<BodyPartSymmetry?>(RadioOptionsLayout.Horizontal);
options.ButtonStyle = "OpenBoth";
options.FirstButtonStyle = "OpenRight";
options.LastButtonStyle = "OpenLeft";
options.AddItem(Loc.GetString("autodoc-body-symmetry-ignored"), null);
foreach (var symmetry in Enum.GetValues<BodyPartSymmetry>())
{
var name = Loc.GetString("autodoc-body-symmetry-" + symmetry.ToString());
options.AddItem(name, symmetry);
}
Symmetry.AddChild(options);
options.OnItemSelected += args =>
{
args.Button.Select(args.Id);
_symmetry = args.Button.SelectedValue;
UpdateSurgeries();
};
foreach (var part in Enum.GetValues<BodyPartType>())
{
var name = Loc.GetString("autodoc-body-part-" + part.ToString());
Parts.AddItem(name, metadata: part);
}
Parts.OnItemSelected += args =>
{
if (Parts[args.ItemIndex].Metadata is not BodyPartType part)
return;
_part = part;
UpdateSubmit();
UpdateSurgeries();
};
Parts.OnItemDeselected += _ =>
{
_part = null;
SubmitButton.Disabled = true;
};
Surgeries.OnItemSelected += args =>
{
if (Surgeries[args.ItemIndex].Metadata is not EntProtoId<SurgeryComponent> id)
return;
_surgeryId = id;
UpdateSubmit();
};
Surgeries.OnItemDeselected += _ =>
{
_surgeryId = null;
SubmitButton.Disabled = true;
};
SubmitButton.OnPressed += _ =>
{
var step = new SurgeryAutodocStep()
{
Part = _part!.Value,
Symmetry = _symmetry,
Surgery = _surgeryId!.Value
};
OnAddStep?.Invoke(step);
};
UpdateSurgeries();
}
// doesn't handle prototype reload so you have to reopen the window to see new surgeries
private void UpdateSurgeries()
{
Surgeries.Clear();
foreach (var id in _surgery.AllSurgeries)
{
var name = _proto.Index(id).Name;
var protoId = new EntProtoId<SurgeryComponent>(id);
if (_part is not BodyPartType part)
{
Surgeries.AddItem(name, metadata: protoId);
continue;
}
var ent = _surgery.GetSingleton(protoId);
if (ent is null)
continue;
if (!_entMan.TryGetComponent<SurgeryPartConditionComponent>(ent.Value, out var comp))
{
Surgeries.AddItem(name, metadata: protoId);
continue;
}
var partOk = comp.Part == part;
var symmetryOk = (comp.Symmetry == null || _symmetry == null) ? true : comp.Symmetry == _symmetry;
var passesFilter = (partOk && symmetryOk) ^ comp.Inverse;
if (passesFilter)
Surgeries.AddItem(name, metadata: protoId);
}
Surgeries.SortItemsByText();
}
private void UpdateSubmit()
{
// symmetry is optional, others are not
SubmitButton.Disabled = _part == null || _surgeryId == null;
}
}

View File

@ -1,5 +0,0 @@
using Content.Shared._Shitmed.Autodoc.Systems;
namespace Content.Client._Shitmed.Autodoc.Systems;
public sealed class AutodocSystem : SharedAutodocSystem;

View File

@ -1,7 +0,0 @@
// DeltaV - When "Fix eating and drinking verbs showing up after a short delay and making your verb UI bounce (#38164)"
// was merged from upstream, it moved StomachComponent into shared, causing a duplicate component registered
// crash on client startup. I have no idea if me commenting this out is problematic, but i don't think so?
//namespace Content.Client._Shitmed.Body.Components;
//[RegisterComponent]
//public sealed partial class StomachComponent : Component { }

View File

@ -1,17 +0,0 @@
<ui:ChoiceControl
xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client._Shitmed.Choice.UI">
<BoxContainer Orientation="Horizontal">
<Button Name="Button" Access="Public"
HorizontalExpand="True" VerticalExpand="False"
StyleClasses="ButtonSquare" Margin="0">
<BoxContainer Orientation="Horizontal" Margin="0">
<TextureRect Name="Texture" Access="Public"
HorizontalExpand="False" VerticalExpand="False"
Margin="1"/>
<Control MinWidth="5"/>
<RichTextLabel Name="NameLabel" Access="Public" VerticalAlignment="Center"/>
</BoxContainer>
</Button>
</BoxContainer>
</ui:ChoiceControl>

View File

@ -1,26 +0,0 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client._Shitmed.Choice.UI;
[GenerateTypedNameReferences]
[Virtual]
public partial class ChoiceControl : Control
{
public ChoiceControl() => RobustXamlLoader.Load(this);
public void Set(string name, Texture? texture)
{
NameLabel.SetMessage(name);
Texture.Texture = texture;
}
public void Set(FormattedMessage msg, Texture? texture)
{
NameLabel.SetMessage(msg);
Texture.Texture = texture;
}
}

View File

@ -1,473 +0,0 @@
using Content.Client._Shitmed.Choice.UI;
using Content.Client.Administration.UI.CustomControls;
using Content.Shared._Shitmed.Medical.Surgery;
using Content.Shared._Shitmed.Medical.Surgery.Steps.Parts; // DeltaV - New UI
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Humanoid;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls; // DeltaV - New UI
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client._Shitmed.Medical.Surgery;
[UsedImplicitly]
public sealed class SurgeryBui : BoundUserInterface
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPlayerManager _player = default!;
private readonly SurgerySystem _system;
[ViewVariables]
private SurgeryWindow? _window;
private EntityUid? _part;
private bool _isBody;
private (EntityUid Ent, EntProtoId Proto)? _surgery;
private readonly List<EntProtoId> _previousSurgeries = new();
private (EntityUid Ent, TextureButton)? _previousPart; // DeltaV - New UI
public SurgeryBui(EntityUid owner, Enum uiKey) : base(owner, uiKey) => _system = _entities.System<SurgerySystem>();
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
if (_window is null
|| message is not SurgeryBuiRefreshMessage)
return;
RefreshUI();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
if (state is not SurgeryBuiState s)
return;
Update(s);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_window?.Dispose();
}
private void Update(SurgeryBuiState state)
{
if (_window == null)
{
_window = new SurgeryWindow();
_window.OnClose += Close;
_window.Title = Loc.GetString("surgery-ui-window-title");
_window.PartsButton.OnPressed += _ =>
{
_part = null;
_isBody = false;
_surgery = null;
_previousSurgeries.Clear();
DeactivateOtherParts(); // DeltaV - New UI
View(ViewType.Parts);
};
_window.SurgeriesButton.OnPressed += _ =>
{
_surgery = null;
_previousSurgeries.Clear();
if (!_entities.TryGetNetEntity(_part, out var netPart)
|| State is not SurgeryBuiState s
|| !s.Choices.TryGetValue(netPart.Value, out var surgeries))
return;
OnPartPressed(netPart.Value, surgeries);
};
_window.StepsButton.OnPressed += _ =>
{
if (!_entities.TryGetNetEntity(_part, out var netPart)
|| _previousSurgeries.Count == 0)
return;
var last = _previousSurgeries[^1];
_previousSurgeries.RemoveAt(_previousSurgeries.Count - 1);
if (_system.GetSingleton(last) is not { } previousId
|| !_entities.TryGetComponent(previousId, out SurgeryComponent? previous))
return;
OnSurgeryPressed((previousId, previous), netPart.Value, last);
};
}
_window.Surgeries.RemoveAllChildren();
_window.Steps.RemoveAllChildren();
_window.Parts.RemoveAllChildren();
View(ViewType.Parts);
var oldSurgery = _surgery;
var oldPart = _part;
_part = null;
_surgery = null;
var options = new List<(NetEntity netEntity, EntityUid entity, string Name, BodyPartType? PartType)>();
foreach (var choice in state.Choices.Keys)
if (_entities.TryGetEntity(choice, out var ent))
{
if (_entities.TryGetComponent(ent, out BodyPartComponent? part))
options.Add((choice, ent.Value, _entities.GetComponent<MetaDataComponent>(ent.Value).EntityName, part.PartType));
else if (_entities.TryGetComponent(ent, out BodyComponent? body))
options.Add((choice, ent.Value, _entities.GetComponent<MetaDataComponent>(ent.Value).EntityName, null));
}
options.Sort((a, b) =>
{
int GetScore(BodyPartType? partType)
{
return partType switch
{
BodyPartType.Head => 1,
BodyPartType.Torso => 2,
BodyPartType.Arm => 3,
BodyPartType.Hand => 4,
BodyPartType.Leg => 5,
BodyPartType.Foot => 6,
// BodyPartType.Tail => 7, No tails yet!
BodyPartType.Other => 8,
_ => 9
};
}
return GetScore(a.PartType) - GetScore(b.PartType);
});
// DeltaV Start - New UI
foreach (var textureButton in _window.Buttons)
{
textureButton.Visible = false;
textureButton.StyleIdentifier = "SurgeryTextureButton";
}
// DeltaV End - New UI
foreach (var (netEntity, entity, partName, bodyPartType) in options) // DeltaV - New UI
{
//var netPart = _entities.GetNetEntity(part.Owner);
var surgeries = state.Choices[netEntity];
// Delta Start - New UI
var button = GetBodyPartTextureButton(entity, bodyPartType);
if (button != null)
{
button.Visible = true;
button.OnPressed += _ => OnPartPressed(netEntity, surgeries);
if (_entities.HasComponent<IncisionOpenComponent>(entity))
button.StyleIdentifier = "OpenIncision";
}
else
{
var partButton = new ChoiceControl();
partButton.Set(partName, null);
partButton.Button.OnPressed += _ => OnPartPressed(netEntity, surgeries);
_window.Parts.AddChild(partButton);
}
// Delta End - New UI
foreach (var surgeryId in surgeries)
{
if (_system.GetSingleton(surgeryId) is not { } surgery ||
!_entities.TryGetComponent(surgery, out SurgeryComponent? surgeryComp))
continue;
if (oldPart == entity && oldSurgery?.Proto == surgeryId)
OnSurgeryPressed((surgery, surgeryComp), netEntity, surgeryId);
}
if (oldPart == entity && oldSurgery == null)
OnPartPressed(netEntity, surgeries);
}
if (!_window.IsOpen)
_window.OpenCentered();
}
private void AddStep(EntProtoId stepId, NetEntity netPart, EntProtoId surgeryId)
{
if (_window == null
|| _system.GetSingleton(stepId) is not { } step)
return;
var stepName = new FormattedMessage();
stepName.AddText(_entities.GetComponent<MetaDataComponent>(step).EntityName);
var stepButton = new SurgeryStepButton { Step = step };
stepButton.Button.OnPressed += _ => SendMessage(new SurgeryStepChosenBuiMsg(netPart, surgeryId, stepId, _isBody));
_window.Steps.AddChild(stepButton);
}
private void OnSurgeryPressed(Entity<SurgeryComponent> surgery, NetEntity netPart, EntProtoId surgeryId)
{
if (_window == null)
return;
_part = _entities.GetEntity(netPart);
_isBody = _entities.HasComponent<BodyComponent>(_part);
_surgery = (surgery, surgeryId);
_window.Steps.RemoveAllChildren();
// This apparently does not consider if theres multiple surgery requirements in one surgery. Maybe thats fine.
if (surgery.Comp.Requirement is { } requirementId && _system.GetSingleton(requirementId) is { } requirement)
{
var label = new ChoiceControl();
label.Button.OnPressed += _ =>
{
_previousSurgeries.Add(surgeryId);
if (_entities.TryGetComponent(requirement, out SurgeryComponent? requirementComp))
OnSurgeryPressed((requirement, requirementComp), netPart, requirementId);
};
var msg = new FormattedMessage();
var surgeryName = _entities.GetComponent<MetaDataComponent>(requirement).EntityName;
msg.AddMarkup($"[bold]{Loc.GetString("surgery-ui-window-require")}: {surgeryName}[/bold]");
label.Set(msg, null);
_window.Steps.AddChild(label);
_window.Steps.AddChild(new HSeparator { Margin = new Thickness(0, 0, 0, 1) });
}
foreach (var stepId in surgery.Comp.Steps)
AddStep(stepId, netPart, surgeryId);
View(ViewType.Steps);
RefreshUI();
}
private void OnPartPressed(NetEntity netPart, List<EntProtoId> surgeryIds)
{
if (_window == null)
return;
_part = _entities.GetEntity(netPart);
_isBody = _entities.HasComponent<BodyComponent>(_part);
// DeltaV Start - New UI
var bodyPart = _entities.GetComponent<BodyPartComponent>(_part.Value);
var body = bodyPart.Body!.Value;
if (_previousPart != null)
{
(var previousEnt, var previousButton) = _previousPart.Value;
if (!_entities.HasComponent<IncisionOpenComponent>(previousEnt))
previousButton.StyleIdentifier = "SurgeryTextureButton";
}
var button = GetBodyPartTextureButton(_part.Value, bodyPart.PartType);
if (button != null && button.Pressed)
{
_previousPart = (_part.Value, button);
DeactivateOtherParts(button);
}
else if (button != null && !button.Pressed)
{
_part = null;
_isBody = false;
_surgery = null;
_previousSurgeries.Clear();
View(ViewType.Parts);
return;
}
// DeltaV End - New UI
_window.Surgeries.RemoveAllChildren();
var surgeries = new List<(Entity<SurgeryComponent> Ent, EntProtoId Id, string Name)>();
foreach (var surgeryId in surgeryIds)
{
if (_system.GetSingleton(surgeryId) is not { } surgery ||
!_entities.TryGetComponent(surgery, out SurgeryComponent? surgeryComp))
{
continue;
}
// Begin DeltaV Additions - only show surgeries with completed requirements
if (surgeryComp.Requirement is { } reqId && _system.GetSingleton(reqId) is { } reqUid)
{
if (!_entities.TryGetComponent<SurgeryComponent>(reqUid, out var reqComp) ||
!_system.PreviousStepsComplete(body, _part.Value, (reqUid, reqComp), string.Empty)) // step is unused as this is only for checking the requirement
{
// don't show any surgeries whose requirement isn't complete
continue;
}
}
// End DeltaV Additions
var name = _entities.GetComponent<MetaDataComponent>(surgery).EntityName;
surgeries.Add(((surgery, surgeryComp), surgeryId, name));
}
surgeries.Sort((a, b) =>
{
var priority = a.Ent.Comp.Priority.CompareTo(b.Ent.Comp.Priority);
if (priority != 0)
return priority;
return string.Compare(a.Name, b.Name, StringComparison.Ordinal);
});
foreach (var surgery in surgeries)
{
var surgeryButton = new ChoiceControl();
surgeryButton.Set(surgery.Name, null);
surgeryButton.Button.OnPressed += _ => OnSurgeryPressed(surgery.Ent, netPart, surgery.Id);
_window.Surgeries.AddChild(surgeryButton);
}
RefreshUI();
View(ViewType.Surgeries);
}
// DeltaV Start - New UI
private TextureButton? GetBodyPartTextureButton(EntityUid entity, BodyPartType? partType)
{
if (_window == null
|| !_entities.TryGetComponent(entity, out BodyPartComponent? bodyPart))
return null;
var isLeftPart = bodyPart.Symmetry.HasFlag(BodyPartSymmetry.Left);
switch (partType)
{
case BodyPartType.Torso:
var isHumanoid = _entities.HasComponent<HumanoidAppearanceComponent>(bodyPart.Body);
return isHumanoid ? _window.ChestButton : _window.CarpButton;
case BodyPartType.Head:
return _window.HeadButton;
case BodyPartType.Arm:
return isLeftPart ? _window.LArmButton : _window.RArmButton;
case BodyPartType.Hand:
return isLeftPart ? _window.LHandButton : _window.RHandButton;
case BodyPartType.Leg:
return isLeftPart ? _window.LLegButton : _window.RLegButton;
case BodyPartType.Foot:
return isLeftPart ? _window.LFootButton : _window.RFootButton;
case BodyPartType.Tail:
case BodyPartType.Other:
case null:
return null;
default:
throw new ArgumentOutOfRangeException(nameof(partType), partType, null);
}
}
/// <summary>
/// Deactivates all other buttons that are currently active.
/// </summary>
/// <param name="activatedButton">The button that should be the only active button.</param>
private void DeactivateOtherParts(TextureButton? activatedButton = null)
{
if (_window == null)
return;
foreach (var button in _window.Buttons)
{
if (activatedButton == button)
continue;
button.Pressed = false;
}
}
// DeltaV End - New UI
private void RefreshUI()
{
if (_window == null
|| !_window.IsOpen
|| _part == null
|| !_entities.HasComponent<SurgeryComponent>(_surgery?.Ent)
|| !_entities.TryGetComponent(_player.LocalEntity ?? EntityUid.Invalid, out SurgeryTargetComponent? surgeryComp)
|| !surgeryComp.CanOperate)
return;
var next = _system.GetNextStep(Owner, _part.Value, _surgery.Value.Ent);
var i = 0;
foreach (var child in _window.Steps.Children)
{
if (child is not SurgeryStepButton stepButton)
continue;
var status = StepStatus.Incomplete;
if (next == null)
status = StepStatus.Complete;
else if (next.Value.Surgery.Owner != _surgery.Value.Ent)
status = StepStatus.Incomplete;
else if (next.Value.Step == i)
status = StepStatus.Next;
else if (i < next.Value.Step)
status = StepStatus.Complete;
stepButton.Button.Disabled = status != StepStatus.Next;
var stepName = new FormattedMessage();
stepName.AddText(_entities.GetComponent<MetaDataComponent>(stepButton.Step).EntityName);
if (status == StepStatus.Complete)
stepButton.Button.Modulate = Color.Green;
else
{
stepButton.Button.Modulate = Color.White;
if (_player.LocalEntity is { } player
&& status == StepStatus.Next
&& !_system.CanPerformStep(player, Owner, _part.Value, stepButton.Step, false, out var popup, out var reason, out _))
stepButton.ToolTip = popup;
}
var texture = _entities.GetComponentOrNull<SpriteComponent>(stepButton.Step)?.Icon?.Default;
stepButton.Set(stepName, texture);
i++;
}
}
private void View(ViewType type)
{
if (_window == null)
return;
_window.PartsButton.Parent!.Margin = new Thickness(0, 0, 0, 10);
_window.Parts.Visible = type == ViewType.Parts;
_window.PartsButton.Disabled = type == ViewType.Parts;
_window.Surgeries.Visible = type == ViewType.Surgeries;
_window.SurgeriesButton.Disabled = type != ViewType.Steps;
_window.Steps.Visible = type == ViewType.Steps;
_window.StepsButton.Disabled = type != ViewType.Steps || _previousSurgeries.Count == 0;
if (_entities.TryGetComponent(_part, out MetaDataComponent? partMeta) &&
_entities.TryGetComponent(_surgery?.Ent, out MetaDataComponent? surgeryMeta))
_window.Title = $"Surgery - {partMeta.EntityName}, {surgeryMeta.EntityName}";
else if (partMeta != null)
_window.Title = $"Surgery - {partMeta.EntityName}";
else
_window.Title = "Surgery";
}
private enum ViewType
{
Parts,
Surgeries,
Steps
}
private enum StepStatus
{
Next,
Complete,
Incomplete
}
}

View File

@ -1,4 +0,0 @@
<controls:SurgeryStepButton
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client._Shitmed.Medical.Surgery">
</controls:SurgeryStepButton>

View File

@ -1,16 +0,0 @@
using Content.Client._Shitmed.Choice.UI;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._Shitmed.Medical.Surgery;
[GenerateTypedNameReferences]
public sealed partial class SurgeryStepButton : ChoiceControl
{
public EntityUid Step { get; set; }
public SurgeryStepButton()
{
RobustXamlLoader.Load(this);
}
}

View File

@ -1,11 +0,0 @@
using Content.Shared._Shitmed.Medical.Surgery;
namespace Content.Client._Shitmed.Medical.Surgery;
public sealed class SurgerySystem : SharedSurgerySystem
{
public override void Initialize()
{
base.Initialize();
}
}

View File

@ -1,80 +0,0 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:customcontrols="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:ui="clr-namespace:Content.Client._Shitmed.Medical.Surgery"
MinSize="400 400">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True" MinSize="570, 336">
<BoxContainer Orientation="Vertical" HorizontalExpand="False" VerticalExpand="False" MinSize="264, 408">
<TextureRect Name="Texture" TextureScale="6 6"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUIBackground.png" Access="Public"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="KeepAspectCentered" StyleClasses="">
<PanelContainer>
<TextureButton
Name="CarpButton" Scale="8 8" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0 0 0 11"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUICarp.png" Access="Public"/>
<TextureButton
Name="ChestButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0 0 0 11"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUITorso.png" Access="Public"/>
<TextureButton
Name="HeadButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0 30 0 0"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUIHead.png" Access="Public"/>
<TextureButton
Name="RArmButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="30 0 0 47"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUIRArm.png" Access="Public"/>
<TextureButton
Name="RHandButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="30 61 0 0"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUIRHand.png" Access="Public"/>
<TextureButton
Name="RLegButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="78 0 0 42"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUIRLeg.png" Access="Public"/>
<TextureButton
Name="RFootButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="54 0 0 30"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUIRFoot.png" Access="Public"/>
<TextureButton
Name="LArmButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 30 47"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUILArm.png" Access="Public"/>
<TextureButton
Name="LHandButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 61 30 0"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUILHand.png" Access="Public"/>
<TextureButton
Name="LLegButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0 0 78 42"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUILLeg.png" Access="Public"/>
<TextureButton
Name="LFootButton" Scale="6 6" ToggleMode="True" StyleIdentifier="SurgeryTextureButton" Visible="False"
VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0 0 54 30"
TexturePath="/Textures/_Shitmed/Interface/SurgeryUI/surgeryUILFoot.png" Access="Public"/>
</PanelContainer>
</TextureRect>
</BoxContainer>
<customcontrols:VSeparator StyleClasses="LowDivider" />
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 0 0 10">
<Button Name="PartsButton" Access="Public" Text="{Loc 'surgery-ui-window-parts'}"
HorizontalExpand="True" StyleClasses="OpenBoth" />
<Button Name="SurgeriesButton" Access="Public" Text="{Loc 'surgery-ui-window-surgeries'}"
HorizontalExpand="True" StyleClasses="OpenBoth" />
<Button Name="StepsButton" Access="Public" Text="{Loc 'surgery-ui-window-steps'}"
HorizontalExpand="True" StyleClasses="OpenBoth" />
</BoxContainer>
<customcontrols:HSeparator />
<ScrollContainer VScrollEnabled="True" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Name="Parts" Access="Public" Orientation="Vertical" Visible="False" />
<BoxContainer Name="Surgeries" Access="Public" Orientation="Vertical" Visible="False" />
<BoxContainer Name="Steps" Access="Public" Orientation="Vertical" Visible="False" />
</ScrollContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@ -1,30 +0,0 @@
using Content.Client.UserInterface.Controls; // DeltaV - New UI
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; // DeltaV - New UI
using Robust.Client.UserInterface.XAML;
namespace Content.Client._Shitmed.Medical.Surgery;
[GenerateTypedNameReferences]
public sealed partial class SurgeryWindow : FancyWindow
{
public List<TextureButton> Buttons { get; } = []; // DeltaV - New UI
public SurgeryWindow()
{
RobustXamlLoader.Load(this);
// DeltaV Start - New UI
Buttons.Add(ChestButton);
Buttons.Add(HeadButton);
Buttons.Add(LArmButton);
Buttons.Add(LHandButton);
Buttons.Add(RArmButton);
Buttons.Add(RHandButton);
Buttons.Add(LLegButton);
Buttons.Add(LFootButton);
Buttons.Add(RLegButton);
Buttons.Add(RFootButton);
Buttons.Add(CarpButton);
// DeltaV End - New UI
}
}

View File

@ -1,102 +0,0 @@
using Content.Shared.Input;
using Content.Shared._Shitmed.Targeting;
using Content.Shared._Shitmed.Targeting.Events;
using Robust.Client.Player;
using Robust.Shared.Input.Binding;
using Robust.Shared.Player;
namespace Content.Client._Shitmed.Targeting;
public sealed class TargetingSystem : SharedTargetingSystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
public event Action<TargetingComponent>? TargetingStartup;
public event Action? TargetingShutdown;
public event Action<TargetBodyPart>? TargetChange;
public event Action<TargetingComponent>? PartStatusStartup;
public event Action<TargetingComponent>? PartStatusUpdate;
public event Action? PartStatusShutdown;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TargetingComponent, LocalPlayerAttachedEvent>(HandlePlayerAttached);
SubscribeLocalEvent<TargetingComponent, LocalPlayerDetachedEvent>(HandlePlayerDetached);
SubscribeLocalEvent<TargetingComponent, ComponentStartup>(OnTargetingStartup);
SubscribeLocalEvent<TargetingComponent, ComponentShutdown>(OnTargetingShutdown);
SubscribeNetworkEvent<TargetIntegrityChangeEvent>(OnTargetIntegrityChange);
CommandBinds.Builder
.Bind(ContentKeyFunctions.TargetHead,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.Head)))
.Bind(ContentKeyFunctions.TargetTorso,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.Torso)))
.Bind(ContentKeyFunctions.TargetLeftArm,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.LeftArm)))
.Bind(ContentKeyFunctions.TargetLeftHand,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.LeftHand)))
.Bind(ContentKeyFunctions.TargetRightArm,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.RightArm)))
.Bind(ContentKeyFunctions.TargetRightHand,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.RightHand)))
.Bind(ContentKeyFunctions.TargetLeftLeg,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.LeftLeg)))
.Bind(ContentKeyFunctions.TargetLeftFoot,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.LeftFoot)))
.Bind(ContentKeyFunctions.TargetRightLeg,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.RightLeg)))
.Bind(ContentKeyFunctions.TargetRightFoot,
InputCmdHandler.FromDelegate((session) => HandleTargetChange(session, TargetBodyPart.RightFoot)))
.Register<SharedTargetingSystem>();
}
private void HandlePlayerAttached(EntityUid uid, TargetingComponent component, LocalPlayerAttachedEvent args)
{
TargetingStartup?.Invoke(component);
PartStatusStartup?.Invoke(component);
}
private void HandlePlayerDetached(EntityUid uid, TargetingComponent component, LocalPlayerDetachedEvent args)
{
TargetingShutdown?.Invoke();
PartStatusShutdown?.Invoke();
}
private void OnTargetingStartup(EntityUid uid, TargetingComponent component, ComponentStartup args)
{
if (_playerManager.LocalEntity != uid)
return;
TargetingStartup?.Invoke(component);
PartStatusStartup?.Invoke(component);
}
private void OnTargetingShutdown(EntityUid uid, TargetingComponent component, ComponentShutdown args)
{
if (_playerManager.LocalEntity != uid)
return;
TargetingShutdown?.Invoke();
PartStatusShutdown?.Invoke();
}
private void OnTargetIntegrityChange(TargetIntegrityChangeEvent args)
{
if (!TryGetEntity(args.Uid, out var uid)
|| !_playerManager.LocalEntity.Equals(uid)
|| !TryComp(uid, out TargetingComponent? component)
|| !args.RefreshUi)
return;
PartStatusUpdate?.Invoke(component);
}
private void HandleTargetChange(ICommonSession? session, TargetBodyPart target)
{
if (session == null
|| session.AttachedEntity is not { } uid
|| !TryComp<TargetingComponent>(uid, out var targeting))
return;
TargetChange?.Invoke(target);
}
}

View File

@ -1,82 +0,0 @@
using Content.Client.Gameplay;
using Content.Client._Shitmed.UserInterface.Systems.PartStatus.Widgets;
using Content.Shared._Shitmed.Targeting;
using Content.Client._Shitmed.Targeting;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controllers;
using Robust.Client.Player;
using Robust.Shared.Utility;
using Robust.Client.Graphics;
namespace Content.Client._Shitmed.UserInterface.Systems.PartStatus;
public sealed class PartStatusUIController : UIController, IOnStateEntered<GameplayState>, IOnSystemChanged<TargetingSystem>
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IEntityNetworkManager _net = default!;
private SpriteSystem _spriteSystem = default!;
private TargetingComponent? _targetingComponent;
private PartStatusControl? PartStatusControl => UIManager.GetActiveUIWidgetOrNull<PartStatusControl>();
public void OnSystemLoaded(TargetingSystem system)
{
system.PartStatusStartup += AddPartStatusControl;
system.PartStatusShutdown += RemovePartStatusControl;
system.PartStatusUpdate += UpdatePartStatusControl;
}
public void OnSystemUnloaded(TargetingSystem system)
{
system.PartStatusStartup -= AddPartStatusControl;
system.PartStatusShutdown -= RemovePartStatusControl;
system.PartStatusUpdate -= UpdatePartStatusControl;
}
public void OnStateEntered(GameplayState state)
{
if (PartStatusControl != null)
{
PartStatusControl.SetVisible(_targetingComponent != null);
if (_targetingComponent != null)
PartStatusControl.SetTextures(_targetingComponent.BodyStatus);
}
}
public void AddPartStatusControl(TargetingComponent component)
{
_targetingComponent = component;
if (PartStatusControl != null)
{
PartStatusControl.SetVisible(_targetingComponent != null);
if (_targetingComponent != null)
PartStatusControl.SetTextures(_targetingComponent.BodyStatus);
}
}
public void RemovePartStatusControl()
{
if (PartStatusControl != null)
PartStatusControl.SetVisible(false);
_targetingComponent = null;
}
public void UpdatePartStatusControl(TargetingComponent component)
{
if (PartStatusControl != null && _targetingComponent != null)
PartStatusControl.SetTextures(_targetingComponent.BodyStatus);
}
public Texture GetTexture(SpriteSpecifier specifier)
{
if (_spriteSystem == null)
_spriteSystem = _entManager.System<SpriteSystem>();
return _spriteSystem.Frame0(specifier);
}
}

View File

@ -1,57 +0,0 @@
<widgets:PartStatusControl
xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:widgets="clr-namespace:Content.Client._Shitmed.UserInterface.Systems.PartStatus.Widgets"
Name="PartStatusIndicator"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Control>
<PanelContainer>
<TextureRect
Name="DollHead"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollTorso"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollGroin"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollLeftArm"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollLeftHand"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollRightArm"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollRightHand"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollLeftLeg"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollLeftFoot"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollRightLeg"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
<TextureRect
Name="DollRightFoot"
Stretch="KeepAspectCentered"
SetSize="64 64"/>
</PanelContainer>
</Control>
</widgets:PartStatusControl>

View File

@ -1,50 +0,0 @@
using Content.Client._Shitmed.UserInterface.Systems.PartStatus;
using Content.Shared._Shitmed.Targeting;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
using System.Linq;
namespace Content.Client._Shitmed.UserInterface.Systems.PartStatus.Widgets;
[GenerateTypedNameReferences]
public sealed partial class PartStatusControl : UIWidget
{
private readonly Dictionary<TargetBodyPart, TextureRect> _partStatusControls;
private readonly PartStatusUIController _controller;
public PartStatusControl()
{
RobustXamlLoader.Load(this);
_controller = UserInterfaceManager.GetUIController<PartStatusUIController>();
_partStatusControls = new Dictionary<TargetBodyPart, TextureRect>
{
{ TargetBodyPart.Head, DollHead },
{ TargetBodyPart.Torso, DollTorso },
{ TargetBodyPart.Groin, DollGroin },
{ TargetBodyPart.LeftArm, DollLeftArm },
{ TargetBodyPart.LeftHand, DollLeftHand },
{ TargetBodyPart.RightArm, DollRightArm },
{ TargetBodyPart.RightHand, DollRightHand },
{ TargetBodyPart.LeftLeg, DollLeftLeg },
{ TargetBodyPart.LeftFoot, DollLeftFoot },
{ TargetBodyPart.RightLeg, DollRightLeg },
{ TargetBodyPart.RightFoot, DollRightFoot }
};
}
public void SetTextures(Dictionary<TargetBodyPart, TargetIntegrity> state)
{
foreach (var (bodyPart, integrity) in state)
{
string enumName = Enum.GetName(typeof(TargetBodyPart), bodyPart) ?? "Unknown";
int enumValue = (int) integrity;
var texture = new SpriteSpecifier.Rsi(new ResPath($"/Textures/_Shitmed/Interface/Targeting/Status/{enumName.ToLowerInvariant()}.rsi"), $"{enumName.ToLowerInvariant()}_{enumValue}");
_partStatusControls[bodyPart].Texture = _controller.GetTexture(texture);
}
}
public void SetVisible(bool visible) => this.Visible = visible;
}

View File

@ -1,82 +0,0 @@
using Content.Client.Gameplay;
using Content.Client._Shitmed.UserInterface.Systems.Targeting.Widgets;
using Content.Shared._Shitmed.Targeting;
using Content.Client._Shitmed.Targeting;
using Content.Shared._Shitmed.Targeting.Events;
using Robust.Client.UserInterface.Controllers;
using Robust.Client.Player;
namespace Content.Client._Shitmed.UserInterface.Systems.Targeting;
public sealed class TargetingUIController : UIController, IOnStateEntered<GameplayState>, IOnSystemChanged<TargetingSystem>
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IEntityNetworkManager _net = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
private TargetingComponent? _targetingComponent;
private TargetingControl? TargetingControl => UIManager.GetActiveUIWidgetOrNull<TargetingControl>();
public void OnSystemLoaded(TargetingSystem system)
{
system.TargetingStartup += AddTargetingControl;
system.TargetingShutdown += RemoveTargetingControl;
system.TargetChange += CycleTarget;
}
public void OnSystemUnloaded(TargetingSystem system)
{
system.TargetingStartup -= AddTargetingControl;
system.TargetingShutdown -= RemoveTargetingControl;
system.TargetChange -= CycleTarget;
}
public void OnStateEntered(GameplayState state)
{
if (TargetingControl == null)
return;
TargetingControl.SetTargetDollVisible(_targetingComponent != null);
if (_targetingComponent != null)
TargetingControl.SetBodyPartsVisible(_targetingComponent.Target);
}
public void AddTargetingControl(TargetingComponent component)
{
_targetingComponent = component;
if (TargetingControl != null)
{
TargetingControl.SetTargetDollVisible(_targetingComponent != null);
if (_targetingComponent != null)
TargetingControl.SetBodyPartsVisible(_targetingComponent.Target);
}
}
public void RemoveTargetingControl()
{
if (TargetingControl != null)
TargetingControl.SetTargetDollVisible(false);
_targetingComponent = null;
}
public void CycleTarget(TargetBodyPart bodyPart)
{
if (_playerManager.LocalEntity is not { } user
|| _entManager.GetComponent<TargetingComponent>(user) is not { } targetingComponent
|| TargetingControl == null)
return;
var player = _entManager.GetNetEntity(user);
if (bodyPart != targetingComponent.Target)
{
var msg = new TargetChangeEvent(player, bodyPart);
_net.SendSystemNetworkMessage(msg);
TargetingControl?.SetBodyPartsVisible(bodyPart);
}
}
}

View File

@ -1,216 +0,0 @@
<widgets:TargetingControl
xmlns="https://spacestation14.io"
xmlns:widgets="clr-namespace:Content.Client._Shitmed.UserInterface.Systems.Targeting.Widgets"
Name="TargetingButton"
VerticalExpand="True"
VerticalAlignment="Bottom"
HorizontalAlignment="Right">
<Control HorizontalAlignment="Right">
<TextureRect
Name="TargetDoll"
Stretch="KeepAspectCentered"
SetSize="96 96">
<PanelContainer
SetSize="57 96"
Margin="18 0 0 0"
VerticalAlignment="Bottom"
HorizontalAlignment="Left">
<PanelContainer
SetSize="18 39"
Margin="0 7 0 0"
HorizontalAlignment="Left">
<TextureButton
Name="RightArmButton"
MinSize="18 30"
StyleClasses="TargetDollButtonRightArm"
VerticalAlignment="Top">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/rightarm.png"
Stretch="KeepAspectCentered"
SetSize="12 24"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
<TextureButton
Name="RightHandButton"
MinSize="18 18"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonRightHand">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/righthand.png"
Stretch="KeepAspectCentered"
SetSize="12 12"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
</PanelContainer>
<PanelContainer
SetSize="51 90"
Margin="0 64 0 0"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
<TextureButton
Name="HeadButton"
MinSize="33 27"
VerticalAlignment="Top"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonHead">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/head.png"
Stretch="KeepAspectCentered"
SetSize="27 21"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<!--<PanelContainer
SetSize="15 15"
Margin="0 9 0 0"
HorizontalAlignment="Center">
<TextureButton
Name="EyesButton"
MinSize="15 9"
VerticalAlignment="Top"
StyleClasses="TargetDollButtonEyes">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/eyes.png"
Stretch="KeepAspectCentered"
SetSize="15 9"/>
</TextureButton>
<TextureButton
Name="MouthButton"
SetSize="9 6"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonMouth">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/mouth.png"
Stretch="KeepAspectCentered"
SetSize="9 6"/>
</TextureButton>
</PanelContainer>-->
</TextureButton>
<TextureButton
Name="ChestButton"
SetSize="33 36"
Margin="0 21 0 0"
VerticalAlignment="Top"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonChest">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/torso.png"
Stretch="KeepAspectCentered"
SetSize="27 30"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
<PanelContainer
MinSize="45 42"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
<TextureButton
Name="GroinButton"
MinSize="33 18"
VerticalAlignment="Top"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonGroin">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/groin.png"
Stretch="KeepAspectCentered"
SetSize="27 12"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
<PanelContainer
MinSize="24 36"
VerticalAlignment="Bottom"
HorizontalAlignment="Right">
<TextureButton
Name="LeftLegButton"
MinSize="18 33"
VerticalAlignment="Top"
HorizontalAlignment="Left"
StyleClasses="TargetDollButtonLeftLeg">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/leftleg.png"
Stretch="KeepAspectCentered"
SetSize="12 27"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
<TextureButton
Name="LeftFootButton"
MinSize="24 12"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonLeftFoot">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/leftfoot.png"
Stretch="KeepAspectCentered"
SetSize="18 9"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
</PanelContainer>
<PanelContainer
MinSize="24 36"
VerticalAlignment="Bottom"
HorizontalAlignment="Left">
<TextureButton
Name="RightLegButton"
MinSize="18 33"
VerticalAlignment="Top"
HorizontalAlignment="Right"
StyleClasses="TargetDollButtonRightLeg">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/rightleg.png"
Stretch="KeepAspectCentered"
SetSize="12 27"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
<TextureButton
Name="RightFootButton"
MinSize="24 12"
VerticalAlignment="Bottom"
HorizontalAlignment="Center"
StyleClasses="TargetDollButtonRightFoot">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/rightfoot.png"
Stretch="KeepAspectCentered"
SetSize="18 9"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
</PanelContainer>
</PanelContainer>
</PanelContainer>
<PanelContainer
SetSize="18 39"
Margin="0 7 0 0"
HorizontalAlignment="Right">
<TextureButton
Name="LeftArmButton"
MinSize="18 30"
StyleClasses="TargetDollButtonLeftArm"
VerticalAlignment="Top">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/leftarm.png"
Stretch="KeepAspectCentered"
SetSize="12 24"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
<TextureButton
Name="LeftHandButton"
MinSize="18 18"
VerticalAlignment="Bottom"
StyleClasses="TargetDollButtonLeftHand">
<TextureRect
TexturePath="/Textures/_Shitmed/Interface/Targeting/Doll/lefthand.png"
Stretch="KeepAspectCentered"
SetSize="12 12"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</TextureButton>
</PanelContainer>
</PanelContainer>
</TextureRect>
</Control>
</widgets:TargetingControl>

View File

@ -1,58 +0,0 @@
using System.Linq;
using Content.Shared._Shitmed.Targeting;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._Shitmed.UserInterface.Systems.Targeting.Widgets;
[GenerateTypedNameReferences]
public sealed partial class TargetingControl : UIWidget
{
private readonly TargetingUIController _controller;
private readonly Dictionary<TargetBodyPart, TextureButton> _bodyPartControls;
public TargetingControl()
{
RobustXamlLoader.Load(this);
_controller = UserInterfaceManager.GetUIController<TargetingUIController>();
_bodyPartControls = new Dictionary<TargetBodyPart, TextureButton>
{
// TODO: ADD EYE AND MOUTH TARGETING
{ TargetBodyPart.Head, HeadButton },
{ TargetBodyPart.Torso, ChestButton },
{ TargetBodyPart.Groin, GroinButton },
{ TargetBodyPart.LeftArm, LeftArmButton },
{ TargetBodyPart.LeftHand, LeftHandButton },
{ TargetBodyPart.RightArm, RightArmButton },
{ TargetBodyPart.RightHand, RightHandButton },
{ TargetBodyPart.LeftLeg, LeftLegButton },
{ TargetBodyPart.LeftFoot, LeftFootButton },
{ TargetBodyPart.RightLeg, RightLegButton },
{ TargetBodyPart.RightFoot, RightFootButton },
};
foreach (var bodyPartButton in _bodyPartControls)
{
bodyPartButton.Value.MouseFilter = MouseFilterMode.Stop;
bodyPartButton.Value.OnPressed += _ => SetActiveBodyPart(bodyPartButton.Key);
TargetDoll.Texture = Theme.ResolveTexture("target_doll");
}
}
private void SetActiveBodyPart(TargetBodyPart bodyPart) => _controller.CycleTarget(bodyPart);
public void SetBodyPartsVisible(TargetBodyPart bodyPart)
{
foreach (var bodyPartButton in _bodyPartControls)
bodyPartButton.Value.Children.First().Visible = bodyPartButton.Key == bodyPart;
}
protected override void OnThemeUpdated() => TargetDoll.Texture = Theme.ResolveTexture("target_doll");
public void SetTargetDollVisible(bool visible) => Visible = visible;
}

View File

@ -47,11 +47,11 @@ public sealed class SaveLoadReparentTest
var parts = bodySystem.GetBodyChildren(human).Skip(1).ToArray();
var organs = bodySystem.GetBodyOrgans(human).ToArray();
Assert.Multiple(() =>
{
Assert.That(parts, Is.Not.Empty);
Assert.That(organs, Is.Not.Empty);
});
// Assert.Multiple(() =>
// {
// Assert.That(parts, Is.Not.Empty);
// Assert.That(organs, Is.Not.Empty);
// });
foreach (var (id, component) in parts)
{
@ -132,11 +132,11 @@ public sealed class SaveLoadReparentTest
parts = bodySystem.GetBodyChildren(human).Skip(1).ToArray();
organs = bodySystem.GetBodyOrgans(human).ToArray();
Assert.Multiple(() =>
{
Assert.That(parts, Is.Not.Empty);
Assert.That(organs, Is.Not.Empty);
});
// Assert.Multiple(() =>
// {
// Assert.That(parts, Is.Not.Empty);
// Assert.That(organs, Is.Not.Empty);
// });
foreach (var (id, component) in parts)
{

View File

@ -315,9 +315,7 @@ namespace Content.IntegrationTests.Tests.Buckle
await server.WaitAssertion(() =>
{
// Still buckled
//Assert.That(buckle.Buckled); // goob edit
// he's not supposed to be buckled with the new falling down system
// do i just did this :trollface:
Assert.That(buckle.Buckled);
// Still with items in hand
foreach (var hand in hands.Hands.Keys)

View File

@ -87,12 +87,8 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(3));
var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c);
var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray();
// Goobstation - IPC have BorgHealth instead of HumanHealth
var expectedDebugIDs = new[] { "Debug1", "Debug2" };
var expectedHealthIDs = new[] { "BorgHealth", "HumanHealth" };
Assert.That(alertIDs, Is.SupersetOf(expectedDebugIDs));
Assert.That(alertIDs, Has.Some.Matches<string>(item => expectedHealthIDs.Contains(item)));
var expectedIDs = new[] { "HumanHealth", "Debug1", "Debug2" };
Assert.That(alertIDs, Is.SupersetOf(expectedIDs));
});
await server.WaitAssertion(() =>
@ -108,12 +104,8 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(2));
var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c);
var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray();
// Goobstation - IPC have BorgHealth instead of HumanHealth
var expectedDebugIDs = new[] { "Debug2" };
var expectedHealthIDs = new[] { "BorgHealth", "HumanHealth" };
Assert.That(alertIDs, Is.SupersetOf(expectedDebugIDs));
Assert.That(alertIDs, Has.Some.Matches<string>(item => expectedHealthIDs.Contains(item)));
var expectedIDs = new[] { "HumanHealth", "Debug2" };
Assert.That(alertIDs, Is.SupersetOf(expectedIDs));
});
await pair.CleanReturnAsync();

View File

@ -24,7 +24,6 @@ using Content.Shared.Station.Components;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components;
using Content.Shared._EE.Silicon.Components; // Goobstation
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.GameRules;
@ -230,15 +229,12 @@ public sealed class NukeOpsTest
var totalSeconds = 30;
var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
var increment = 5;
var resp = entMan.GetComponent<RespiratorComponent>(player);
var damage = entMan.GetComponent<DamageableComponent>(player);
for (var tick = 0; tick < totalTicks; tick += increment)
{
await pair.RunTicksSync(increment);
if (!entMan.HasComponent<SiliconComponent>(player)) // Goobstation - IPC
{
var resp = entMan.GetComponent<RespiratorComponent>(player);
Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
}
Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
}

View File

@ -1,63 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Content.Shared.Humanoid.Prototypes;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests._Shitmed.Body;
[TestFixture]
public sealed class SpeciesBUiTest
{
[TestPrototypes]
private const string Prototypes = @"
- type: entity
name: BaseMobSpeciesTest
id: BaseMobSpeciesTest
parent: BaseMobSpecies
";
private Dictionary<Enum, InterfaceData> GetInterfaces(UserInterfaceComponent comp) =>
(Dictionary<Enum, InterfaceData>)
typeof(UserInterfaceComponent).GetField("Interfaces", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(comp);
[Test]
public async Task AllSpeciesHaveBaseBUiTest()
{
await using var pair = await PoolManager.GetServerClient(new PoolSettings
{
Dirty = true,
Connected = false
});
var server = pair.Server;
var proto = server.ResolveDependency<IPrototypeManager>();
var factoryComp = server.ResolveDependency<IComponentFactory>();
await server.WaitAssertion(() =>
{
var bUiSys = server.System<SharedUserInterfaceSystem>();
Assert.That(proto.TryIndex("BaseMobSpeciesTest", out var baseEnt), Is.True);
Assert.That(baseEnt, Is.Not.Null);
Assert.That(baseEnt.TryGetComponent<UserInterfaceComponent>(out var bUiBase, factoryComp), Is.True);
Assert.That(bUiBase, Is.Not.Null);
var baseKeys = GetInterfaces(bUiBase).Keys.ToArray();
foreach (var species in proto.EnumeratePrototypes<SpeciesPrototype>())
{
var ent = proto.Index(species.Prototype);
Assert.That(ent.TryGetComponent<UserInterfaceComponent>(out var bUi, factoryComp), Is.True);
Assert.That(bUi, Is.Not.Null);
var states = GetInterfaces(bUiBase);
foreach (var key in baseKeys)
{
Assert.That(states.ContainsKey(key), Is.True, $"Species {species.ID} has not UserInterface of type enum.{key.GetType().Name}");
}
}
});
await pair.CleanReturnAsync();
}
}

View File

@ -36,6 +36,7 @@ public sealed partial class AdminVerbSystem
private static readonly EntProtoId ParadoxCloneRuleId = "ParadoxCloneSpawn";
private static readonly EntProtoId DefaultConspiratorRule = "Conspirators"; // Harmony
private static readonly EntProtoId DefaultWizardRule = "Wizard";
private static readonly EntProtoId DefaultHitmanRule = "HitmanRule";
private static readonly EntProtoId DefaultNinjaRule = "NinjaSpawn";
private static readonly ProtoId<StartingGearPrototype> PirateGearId = "PirateGear";
@ -262,7 +263,22 @@ public sealed partial class AdminVerbSystem
};
args.Verbs.Add(agent);
// End DeltaV Additions
// start DeltaV Additions - add hitman
var hitmanName = Loc.GetString("admin-verb-make-hitman");
Verb hitman = new()
{
Text = hitmanName,
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/_DV/Interface/Misc/job_icons.rsi"), "Hitman"),
Act = () =>
{
_antag.ForceMakeAntag<HitmanRuleComponent>(targetPlayer, DefaultHitmanRule);
},
Impact = LogImpact.High,
Message = string.Join(": ", hitmanName, Loc.GetString("admin-verb-text-make-hitman")),
};
args.Verbs.Add(hitman);
// end DeltaV additions - add hitman
// Harmony start
var conspiratorName = Loc.GetString("admin-verb-text-make-conspirator");
Verb conspirator = new()

View File

@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Server._DV.Access.Systems; // DeltaV - Subdermal ID Cards
using Content.Server.Cargo.Components;
using Content.Server.Doors.Systems;
using Content.Server.Hands.Systems;
@ -57,6 +58,7 @@ public sealed partial class AdminVerbSystem
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly GunSystem _gun = default!;
[Dependency] private readonly RevenantAnimatedSystem _revenantAnimate = default!; // Imp
[Dependency] private readonly SubdermalIdCardSystem _subdermalId = default!; // DeltaV - Subdermal ID Cards
private void AddTricksVerbs(GetVerbsEvent<Verb> args)
{
@ -869,6 +871,11 @@ public sealed partial class AdminVerbSystem
}
}
// Begin DeltaV Additions - Subdermal ID chips
if (_subdermalId.TryGetIdCard(target, out var idEntity))
return idEntity;
// End DeltaV Additions - Subdermal ID chips
return null;
}

View File

@ -239,7 +239,7 @@ namespace Content.Server.Atmos.EntitySystems
if (pressure <= Atmospherics.HazardLowPressure)
{
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
_damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false, canSever: false, partMultiplier: 0.2f); // Shitmed Change
_damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false);
if (!barotrauma.TakingDamage)
{
@ -254,7 +254,7 @@ namespace Content.Server.Atmos.EntitySystems
var damageScale = MathF.Min(((pressure / Atmospherics.HazardHighPressure) - 1) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
_damageableSystem.TryChangeDamage(uid, barotrauma.Damage * damageScale, true, false, canSever: false); // Shitmed Change
_damageableSystem.TryChangeDamage(uid, barotrauma.Damage * damageScale, true, false);
if (!barotrauma.TakingDamage)
{

View File

@ -141,10 +141,7 @@ namespace Content.Server.Body.Commands
if (attachAt == default)
attachAt = bodySystem.GetBodyChildren(entity, body).First();
// Shitmed Change Start
var slotId = $"{part.Symmetry.ToString().ToLower()} {part.GetHashCode().ToString()}";
part.SlotId = part.GetHashCode().ToString();
// Shitmed Change End
var slotId = part.GetHashCode().ToString();
if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand, attachAt.Component, part))
{

View File

@ -98,15 +98,8 @@ namespace Content.Server.Body.Commands
return;
}
// Shitmed Change Start
var slotId = "";
if (part.Symmetry != BodyPartSymmetry.None)
slotId = $"{part.Symmetry.ToString().ToLower()} {part.GetHashCode().ToString()}";
else
slotId = $"{part.GetHashCode().ToString()}";
var slotId = $"AttachBodyPartVerb-{partUid}";
part.SlotId = part.GetHashCode().ToString();
// Shitmed Change End
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (body.RootContainer.ContainedEntity != null)
{

View File

@ -1,7 +1,6 @@
using System.Numerics;
using Content.Server.Ghost;
using Content.Server.Humanoid;
using Content.Shared._Shitmed.Body.Part;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events;
using Content.Shared.Body.Part;
@ -19,10 +18,6 @@ using Content.Server.Polymorph.Components; // DeltaV
using Content.Server.Polymorph.Systems; // DeltaV
using Content.Shared.Damage.Components;
// Shitmed Change
using System.Linq;
using Content.Shared.Damage;
namespace Content.Server.Body.Systems;
public sealed class BodySystem : SharedBodySystem
@ -31,7 +26,6 @@ public sealed class BodySystem : SharedBodySystem
[Dependency] private readonly GhostSystem _ghostSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; // Shitmed Change
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly PolymorphSystem _polymorph = default!;
@ -86,7 +80,7 @@ public sealed class BodySystem : SharedBodySystem
}
}
public override void RemovePart( // DeltaV - Made public
protected override void RemovePart(
Entity<BodyComponent?> bodyEnt,
Entity<BodyPartComponent> partEnt,
string slotId)
@ -103,31 +97,5 @@ public sealed class BodySystem : SharedBodySystem
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
_humanoidSystem.SetLayersVisibility((bodyEnt, humanoid), layers, visible: false);
_appearance.SetData(bodyEnt, layer, true); // Shitmed Change
}
// Shitmed Change Start
protected override void ApplyPartMarkings(EntityUid target, BodyPartAppearanceComponent component)
{
return;
}
protected override void RemoveBodyMarkings(EntityUid target, BodyPartAppearanceComponent partAppearance, HumanoidAppearanceComponent bodyAppearance)
{
foreach (var (visualLayer, markingList) in partAppearance.Markings)
foreach (var marking in markingList)
_humanoidSystem.RemoveMarking(target, marking.MarkingId, sync: false, humanoid: bodyAppearance);
Dirty(target, bodyAppearance);
}
protected override void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartComponent> partEnt)
{
var bleeding = partEnt.Comp.SeverBleeding;
if (partEnt.Comp.IsVital)
bleeding *= 2f;
TryComp<BloodstreamComponent>(bodyEnt, out var bloodstream);
_bloodstream.TryModifyBleedAmount((bodyEnt, bloodstream), bleeding);
}
// Shitmed Change End
}

View File

@ -1,8 +1,6 @@
using Content.Server.Administration.Logs;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Shared._Shitmed.Body.Components; // Shitmed Change
using Content.Shared._Shitmed.Body.Organ; // Shitmed Change
using Content.Server.Chat.Systems;
using Content.Shared._DV.Body.Components; // DeltaV - Addition of CPR
using Content.Shared.Body.Systems;
@ -29,7 +27,6 @@ using Robust.Shared.Timing;
using Content.Shared._DV.CosmicCult.Components; // DeltaV
using Content.Shared._Goobstation.Body.Components; // Goobstation
namespace Content.Server.Body.Systems;
[UsedImplicitly]
@ -84,7 +81,7 @@ public sealed class RespiratorSystem : EntitySystem
respirator.NextUpdate += respirator.AdjustedUpdateInterval;
if (_mobState.IsDead(uid) || HasComp<BreathingImmunityComponent>(uid) || HasComp<SpecialBreathingImmunityComponent>(uid)) // Shitmed: BreathingImmunity Goob immunity too
if (_mobState.IsDead(uid) || HasComp<SpecialBreathingImmunityComponent>(uid)) // Shitmed: BreathingImmunity Goob immunity too
continue;
// Begin DeltaV Additions
@ -98,8 +95,7 @@ public sealed class RespiratorSystem : EntitySystem
UpdateSaturation(uid, multiplier * (float) respirator.UpdateInterval.TotalSeconds, respirator); // DeltaV: use multiplier instead of negating
if ((!_mobState.IsIncapacitated(uid)
|| TryComp<AffectedByCPRComponent>(uid, out var cprComp) && cprComp.IsActive) // DeltaV - Addition of CPR
&& !HasComp<DebrainedComponent>(uid)) // Shitmed Change - Cannot breathe in crit or when no brain.
|| TryComp<AffectedByCPRComponent>(uid, out var cprComp) && cprComp.IsActive)) // DeltaV - Addition of CPR.
{
switch (respirator.Status)
{
@ -386,8 +382,7 @@ public sealed class RespiratorSystem : EntitySystem
if (ent.Comp.SuffocationCycles == 2)
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating");
var suffocationDamage = HasComp<DebrainedComponent>(ent) ? ent.Comp.Damage * 4.5f : ent.Comp.Damage; // Shitmed Change - Increased damage for debrained
_damageableSys.ChangeDamage(ent.Owner, suffocationDamage, interruptsDoAfters: false, ignoreResistances: true); // Shitmed Change
_damageableSys.ChangeDamage(ent.Owner, ent.Comp.Damage, interruptsDoAfters: false, ignoreResistances: true); // Shitmed Change
if (ent.Comp.SuffocationCycles < ent.Comp.SuffocationCycleThreshold)
return;

View File

@ -12,6 +12,7 @@ using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.Mind;
using Content.Shared.Players; // DeltaV - OOC muting
using Content.Shared.Players.RateLimiting;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
@ -283,6 +284,11 @@ internal sealed partial class ChatManager : IChatManager
return;
}
// DeltaV - OOC muting START
if (player.ContentData() is { OocMuted: true })
return;
// DeltaV END
Color? colorOverride = null;
var wrappedMessage = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message)));
if (_adminManager.HasAdminFlag(player, AdminFlags.Admin))

View File

@ -92,6 +92,8 @@ public sealed class ChatSanitizationManager : IChatSanitizationManager
Entry("[':", "chatsan-tearfully-smiles"),
Entry("('=", "chatsan-tearfully-smiles"),
Entry("['=", "chatsan-tearfully-smiles"),
Entry("?", "chatsan-confused"), //DeltaV
Entry("…", "chatsan-sighs"), //DeltaV (note that ... doesn't work as that turns into a radio message)
];
[Dependency] private readonly IConfigurationManager _configurationManager = default!;

Some files were not shown because too many files have changed in this diff Show More