From 435b7d5cf89897476ee60995a14944c6ed8dd1a1 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Mon, 12 Jan 2026 03:47:47 +0100 Subject: [PATCH] Add the ability for station maps to track grids they are not on (#41248) * Initial commit * Accidentally included the nukie map changes * Fix the gridcheck * Addressing review * Review change * Review comments --- .../UI/StationMapBoundUserInterface.cs | 10 +++- .../GameTicking/Rules/NukeopsRuleSystem.cs | 19 +++++++ Content.Server/Pinpointer/StationMapSystem.cs | 53 +++++++++++++++++- .../Pinpointer/NukeopsStationMapComponent.cs | 10 ++++ .../Pinpointer/StationMapComponent.cs | 20 ++++++- .../Entities/Objects/Devices/station_map.yml | 34 +++++++++++ .../tablets.rsi/inhand-left-syndie.png | Bin 0 -> 15446 bytes .../tablets.rsi/inhand-right-syndie.png | Bin 0 -> 15527 bytes .../Objects/Devices/tablets.rsi/meta.json | 19 ++++++- .../Objects/Devices/tablets.rsi/syndie.png | Bin 0 -> 15127 bytes 10 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 Content.Shared/Pinpointer/NukeopsStationMapComponent.cs create mode 100644 Resources/Textures/Objects/Devices/tablets.rsi/inhand-left-syndie.png create mode 100644 Resources/Textures/Objects/Devices/tablets.rsi/inhand-right-syndie.png create mode 100644 Resources/Textures/Objects/Devices/tablets.rsi/syndie.png diff --git a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs index 3d1eb1723c..49b383a7d7 100644 --- a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs +++ b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs @@ -17,7 +17,11 @@ public sealed class StationMapBoundUserInterface : BoundUserInterface base.Open(); EntityUid? gridUid = null; - if (EntMan.TryGetComponent(Owner, out var xform)) + if (EntMan.TryGetComponent(Owner, out var comp) && comp.TargetGrid != null) + { + gridUid = comp.TargetGrid; + } + else if (EntMan.TryGetComponent(Owner, out var xform)) { gridUid = xform.GridUid; } @@ -30,8 +34,8 @@ public sealed class StationMapBoundUserInterface : BoundUserInterface { stationName = gridMetaData.EntityName; } - - if (EntMan.TryGetComponent(Owner, out var comp) && comp.ShowLocation) + + if (comp != null && comp.ShowLocation) _window.Set(stationName, gridUid, Owner); else _window.Set(stationName, gridUid, null); diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index c669ca5278..0dc906738f 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -86,6 +86,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem return; component.TargetStation = RobustRandom.Pick(eligible); + var ev = new NukeopsTargetStationSelectedEvent(uid, component.TargetStation); + RaiseLocalEvent(ref ev); } #region Event Handlers @@ -549,3 +551,20 @@ public sealed class NukeopsRuleSystem : GameRuleSystem return null; } } + +/// +/// Raised when a station has been assigned as a target for the NukeOps rule. +/// +[ByRefEvent] +public readonly struct NukeopsTargetStationSelectedEvent(EntityUid ruleEntity, EntityUid? targetStation) +{ + /// + /// The entity containing the NukeOps gamerule. + /// + public readonly EntityUid RuleEntity = ruleEntity; + + /// + /// The target station, if it exists. + /// + public readonly EntityUid? TargetStation = targetStation; +} diff --git a/Content.Server/Pinpointer/StationMapSystem.cs b/Content.Server/Pinpointer/StationMapSystem.cs index c8e5b22617..bf2d2e2817 100644 --- a/Content.Server/Pinpointer/StationMapSystem.cs +++ b/Content.Server/Pinpointer/StationMapSystem.cs @@ -1,7 +1,10 @@ +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; using Content.Shared.PowerCell; using Content.Shared.Pinpointer; +using Content.Shared.Station; using Robust.Server.GameObjects; -using Robust.Shared.Player; namespace Content.Server.Pinpointer; @@ -9,12 +12,18 @@ public sealed class StationMapSystem : EntitySystem { [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly PowerCellSystem _cell = default!; + [Dependency] private readonly SharedStationSystem _station = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnUserParentChanged); + SubscribeLocalEvent(OnNukeopsStationSelected); + Subs.BuiEvents(StationMapUiKey.Key, subs => { subs.Event(OnStationMapOpened); @@ -22,6 +31,33 @@ public sealed class StationMapSystem : EntitySystem }); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (!ent.Comp.InitializeWithStation) + return; + + // If we ever find a need to make more exceptions like this, just turn this into an event. + if (HasComp(ent)) + { + foreach (var rule in _gameTicker.GetActiveGameRules()) + { + if (TryComp(rule, out var nukeopsRule) && nukeopsRule.TargetStation != null) + { + ent.Comp.TargetGrid = _station.GetLargestGrid((nukeopsRule.TargetStation.Value, null)); + Dirty(ent); + return; + } + } + } + + var station = _station.GetStationInMap(_xform.GetMapId(ent.Owner)); + if (station != null) + { + ent.Comp.TargetGrid = _station.GetLargestGrid((station.Value, null)); + Dirty(ent); + } + } + private void OnStationMapClosed(EntityUid uid, StationMapComponent component, BoundUIClosedEvent args) { if (!Equals(args.UiKey, StationMapUiKey.Key)) @@ -43,4 +79,19 @@ public sealed class StationMapSystem : EntitySystem var comp = EnsureComp(args.Actor); comp.Map = uid; } + + private void OnNukeopsStationSelected(Entity ent, ref NukeopsTargetStationSelectedEvent args) + { + if (args.TargetStation == null) + return; + + if (!TryComp(ent, out var stationMap) || !TryComp(args.RuleEntity, out var ruleGrids)) + return; + + if (Transform(ent).MapID != ruleGrids.Map) + return; + + stationMap.TargetGrid = _station.GetLargestGrid((args.TargetStation.Value, null)); + Dirty(ent); + } } diff --git a/Content.Shared/Pinpointer/NukeopsStationMapComponent.cs b/Content.Shared/Pinpointer/NukeopsStationMapComponent.cs new file mode 100644 index 0000000000..2b57959f9e --- /dev/null +++ b/Content.Shared/Pinpointer/NukeopsStationMapComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Pinpointer; + +/// +/// Used to indicate that an entity with the StationMapComponent should be updated to target the TargetStation of NukeopsRuleComponent. +/// Uses the most recent active NukeopsRule when spawned, or the NukeopsRule which spawned the grid that the entity is on. +/// +[RegisterComponent] +public sealed partial class NukeopsStationMapComponent : Component +{ +} diff --git a/Content.Shared/Pinpointer/StationMapComponent.cs b/Content.Shared/Pinpointer/StationMapComponent.cs index 07cc99605e..bba46215d7 100644 --- a/Content.Shared/Pinpointer/StationMapComponent.cs +++ b/Content.Shared/Pinpointer/StationMapComponent.cs @@ -1,11 +1,27 @@ +using Robust.Shared.GameStates; + namespace Content.Shared.Pinpointer; -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class StationMapComponent : Component { /// /// Whether or not to show the user's location on the map. /// - [DataField] + [DataField, AutoNetworkedField] public bool ShowLocation = true; + + /// + /// If true, when this entity initializes it will target and remember the station grid of the map the entity is in. + /// If there is no station, the entity will target a random station in the current session. + /// + [DataField] + public bool InitializeWithStation = false; + + /// + /// The target grid that the map will display. + /// If null, it will display the user's current grid. + /// + [DataField, AutoNetworkedField] + public EntityUid? TargetGrid; } diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml index e440cec09a..8724a4060b 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml @@ -52,3 +52,37 @@ id: HandheldStationMapUnpowered parent: BaseHandheldStationMap suffix: Handheld, Always Powered + +- type: entity + parent: HandheldStationMap + id: HandheldStationMapStatic + suffix: Handheld, Works Off-Station + components: + - type: StationMap + initializeWithStation: true + +- type: entity + parent: HandheldStationMap + id: HandheldStationMapNukeops + name: target station map + suffix: Handheld, NukeOps + description: Displays a readout of the target station. + components: + - type: Sprite + sprite: Objects/Devices/tablets.rsi + layers: + - state: tablet + - state: syndie + shader: unshaded + - type: Item + sprite: Objects/Devices/tablets.rsi + inhandVisuals: + left: + - state: inhand-left-syndie + right: + - state: inhand-right-syndie + - type: StationMap + showLocation: false + initializeWithStation: true + - type: NukeopsStationMap + diff --git a/Resources/Textures/Objects/Devices/tablets.rsi/inhand-left-syndie.png b/Resources/Textures/Objects/Devices/tablets.rsi/inhand-left-syndie.png new file mode 100644 index 0000000000000000000000000000000000000000..2bff1528e7dd7fd72e8d101b1e7bcf5bdce08ed5 GIT binary patch literal 15446 zcmeI3e^gWF8OL9QDT4`2=xF^#j6c>Y$xQ+Qa!&{#QHX1h0$TL2YDjM2N|KxA-jIOu z<48R^aXYq7oT9Y#AnFk6sah4Q44tl9yCSY=i&IwJq>6~RP6S8Yy-7%3Lg;OG`sd9# z;XZla@AEwG{XFmcaR0f#%FIZc8ZxwGW~aWsA(U4`LQ{D6$yZF zF7x%XA4n<&z;7#_nwpteNKgb>NSJx5)Ks3?Lg;Y=27tY5=jFu0Y`=($1KsOiS$L(Z z?I&62hjZ@bM?Uv@^x@|Ng6IEwyK>5oT??lE^T3>_Q&dD=a7cOd%2|1Ptyc4M0d1jS zwl-DAl4UcerHAqD2QQo+%zH2r{@~=TdRyVn`WyXs$S)4%E=(iWY^tf>TYO~NAlh5I zD_DQ^y}?QLlUKIxsC(A9scR*&V9U|zwwAJuwWrLD-FL(Po`&9R{w%QW_RA-87lv<8 zwxCzKx0@nMHw&g36($ z$0yXfqtQKEtuy#5{MCZ8MSHsVi6ycxUKpAmeO;8mM0&ZpWL2D+GL;42&qV#9THl%efX2c$Maf`wO@R@{_%p>?)5{mGgTq;-nGO8tvLXD2d$AosHnXVxK@+7-iqbeF{C@!8CFB6KT@$s?o(L9MrEJZ{yNQ_7z zi9usUXsm?i`tTJ&v?aIb^k|lHsVg1*E0JGFQDziDY&M(F79%7q1&CNClOZArB9RDa zg@7zEQ5w6zM9y^v8I7aFNUa4oQ#fJbF>y8dgq2F<^O;2MkL$jSW_KbJ>0(DyMC=+f zA{L4eUrsu$+sACR7#vH}X%WnT88H(@(tcu}e&*40<8~jD(mg$4V!9@3wQuLq^%{*n zQ6o;mo@7pm8AGx#lCWB|*pg!UR_6M;dkWWkiZdn>Gc)1ZF*`ni8#Ci{ zb8H1?h2-?TqNx^4LlKr7f-oqYyUf#i=+49JD&C8);wBwoBVUpu-Y%Ycd!k`V4TUKr zB8gNW77Ij@9FYVS$xvzRB9RysiJ%}(rB@6!p~LkhK4OT)Xlx7|15$d&pwEv^LutNK z6j%$z(P>dVVKHhb1#Z+7V2IgNAV)ldkkY-Y&{V=eSm9 zqR)7~6*o`<+{7cZ(iX#UAR!dUv1L(}iKH|pEv8Z`=m8;)>(Dq%lrKw&$rr@xC1OE> zG&Vt?iI+(Q`ncFQy;K^nO_1pv)ljQp_i>?>gx1O&J6CAk_Y4iWj|;urg3~QiV}LZw z>c4LaA7RF&O20_cpE!rD5Ho$-$#jNts3s0V5_-y}v0y0$ z^q%-GO~V;=tebauIl}xKG2mkh;T#=XpV1vVzMgWXJEHVnpx32w)Icmp#*KM0JN3Ct zO$}CuK1SsDAY$g?@`dNUeOIyg!t>s~9@iBnoKi?!>AZ#^FQ$n*J4`nHyn!j`oAVy> zR%haN%#f?Z>C;OxdonK8<>Ni!({a^Aj?je5(OD?yslZW3=P3QmK|k&w?&qDcC3YW! zAK38R(GMIL4#y>dX|UQHE>=XafWrmTV6{11tcYL%hYO~`YIC?)5y1iu7fgfI=5Vnh zf(0Bdm6%j1paKSWKZ4MVJB3QuTf@!eY z94=Nwuz1`5DmcP$-2$K%cGj|AY^Gk;L__O;DxhC9Dv-Z51z$xt>gUc2#s DJljbz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/tablets.rsi/inhand-right-syndie.png b/Resources/Textures/Objects/Devices/tablets.rsi/inhand-right-syndie.png new file mode 100644 index 0000000000000000000000000000000000000000..89e389cdb2e5a88a83b7b213dbd441696a4faf4b GIT binary patch literal 15527 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/tablets.rsi/meta.json b/Resources/Textures/Objects/Devices/tablets.rsi/meta.json index 5c572d32e4..224ccf739c 100644 --- a/Resources/Textures/Objects/Devices/tablets.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/tablets.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from Baystation12 at https://github.com/Baystation12/Baystation12/tree/17e84546562b97d775cb26790a08e6d87e7f8077 and heavily modified by Flareguy for Space Station 14. Inhands by carousel", + "copyright": "Taken from Baystation12 at https://github.com/Baystation12/Baystation12/tree/17e84546562b97d775cb26790a08e6d87e7f8077 and heavily modified by Flareguy for Space Station 14. Inhands by carousel. Syndie versions modified from generic by SlamBamActionman", "size": { "x": 32, "y": 32 @@ -29,6 +29,23 @@ { "name": "inhand-right", "directions": 4 + }, + { + "name": "syndie", + "delays": [ + [ + 0.3, + 0.3 + ] + ] + }, + { + "name": "inhand-left-syndie", + "directions": 4 + }, + { + "name": "inhand-right-syndie", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Devices/tablets.rsi/syndie.png b/Resources/Textures/Objects/Devices/tablets.rsi/syndie.png new file mode 100644 index 0000000000000000000000000000000000000000..1c288c8bf65e6b752c19230d562002ca6c212d3d GIT binary patch literal 15127 zcmeI3U5MON6vuBBw63lde5e#3!jwWwo5{y~WG0=Ny4`8rwa&V3N4hPAZ6>+18+Im% zNoHquYfH5cDuRMvv}GyUH(Tt73W^m4p;l0^s1Kr``=V4)R76Yhq246(k-al}S!u;^uBN9E$DOqv$fOc>o#j>w*$cK zcYE&@;Mn{<0HnXwi{tKi;Xzq7t5HQWOEB7~T4*)^nJo=VQKzBHl;EUpWFxN=4`lgZltKrO{-}wvL)A-X~-x*6}k%3s8$R|Ze$~VTp9iLhPepi zPjRQSkpV9uGhP^F22C3>iD;5l`J}{1>8Oy9q`0(+5qTlO@i8vOvtmq+^Kx8dnnxtp zhkj*jtt^k_w>Q%vD;t?|T}$S;dc7X4$D*b^$qDInn&U-I6j?Nbb!H7$X|RT~HVD#+ zlZTFK>z1pV2IIw5N@mT?Mj~FK_R+kqs?|=^XVb zXpjmj2<6XOI*4PI-MV7Kfl0I{mQ(9N)WtG9e1`M7e?|Wf7Q#_xu5THZS4Y>k42ycw z|F;aCU93(i#w65oTx$cj29~#S!Fn#PtX4zluEdP@cO;`KMmtLJs|R(DuCY$$VE(vQ zjg^`|UlMtAaDw;Ql!o27bT?TVcH`2H@)1LKb7C_aHXKrU|CvEU--&MTU@n@TLwA^5`xdjS zJlkFDM*iqr(T$9X#iB_tAY_x`BJ;t#6c+}BY*JigKA4x{!hn!Xii^w#^HN+G5VA>e zk@;X=iVFimHYqMLAIwW}VL-?x#YN_Wc_}Uo2-&2#$b2v_#f1SOn-mwB59Xz~Fd$@; z;v)0Gyc8D(gltk=WImXe;=+KCO^S=m2lG-~7!a~aagq68UWy9?LN+NbG9S!KabZBn zCdEbOgLx?~3<%kzxX64kFU5rcA)6EznGfcrxG*4Olj0)t!Mqd~283)J zkWGq<%m?#QTo@3tNyOFHd1wxzd0RPh@7a;r1FIg}aS0yWD1Eq}Ik;}`lM9!RU3%}X zqv=;pp6=iD_uh+M$l&;W;=-BtuRQSD<(q@oxj$Ec7bjxKx$qbG1H5(Sz{WGjZ~e}j OJiKire|qb_C;kC0PiYkZ literal 0 HcmV?d00001 -- 2.52.0