mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 23:57:29 +00:00
Compare commits
279
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4702b34eb5 | ||
|
|
4d28fabc9d | ||
|
|
474a6d358b | ||
|
|
5e6b27a860 | ||
|
|
008b84902a | ||
|
|
52d887faff | ||
|
|
097d8282c8 | ||
|
|
8919a88ea0 | ||
|
|
2dcf965e41 | ||
|
|
c776571860 | ||
|
|
78dd74e5c1 | ||
|
|
b3d15cdfd4 | ||
|
|
92883eb3ff | ||
|
|
53773326df | ||
|
|
2281cdbf8d | ||
|
|
ff20b12532 | ||
|
|
8e62f86239 | ||
|
|
e2e5c40f83 | ||
|
|
d0c3cff669 | ||
|
|
ed1fa64a6e | ||
|
|
cb245477b8 | ||
|
|
0fdfef7175 | ||
|
|
646d538c4a | ||
|
|
6ebcf78d08 | ||
|
|
2b7e3c8058 | ||
|
|
4736a8db6f | ||
|
|
6057388bae | ||
|
|
50a651beea | ||
|
|
7e1308dd12 | ||
|
|
338ff271c7 | ||
|
|
fddd9079d3 | ||
|
|
bedc17ae73 | ||
|
|
ec80e99e26 | ||
|
|
4ceb567439 | ||
|
|
6a9984475c | ||
|
|
030eb95c63 | ||
|
|
00abed028d | ||
|
|
0f3d319a6d | ||
|
|
a9ddc102e7 | ||
|
|
a51896b08d | ||
|
|
356106342f | ||
|
|
a3b450ac07 | ||
|
|
4ddb5901b0 | ||
|
|
972d852bf4 | ||
|
|
ef0d317d12 | ||
|
|
003e08b583 | ||
|
|
74739709c4 | ||
|
|
61840fd5ab | ||
|
|
90f7aa7999 | ||
|
|
2260af7ce1 | ||
|
|
d6230dfaa9 | ||
|
|
1613a75eba | ||
|
|
206b7d0ddb | ||
|
|
005165b6f8 | ||
|
|
bc96c7baec | ||
|
|
cd765f34a7 | ||
|
|
a8c721fbb1 | ||
|
|
a97a42701e | ||
|
|
e8287ada86 | ||
|
|
69f62ca86b | ||
|
|
44f7d66b09 | ||
|
|
3cec2d61df | ||
|
|
a61a09b3d9 | ||
|
|
0eca06ccdf | ||
|
|
9db24b3f4c | ||
|
|
7292a14490 | ||
|
|
5425432338 | ||
|
|
d8f38bcc93 | ||
|
|
ab354dba0d | ||
|
|
10b3e4693d | ||
|
|
bfb6387fcd | ||
|
|
0e9ca82619 | ||
|
|
6705bbc572 | ||
|
|
6ab48778fb | ||
|
|
8a2b4daccb | ||
|
|
c942b89bd3 | ||
|
|
132b3d829d | ||
|
|
00ff4d5d9b | ||
|
|
d2e65e8b5c | ||
|
|
01812253c5 | ||
|
|
c6971fee88 | ||
|
|
4b86de2260 | ||
|
|
2999917b39 | ||
|
|
6e052b3578 | ||
|
|
65eaea0d71 | ||
|
|
0a9ed0bf8d | ||
|
|
3e0dc9516a | ||
|
|
61e7473957 | ||
|
|
438e83ec1e | ||
|
|
560bc4e22a | ||
|
|
a96d87cafe | ||
|
|
cdc66f87f5 | ||
|
|
850af4cf20 | ||
|
|
024924421b | ||
|
|
81e7c02e0e | ||
|
|
a5c651d372 | ||
|
|
1986fbf192 | ||
|
|
d01a50c519 | ||
|
|
6d339c9939 | ||
|
|
8d4401ccaf | ||
|
|
61d342a2a1 | ||
|
|
455e232a38 | ||
|
|
75089bc88e | ||
|
|
4cc00970ee | ||
|
|
c68cdeb673 | ||
|
|
7bca51bb57 | ||
|
|
7203e3e216 | ||
|
|
e9b74fbef4 | ||
|
|
62e6a412dc | ||
|
|
86301996ac | ||
|
|
e5d04bfd65 | ||
|
|
b9b690bf8d | ||
|
|
b7835b8267 | ||
|
|
3258e15c57 | ||
|
|
6fa8f1aba3 | ||
|
|
3a39317ebf | ||
|
|
b85dd9f03d | ||
|
|
3864a87103 | ||
|
|
e87ebe2fbf | ||
|
|
170ee4a0b2 | ||
|
|
ceca76ccdd | ||
|
|
3f57971501 | ||
|
|
65f7a56f84 | ||
|
|
0a2d83c5b4 | ||
|
|
c25aab9214 | ||
|
|
ad24fbbd9d | ||
|
|
e9ac10995b | ||
|
|
378dacbb88 | ||
|
|
948318f71f | ||
|
|
9f5b871312 | ||
|
|
81cbf9d218 | ||
|
|
b817cc35c9 | ||
|
|
5712c39330 | ||
|
|
211277ecce | ||
|
|
4aced8451c | ||
|
|
edfc9a9054 | ||
|
|
42589dc185 | ||
|
|
be5093fe7f | ||
|
|
9122a1ba4c | ||
|
|
676145bd21 | ||
|
|
82bde19134 | ||
|
|
71083b180b | ||
|
|
8e50900df9 | ||
|
|
8acf3e71ac | ||
|
|
32a7139fa1 | ||
|
|
fe7ae2a838 | ||
|
|
12d55978f5 | ||
|
|
86a5d53688 | ||
|
|
a2ccf1b106 | ||
|
|
c33826f91a | ||
|
|
57a92315a2 | ||
|
|
70b5fbfbac | ||
|
|
f92df73e54 | ||
|
|
454b11227a | ||
|
|
710a7b74a2 | ||
|
|
c6c4655d83 | ||
|
|
d5d31aac6a | ||
|
|
55ad1cb4e1 | ||
|
|
90e95b657d | ||
|
|
6669c0b705 | ||
|
|
0ed24648aa | ||
|
|
336b1d07d0 | ||
|
|
880d388624 | ||
|
|
d765649406 | ||
|
|
2939a1f6d8 | ||
|
|
48351bfa1c | ||
|
|
484428e533 | ||
|
|
0378bec386 | ||
|
|
84bc28f1ec | ||
|
|
f2882d2c1c | ||
|
|
9bbcf80999 | ||
|
|
41a3a64cd2 | ||
|
|
7b80369206 | ||
|
|
a35a5b5a1e | ||
|
|
02f1bf84b0 | ||
|
|
f9b8628062 | ||
|
|
86d2422ced | ||
|
|
548a9ffb71 | ||
|
|
3454ace8f5 | ||
|
|
cb62b27fcf | ||
|
|
3e33391980 | ||
|
|
be6a2be915 | ||
|
|
d33b22c1e9 | ||
|
|
47f6adf2a0 | ||
|
|
825267bbb3 | ||
|
|
78e30274b5 | ||
|
|
092efc3b38 | ||
|
|
a7096b143d | ||
|
|
1f8afb9ba6 | ||
|
|
fcab8ccb1e | ||
|
|
cd8cbcb2ea | ||
|
|
305de38eb5 | ||
|
|
72fd5372eb | ||
|
|
8ad049d88e | ||
|
|
afc2655c0a | ||
|
|
337b9366f5 | ||
|
|
8bc0ac3b38 | ||
|
|
a7af20fd67 | ||
|
|
665ad33c21 | ||
|
|
729eabae9d | ||
|
|
a14216de58 | ||
|
|
a494ada80b | ||
|
|
53b126f8c3 | ||
|
|
4c639a7342 | ||
|
|
78de793ff7 | ||
|
|
0c6054532a | ||
|
|
8a6d3d0e05 | ||
|
|
65d1f033af | ||
|
|
a2ea2e216a | ||
|
|
3cc1b824c6 | ||
|
|
83c1e1b1d1 | ||
|
|
94de555aa7 | ||
|
|
f93fb70d1c | ||
|
|
c1540d2aaa | ||
|
|
8c06d97a9b | ||
|
|
d52b986b63 | ||
|
|
b74a9884cb | ||
|
|
1cd82ef301 | ||
|
|
eadd0f7093 | ||
|
|
1b643a38bd | ||
|
|
77b96fbf70 | ||
|
|
8c9fb0e082 | ||
|
|
df5f83be5f | ||
|
|
d2aa5594f1 | ||
|
|
b499f0e1b0 | ||
|
|
97771cc7b9 | ||
|
|
69f0088bd8 | ||
|
|
323c610f15 | ||
|
|
47937aa146 | ||
|
|
3c2f992612 | ||
|
|
f519f164f8 | ||
|
|
33d47e29ef | ||
|
|
3267b4c2a8 | ||
|
|
6fda83f4c0 | ||
|
|
9146c59ecf | ||
|
|
3a05ef885c | ||
|
|
115c06ba8b | ||
|
|
da885b1b1d | ||
|
|
4548e356e5 | ||
|
|
e334477c2b | ||
|
|
d5cc6e2136 | ||
|
|
b0b8b3ad05 | ||
|
|
25e8b301cb | ||
|
|
b417bd327c | ||
|
|
9ca9777ce5 | ||
|
|
a6b1e12d3d | ||
|
|
641335f316 | ||
|
|
e80bead714 | ||
|
|
245f9f5713 | ||
|
|
6c13eee8b9 | ||
|
|
d62f7cef10 | ||
|
|
b465cfb3fd | ||
|
|
ea3c31b79d | ||
|
|
52ca24747a | ||
|
|
7e2d8cd8c5 | ||
|
|
90ae0b394e | ||
|
|
f25854eeec | ||
|
|
5392d18fe3 | ||
|
|
185b00e09b | ||
|
|
3b61986802 | ||
|
|
1adf093ab9 | ||
|
|
03814e2bff | ||
|
|
a0a51842d5 | ||
|
|
c55e2acd10 | ||
|
|
00ceed51dc | ||
|
|
6e6123731d | ||
|
|
02b540ca34 | ||
|
|
513b89feb4 | ||
|
|
7ebc5caa6e | ||
|
|
f30789e404 | ||
|
|
8a5980902d | ||
|
|
3176645b53 | ||
|
|
8930ac52c7 | ||
|
|
fccd3c793f | ||
|
|
a237663f8f | ||
|
|
f34e1d69f0 | ||
|
|
8ce3c188b9 | ||
|
|
c3c6a6abd9 | ||
|
|
6f0ad0c181 |
+1
-1
@@ -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
|
||||
|
||||
@@ -312,3 +312,6 @@ Resources/MapImages
|
||||
|
||||
# Changelog package lock
|
||||
package-lock.json
|
||||
|
||||
# Discord Bot Config
|
||||
link-config.json
|
||||
@@ -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#Captain’s_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}}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Client.Changelog;
|
||||
using Content.Client._DV.AccountLinking; // DeltaV
|
||||
using Content.Client.UserInterface.Systems.EscapeMenu;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared.CCVar;
|
||||
@@ -48,6 +49,18 @@ namespace Content.Client.Info
|
||||
changelogButton.OnPressed += args => UserInterfaceManager.GetUIController<ChangelogUIController>().ToggleWindow();
|
||||
buttons.AddChild(changelogButton);
|
||||
|
||||
// BEGIN DeltaV - Discord / Patreon Account Linking
|
||||
var linkAccount = UserInterfaceManager.GetUIController<LinkAccountUIController>();
|
||||
var linkAccountButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("ui-link-discord-account"),
|
||||
};
|
||||
linkAccountButton.OnPressed += _ => linkAccount.ToggleWindow();
|
||||
buttons.AddChild(linkAccountButton);
|
||||
|
||||
AddInfoButton("ui-patreon", CCVars.InfoLinksPatreon);
|
||||
// END DeltaV
|
||||
|
||||
void AddInfoButton(string loc, CVarDef<string> cVar)
|
||||
{
|
||||
var button = new Button { Text = Loc.GetString(loc) };
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client._DV.AccountLinking; // DeltaV
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Changelog;
|
||||
using Content.Client.Chat.Managers;
|
||||
@@ -62,6 +63,7 @@ namespace Content.Client.IoC
|
||||
collection.Register<SharedPlayerRateLimitManager, PlayerRateLimitManager>();
|
||||
collection.Register<TitleWindowManager>();
|
||||
collection.Register<ClientsidePlaytimeTrackingManager>();
|
||||
collection.Register<LinkAccountManager>(); // DeltaV - Discord Linking
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client._DV.AccountLinking; // DeltaV
|
||||
using Content.Client.Audio;
|
||||
using Content.Client.GameTicking.Managers;
|
||||
using Content.Client.LateJoin;
|
||||
@@ -29,6 +30,7 @@ namespace Content.Client.Lobby
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IVoteManager _voteManager = default!;
|
||||
[Dependency] private readonly ClientsidePlaytimeTrackingManager _playtimeTracking = default!;
|
||||
[Dependency] private readonly LinkAccountManager _linkAccount = default!; // DeltaV - Discord Linking
|
||||
|
||||
private ClientGameTicker _gameTicker = default!;
|
||||
private ContentAudioSystem _contentAudioSystem = default!;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Client._DV.AccountLinking; // DeltaV
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
@@ -38,6 +39,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly JobRequirementsManager _requirements = default!;
|
||||
[Dependency] private readonly MarkingManager _markings = default!;
|
||||
[Dependency] private readonly LinkAccountManager _linkAccount = default!; // DeltaV - Discord Account Linking
|
||||
[UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||
@@ -75,6 +77,8 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
_configurationManager.OnValueChanged(CCVars.GameRoleLoadoutTimers, _ => RefreshProfileEditor());
|
||||
|
||||
_configurationManager.OnValueChanged(CCVars.GameRoleWhitelist, _ => RefreshProfileEditor());
|
||||
|
||||
_linkAccount.Updated += RefreshProfileEditor; // DeltaV - Discord Account Linking
|
||||
}
|
||||
|
||||
private LobbyCharacterPreviewPanel? GetLobbyPreview()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'}" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Client._DV.AccountLinking; // DeltaV
|
||||
using Content.Client.UserInterface.Screens;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared._EE.CCVars; // EE - chat stack
|
||||
@@ -17,6 +18,7 @@ public sealed partial class MiscTab : Control
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly LinkAccountManager _linkAccount = default!; // DeltaV - Discord Account Linking
|
||||
|
||||
public MiscTab()
|
||||
{
|
||||
@@ -48,7 +50,7 @@ public sealed partial class MiscTab : Control
|
||||
|
||||
// Channel can be null in replays so.
|
||||
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
||||
ShowOocPatronColor.Visible = _playerManager.LocalSession?.Channel?.UserData.PatronTier is { };
|
||||
ShowOocPatronColor.Visible = _linkAccount.Tier != null; // DeltaV - Discord Account Linking
|
||||
|
||||
Control.AddOptionDropDown(CVars.InterfaceTheme, DropDownHudTheme, themeEntries);
|
||||
Control.AddOptionDropDown(CCVars.UILayout, DropDownHudLayout, layoutEntries);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -0,0 +1,48 @@
|
||||
using Content.Shared._DV.AccountLinking;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Client._DV.AccountLinking;
|
||||
|
||||
public sealed class LinkAccountManager : IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
|
||||
private readonly List<SharedPatron> _allPatrons = [];
|
||||
|
||||
public SharedPatronTier? Tier { get; private set; }
|
||||
public bool Linked { get; private set; }
|
||||
|
||||
public event Action<Guid>? CodeReceived;
|
||||
public event Action? Updated;
|
||||
|
||||
private void OnCode(LinkAccountCodeMsg message)
|
||||
{
|
||||
CodeReceived?.Invoke(message.Code);
|
||||
}
|
||||
|
||||
private void OnStatus(LinkAccountStatusMsg ev)
|
||||
{
|
||||
Tier = ev.Patron?.Tier;
|
||||
Linked = ev.Patron?.Linked ?? false;
|
||||
Updated?.Invoke();
|
||||
}
|
||||
|
||||
private void OnPatronList(PatronListMsg ev)
|
||||
{
|
||||
_allPatrons.Clear();
|
||||
_allPatrons.AddRange(ev.Patrons);
|
||||
}
|
||||
|
||||
public IReadOnlyList<SharedPatron> GetPatrons()
|
||||
{
|
||||
return _allPatrons;
|
||||
}
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_net.RegisterNetMessage<LinkAccountCodeMsg>(OnCode);
|
||||
_net.RegisterNetMessage<LinkAccountRequestMsg>();
|
||||
_net.RegisterNetMessage<LinkAccountStatusMsg>(OnStatus);
|
||||
_net.RegisterNetMessage<PatronListMsg>(OnPatronList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared._DV.AccountLinking;
|
||||
|
||||
namespace Content.Client._DV.AccountLinking;
|
||||
|
||||
public sealed class LinkAccountSystem : EntitySystem;
|
||||
@@ -0,0 +1,107 @@
|
||||
using Content.Client.Lobby.UI;
|
||||
using Content.Client.Message;
|
||||
using Content.Shared._DV.CCVars;
|
||||
using Content.Shared._DV.AccountLinking;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._DV.AccountLinking;
|
||||
|
||||
public sealed class LinkAccountUIController : UIController
|
||||
{
|
||||
[Dependency] private readonly IClipboardManager _clipboard = default!;
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly LinkAccountManager _linkAccount = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IUriOpener _uriOpener = default!;
|
||||
|
||||
private LinkAccountWindow? _window;
|
||||
private TimeSpan _disableUntil;
|
||||
|
||||
private Guid _code;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_linkAccount.CodeReceived += OnCode;
|
||||
_linkAccount.Updated += OnUpdated;
|
||||
}
|
||||
|
||||
private void OnCode(Guid code)
|
||||
{
|
||||
_code = code;
|
||||
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
_window.CopyButton.Disabled = false;
|
||||
}
|
||||
|
||||
private void OnUpdated()
|
||||
{
|
||||
if (UIManager.ActiveScreen is not LobbyGui gui)
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
public void ToggleWindow()
|
||||
{
|
||||
if (_window == null)
|
||||
{
|
||||
_window = new LinkAccountWindow();
|
||||
_window.OnClose += () => _window = null;
|
||||
_window.Label.SetMarkupPermissive($"{Loc.GetString("ui-link-discord-account-text")}");
|
||||
if (_linkAccount.Linked)
|
||||
_window.Label.SetMarkupPermissive($"{Loc.GetString("ui-link-discord-account-already-linked")}\n\n{Loc.GetString("ui-link-discord-account-text")}");
|
||||
|
||||
_window.CopyButton.OnPressed += _ =>
|
||||
{
|
||||
_clipboard.SetText(_code.ToString());
|
||||
_window.CopyButton.Text = Loc.GetString("ui-link-discord-account-copied");
|
||||
_window.CopyButton.Disabled = true;
|
||||
_disableUntil = _timing.RealTime.Add(TimeSpan.FromSeconds(3));
|
||||
};
|
||||
|
||||
var messageLink = _config.GetCVar(DCCVars.DiscordAccountLinkingMessageLink);
|
||||
if (string.IsNullOrEmpty(messageLink))
|
||||
{
|
||||
_window.LinkButton.Visible = false;
|
||||
_window.CopyButton.RemoveStyleClass("OpenRight");
|
||||
}
|
||||
else
|
||||
{
|
||||
_window.LinkButton.Visible = true;
|
||||
_window.LinkButton.OnPressed += _ => _uriOpener.OpenUri(messageLink);
|
||||
_window.CopyButton.AddStyleClass("OpenRight");
|
||||
}
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
if (_code == default)
|
||||
_window.CopyButton.Disabled = true;
|
||||
|
||||
_net.ClientSendMessage(new LinkAccountRequestMsg());
|
||||
return;
|
||||
}
|
||||
|
||||
_window.Close();
|
||||
_window = null;
|
||||
}
|
||||
|
||||
public override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
var time = _timing.RealTime;
|
||||
if (_disableUntil != default && time > _disableUntil)
|
||||
{
|
||||
_disableUntil = default;
|
||||
_window.CopyButton.Text = Loc.GetString("ui-link-discord-account-copy");
|
||||
_window.CopyButton.Disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<linkAccount:LinkAccountWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:linkAccount="clr-namespace:Content.Client._DV.AccountLinking"
|
||||
Title="{Loc 'ui-link-discord-account'}">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<RichTextLabel Name="Label" Access="Public" Margin="0 0 0 5" />
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center"
|
||||
HorizontalExpand="True">
|
||||
<Button Name="CopyButton" Access="Public" StyleClasses="OpenRight"
|
||||
Text="{Loc 'ui-link-discord-account-copy'}" HorizontalExpand="True" />
|
||||
<Button Name="LinkButton" Access="Public" StyleClasses="OpenLeft"
|
||||
Text="{Loc 'ui-link-discord-account-open-channel'}"
|
||||
HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</linkAccount:LinkAccountWindow>
|
||||
@@ -0,0 +1,15 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.AccountLinking;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LinkAccountWindow : DefaultWindow
|
||||
{
|
||||
public LinkAccountWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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'}" />
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared._Shitmed.Autodoc.Systems;
|
||||
|
||||
namespace Content.Client._Shitmed.Autodoc.Systems;
|
||||
|
||||
public sealed class AutodocSystem : SharedAutodocSystem;
|
||||
@@ -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 { }
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<controls:SurgeryStepButton
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client._Shitmed.Medical.Surgery">
|
||||
</controls:SurgeryStepButton>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
-57
@@ -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>
|
||||
-50
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-216
@@ -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>
|
||||
-58
@@ -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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using Content.DiscordBot.Modules;
|
||||
using Content.Server.Database;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.Interactions;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Content.DiscordBot;
|
||||
|
||||
public sealed class CommandHandler(DiscordSocketClient client, InteractionService interaction, ServerDbContext db, Config config)
|
||||
{
|
||||
private ImmutableDictionary<ulong, PatronTier>? _patronTiers;
|
||||
private ImmutableArray<PatronTier> _tierPriority;
|
||||
private Task? _refreshPatronsTask;
|
||||
|
||||
public int Running = 1;
|
||||
|
||||
public async Task InstallCommandsAsync()
|
||||
{
|
||||
var patronTiers = await db.PatronTiers.ToListAsync();
|
||||
_tierPriority = [..patronTiers.OrderBy(t => t.Priority)];
|
||||
_patronTiers = patronTiers.ToImmutableDictionary(t => t.DiscordRole, t => t);
|
||||
|
||||
client.Ready += ReadyAsync;
|
||||
client.SlashCommandExecuted += HandleCommandAsync;
|
||||
client.ButtonExecuted += HandleButtonAsync;
|
||||
client.ModalSubmitted += HandleModalAsync;
|
||||
client.GuildMemberUpdated += HandleGuildMemberUpdated;
|
||||
|
||||
// If you get a log that says '[ERROR]' here, you probably don't have the corrent intents
|
||||
await client.LoginAsync(TokenType.Bot, config.Token);
|
||||
await client.StartAsync();
|
||||
|
||||
interaction.AddModalInfo<LinkAccountModal>();
|
||||
|
||||
_refreshPatronsTask = Task.Run(async () => await RefreshPatrons());
|
||||
}
|
||||
|
||||
private async Task ReadyAsync()
|
||||
{
|
||||
await interaction.AddModulesAsync(Assembly.GetEntryAssembly(), null);
|
||||
await interaction.RegisterCommandsToGuildAsync(config.Guild);
|
||||
}
|
||||
|
||||
private async Task HandleGuildMemberUpdated(Cacheable<SocketGuildUser, ulong> old, SocketGuildUser user)
|
||||
{
|
||||
if (_patronTiers == null)
|
||||
return;
|
||||
|
||||
var rolesChanged = !old.HasValue || old.Value.Roles.Count != user.Roles.Count || !old.Value.Roles.SequenceEqual(user.Roles);
|
||||
if (!rolesChanged)
|
||||
return;
|
||||
|
||||
var wasPatron = old.HasValue || old.Value.Roles.Any(r => _patronTiers.ContainsKey(r.Id));
|
||||
var isPatron = user.Roles.Any(r => _patronTiers.ContainsKey(r.Id));
|
||||
if (wasPatron && !isPatron)
|
||||
{
|
||||
var linked = await db.DiscordLinkedAccounts
|
||||
.Include(l => l.Player)
|
||||
.ThenInclude(p => p.Patron)
|
||||
.ThenInclude(p => p!.Tier)
|
||||
.Where(l => l.Player.Patron != null)
|
||||
.FirstOrDefaultAsync(p => p.DiscordId == user.Id);
|
||||
|
||||
if (linked?.Player.Patron is { } patron)
|
||||
{
|
||||
db.Patrons.Remove(patron);
|
||||
await db.SaveChangesAsync();
|
||||
await Logger.Info($"Removed patron {user.Username}:{linked.DiscordId}:{linked.Player.LastSeenUserName} with tier {patron.Tier.Name}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPatron)
|
||||
{
|
||||
foreach (var tier in _tierPriority)
|
||||
{
|
||||
if (user.Roles.Any(r => r.Id == tier.DiscordRole))
|
||||
{
|
||||
var linked = await db.DiscordLinkedAccounts
|
||||
.Include(l => l.Player)
|
||||
.ThenInclude(p => p.Patron)
|
||||
.FirstOrDefaultAsync(p => p.DiscordId == user.Id);
|
||||
|
||||
if (linked?.Player is not { } player)
|
||||
return;
|
||||
|
||||
player.Patron ??= db.Patrons.Add(new Patron { PlayerId = player.UserId }).Entity;
|
||||
player.Patron.TierId = tier.Id;
|
||||
await db.SaveChangesAsync();
|
||||
await Logger.Info($"Updated patron {user.Username}:{linked.DiscordId}:{linked.Player.LastSeenUserName} with tier {tier.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleCommandAsync(SocketSlashCommand message)
|
||||
{
|
||||
|
||||
if (message.GuildId != config.Guild)
|
||||
return;
|
||||
|
||||
// Create a WebSocket-based command context based on the message
|
||||
var context = new SocketInteractionContext(client, message);
|
||||
|
||||
// Execute the command with the command context we just
|
||||
// created, along with the service provider for precondition checks.
|
||||
await interaction.ExecuteCommandAsync(context, null);
|
||||
}
|
||||
|
||||
private async Task HandleButtonAsync(SocketMessageComponent component)
|
||||
{
|
||||
switch (component.Data.CustomId)
|
||||
{
|
||||
case "link-ss14-account":
|
||||
await component.RespondWithModalAsync<LinkAccountModal>("link-ss14-account");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleModalAsync(SocketModal modal)
|
||||
{
|
||||
switch (modal.Data.CustomId)
|
||||
{
|
||||
case "link-ss14-account":
|
||||
if (modal.GuildId is not { } guildId)
|
||||
break;
|
||||
|
||||
var codeStr = modal.Data.Components.First(c => c.CustomId == "account_code").Value.Trim();
|
||||
if (string.IsNullOrWhiteSpace(codeStr))
|
||||
break;
|
||||
|
||||
await modal.DeferAsync(true);
|
||||
if (!Guid.TryParse(codeStr, out var code))
|
||||
{
|
||||
await modal.FollowupAsync($"{codeStr} isn't a valid code! Get one in-game from the lobby at the top left of the screen.", ephemeral: true);
|
||||
}
|
||||
|
||||
var author = modal.User;
|
||||
var authorId = author.Id;
|
||||
var discord = await db.DiscordAccounts
|
||||
.Include(d => d.LinkedAccount)
|
||||
.ThenInclude(l => l.Player)
|
||||
.ThenInclude(p => p.Patron)
|
||||
.FirstOrDefaultAsync(a => a.Id == authorId);
|
||||
var codes = await db.DiscordLinkingCodes
|
||||
.Include(l => l.Player)
|
||||
.ThenInclude(player => player.Patron)
|
||||
.FirstOrDefaultAsync(p => p.Code == code);
|
||||
|
||||
if (codes == null)
|
||||
{
|
||||
await modal.FollowupAsync($"No player found with code {codeStr}, join the game server and get another code before trying again, or ask for help in another channel.", ephemeral: true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (codes.CreationTime < DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)))
|
||||
{
|
||||
await modal.FollowupAsync($"Code {codeStr} were generated too long ago, join the game server and get another code before trying again.", ephemeral: true);
|
||||
}
|
||||
|
||||
if (discord?.LinkedAccount is { } linked)
|
||||
{
|
||||
if (linked.Player.Patron is { } patron)
|
||||
db.Patrons.Remove(patron);
|
||||
|
||||
linked.Player.Patron = null;
|
||||
db.DiscordLinkedAccounts.Remove(linked);
|
||||
}
|
||||
|
||||
discord ??= db.DiscordAccounts.Add(new DiscordAccount { Id = authorId }).Entity;
|
||||
discord.LinkedAccount = db.DiscordLinkedAccounts.Add(new DiscordLinkedAccount { Discord = discord }).Entity;
|
||||
discord.LinkedAccount.Player = codes.Player;
|
||||
|
||||
var roles = client.GetGuild(guildId).GetUser(authorId).Roles.Select(r => r.Id).ToArray();
|
||||
var tiers = await db.PatronTiers
|
||||
.Where(t => roles.Contains(t.DiscordRole))
|
||||
.ToListAsync();
|
||||
if (tiers.Count == 0)
|
||||
{
|
||||
discord.LinkedAccount.Player.Patron = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
tiers.Sort((a, b) => a.Priority.CompareTo(b.Priority));
|
||||
var tier = tiers[0];
|
||||
discord.LinkedAccount.Player.Patron = db.Patrons.Add(new Patron { Tier = tier }).Entity;
|
||||
discord.LinkedAccount.Player.Patron.Tier = tier;
|
||||
}
|
||||
|
||||
db.DiscordLinkedAccountLogs.Add(new DiscordLinkedAccountLogs
|
||||
{
|
||||
Discord = discord,
|
||||
Player = discord.LinkedAccount.Player,
|
||||
});
|
||||
|
||||
db.ChangeTracker.DetectChanges();
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var msg = $"Linked SS14 account with name {codes.Player.LastSeenUserName}";
|
||||
if (codes.Player.Patron != null)
|
||||
msg += $" and tier {codes.Player.Patron.Tier.Name}";
|
||||
|
||||
await modal.FollowupAsync(msg, ephemeral: true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshPatrons()
|
||||
{
|
||||
while (Interlocked.CompareExchange(ref Running, 1, 1) == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var patrons = await db.DiscordLinkedAccounts
|
||||
.Include(l => l.Player)
|
||||
.ThenInclude(p => p.Patron)
|
||||
.ThenInclude(p => p!.Tier)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var linked in patrons)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await client.Rest.GetGuildUserAsync(config.Guild, linked.DiscordId);
|
||||
if (user == null)
|
||||
{
|
||||
if (linked.Player.Patron != null)
|
||||
{
|
||||
linked.Player.Patron = null;
|
||||
await Logger.Info($"Removed patron {linked.DiscordId}:{linked.Player.LastSeenUserName}");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var isPatron = false;
|
||||
foreach (var tier in _tierPriority)
|
||||
{
|
||||
if (user.RoleIds.Contains(tier.DiscordRole))
|
||||
{
|
||||
isPatron = true;
|
||||
if (linked.Player.Patron?.Tier.DiscordRole == tier.DiscordRole)
|
||||
break;
|
||||
|
||||
linked.Player.Patron ??= db.Patrons.Add(new Patron { PlayerId = linked.PlayerId })
|
||||
.Entity;
|
||||
linked.Player.Patron.TierId = tier.Id;
|
||||
await Logger.Info($"Updated patron {user.Username}:{linked.DiscordId}:{linked.Player.LastSeenUserName} with tier {tier.Name}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPatron && linked.Player.Patron != null)
|
||||
{
|
||||
linked.Player.Patron = null;
|
||||
await Logger.Info($"Removed patron {user.Username}:{linked.DiscordId}:{linked.Player.LastSeenUserName}");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await Logger.Error($"Error updating patron with discord id {linked.DiscordId} and player id {linked.PlayerId}", e);
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await Task.Delay(60000); // 60 seconds
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await Logger.Error("Error refreshing patrons", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Content.DiscordBot;
|
||||
|
||||
public sealed class Config
|
||||
{
|
||||
public string Token { get; set; } = string.Empty;
|
||||
|
||||
public string DatabaseString { get; set; } = string.Empty;
|
||||
|
||||
public string DatabaseContext { get; set; } = "postgres";
|
||||
|
||||
/// <summary>
|
||||
/// Discord "guild" ID (which is just the server ID). Default is DeltaV's server ID.
|
||||
/// </summary>
|
||||
public ulong Guild { get; set; } = 968983104247185448UL;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<OutputPath>..\bin\Content.DiscordBot\</OutputPath>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<WarningsAsErrors>nullable</WarningsAsErrors>
|
||||
<RootNamespace>Content.DiscordBot</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net" Version="3.15.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Server.Database\Content.Server.Database.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
using Discord;
|
||||
|
||||
namespace Content.DiscordBot;
|
||||
|
||||
public static class Logger
|
||||
{
|
||||
public static Task Log(LogMessage msg)
|
||||
{
|
||||
return Console.Out.WriteLineAsync($"[{msg.Severity.ToString().ToUpper()}] {msg.Message}");
|
||||
}
|
||||
|
||||
public static Task Info(string msg)
|
||||
{
|
||||
return Console.Out.WriteLineAsync($"[INFO] {msg}");
|
||||
}
|
||||
|
||||
public static Task Error(string msg, Exception e)
|
||||
{
|
||||
return Console.Out.WriteLineAsync($"[ERROR] {msg}\n{e}");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user