Shuttle timers (#19471)
* sync * sync * no more squiggles.. * last build error * updated maps for testing * some issue with receiving arrivals setup * networkpayload refactor. TODO: accurate timings * timings accurate. TODO: backport old bugfix * all set? * cleaned up source. TODO: diff arrivals methods * cleaned component. TODO: escape polish, docs * first documentation pass * escape timers work * no more magic numbers * removed dead code leads * sync sync * Automatic changelog update * shuttle timer groundwork no more squiggles.. last build error updated maps for testing some issue with receiving arrivals setup networkpayload refactor. TODO: accurate timings timings accurate. TODO: backport old bugfix all set? cleaned up source. TODO: diff arrivals methods cleaned component. TODO: escape polish, docs first documentation pass escape timers work no more magic numbers * BBQ rib sandwich (#21180) * Fix missing toggle fullscreen loc string (#21264) * Cave Decoration pack (#21265) * add chromite chasm * add desert chasm * snow chasm * finish * fixes and tweaks (#21172) * Automatic changelog update * Fix ItemPlacer (#21160) This is going to lead to many entities being ticked unnecessarily and performance problems. * headrev spawn music (#21119) * headrev spawn music * :trollface: * skill issue * double skill issue * :trollface: * :trollface: * :trollface: * Automatic changelog update * Techfab resprite + department fab sprites (#21136) * Fix popup messages appearing when someone tries to open a door without a tool. (#21099) * The fixTM * typo fix * addressing review * Show "departed and moved on" for when a ghost role is taken (#21092) * fix ghost role not counting for "departed and moved on" * I don't think that bit was needed so away it goes * hopefully finish the upsream merge * Automatic changelog update * Implant whitelist/blacklisting (#20678) * add whitelist and blacklist to implant and implanter components * handle whitelist and blacklist in systems * move hardcoded whitelist/blacklist to base implanter + add admeme implanter * give implants sensible whitelists * cleaner CheckTarget and fix * remove unused imports * network lists --------- Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * Ion storm event (#20277) * ion storm event prototype + locale * add lawsets * use lawsets, make borgs ion storm targets * ion storm rule and ion storm target * lawset prototype * use lawsets * update silicon law system to use lawsets and support ion storm event * new toys * fix * more fix * fixy * ion storm admin logging * assigning laws makes borg provide its own laws, other stuff * 1h reoccurence * 50% chance * better call saul * emagLaws is required * add announcment audio * fixy * family friendly gaming * fixy * address reviews * fixy * more fixy and no erp * pro --------- Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * A return to foam (foam rework) (#20831) * Automatic changelog update * ERT Loadout overhaul + Real deathsquad mobs + ERT fixes (#21230) * "assist with medical efforts" * CentComm official description change * give cburn ert mask * Ert medic hardsuit uses blood-red medic values * description changes, they all used to use the blood-red description * ert engineer hardsuit uses cburn values, good for handling all possible engineering problems. * janitor hardsuit uses cburn values for extreme messes, otherwise we'd send the non eva variant. * spawn suffix changes * shorten suffix * drop armor from ert jumpsuits * drop armor from DS jumpsuit * add more armor to death squad to make up for removed armor in the uniform. * give sec gas masks armor, give syndicate gas masks armor. ERT gas mask uses syndicate mask values * add nanotrasen * removed duplicate * give centcom IDs their hud icon * replace all ert bulletproof armor with basic universal armor * replace all oxygen tanks with air tanks; species is random. * remove gun and meds from ert engineer kit * give ert engineer materials * remove weapons and meds from janitor ert * give ert janitor light replacer * remove ert sec pulse weapons, admins will assign loadout. Either the lecter or enforcer, probably. * Give ert sec the security pistol kit * typo * give eva ert sec pistol * give eva janitor ert gas mask * give jani purple gloves * medical gloves for medical ert * replicate security loadout to leader * quick ert lecter spawns for lazy admins * better suffixes to find them easier * add cburn to ertspawn * Replace "Spawn" with "role" * Add death squad. Give ert engineer gas analyzer. * death squad using wrong equipment * typo * missing ghost roles on lecter loadouts * add freedom implanter to deathsquad * deathsquad ghost role text * Operative sounds better * give Ds flashbang box (why isn't it entirely filled?) * fix typo. add energy shield to DS * fix typos * all centcomm roles are now mindshielded. These cannot be removed. * Rider didnt include some of the changes ? * give zipties instead of cuffs for mass arrests! * upgrade ERT survival boxes to extended capacity * give cburn extended oxygen too * Automatic changelog update * Restore Leviathan's 80 pop cap (#21281) * Un-revert IPlayerManager refactor (#21244) * Update engine to v172.0.0 (#21288) * Bandaid tests (#21292) * rename the rocks (#21275) * Make crystals noRot (#21279) IDK might be better. Ideally the anchoring would be offset 0-0 but this is the world we live in atm. * Fix nukies sound not played (#21268) * Play sound and sending greeting message works for nukies now!!!!! * oops * silly change * Automatic changelog update * Fix hijack objective (#21241) * Fix hijack * Max difficulty * Remove GridModifiedEvent (#21291) * Update submodule to 173.0.0 (#21296) * Fix namespace error (#21298) * Update yaml sequence option in editorconfig (#21297) * Fix namespace (#21299) * fix cburn bag issue, make new bag entity for them and filled bag entity (#21295) * Health analyzer UI improve (#17280) * Automatic changelog update * User accessible playtime (#21242) Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> * Automatic changelog update * Moves cloning comp & cloning event to shared (#21253) * Generalizes solution overflow & slightly increases space lube yield (#21094) * generalize SolutionSpikeOverflowEvent * let reactions overflow * spacelube: 3 -> 5 * restore TryMixAndOverflow threshold cap * Automatic changelog update * Move ActorComponent to shared (#21293) * Update engine to v174.0.0 (#21311) * Fix planet command help message (#21312) * Wearable bee plush (#20623) * add * fix * temporary change, needs fixing * mayb fix * actually fix FR * yes * Automatic changelog update * remove pulse rifle from ert medic (#21310) * Content audio (#20862) * Automatic changelog update * Update submodule to 175.0.0 (#21318) * Revert "Update submodule to 175.0.0 (#21318)" (#21319) * Atomic bomb add uranium (#21143) * fix: Incendiary bullets no longer deal cold, acid, or shock damage that ignores all armor. * Atomic bomb * Action bugfixes (#21321) * Disable OOC during round (#21323) * Fix PDA notifications when creating a news entry using the Mass-Media console. (#21320) * Automatic changelog update * Update belt.yml (#21317) changes the chief engineer's belt to remove the lv wires (they take up a lot of space and are easy to get anyways) in exchange for a holofan, a t-ray, and a gas analyzer (first time coding ever this might have to be edited) * New foam sprites (edge sprites) (#21308) * New foam sprites (icon smoothing) * changed to edge sprites for foam * fix * edges for metal foams * fix * Fix bola stam damage, bring back old construction requirements (#21340) * Automatic changelog update * Added thermal insulation to flannel jackets (#21273) * Automatic changelog update * Space Asshole Gear (#21243) * Add Space Asshole Coat * Add sledgehammer * Adjust sledgehammer damage values * Add copyright string to sledgehammer * Fix broken slot highlight in midnight theme. (#21331) * removed dead code leads removed redundant ensurecomps * textscreen documentation * remove redundant ensurecomps * Add hint for the examine trigger effect (#21166) * examine locale * examine trigger desc Automatic changelog update cornmeal is actually obtainable now (#21162) * do the thing * lets find out * Update Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> --------- Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Automatic changelog update Healing skeletons by pouring milk over them (and clean pie remains off their skulls) (#21231) * mvp done - skellies can heal by spillink regular milk on themselves and clean themselves off creaming with water * added other types of healing milk, also made a separate reaction to oat milk - it has almost no calcium in it * fixed indent error, made a dumb mistake Automatic changelog update Fix anomaly locators frantically beeping when entering detection range. (#21178) * reset beep timer when out of range * prevent deficit from impacting beep timing Automatic changelog update Remove "mk --> mmm, okay" and "u --> you" to chatsan anti slang (#21177) Automatic changelog update Adds AttemptEntity(Uns|S)tickEvent. (#20728) * try-stick * convert spider charge to attempt-stick-events Change ListContainer to send null when selected is removed from the data (#20595) fix feeding unremovable items (#21234) Automatic changelog update Power switchable refactor (#20419) Co-authored-by: deltanedas <@deltanedas:kde.org> simple space mobs cant be flashed (#20784) Co-authored-by: deltanedas <@deltanedas:kde.org> give roundstart borgs names (#20081) Co-authored-by: deltanedas <@deltanedas:kde.org> fix searching on vending machines (#21233) syndicate snack box (#21024) Co-authored-by: deltanedas <@deltanedas:kde.org> Fix DockingControl (#21238) Shadow Dimension visual pack (#21237) Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Potato battery update + potato AI (#21142) Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> [TEST MERGE] Slot-based Storage (#21212) Automatic changelog update Some mild item size balancing + fixes (#21250) Automatic changelog update Revert "Storage TEST MERGE" (#21258) Add missing changelog for storage refactor revert (#21259) * sync sync * Automatic changelog update shuttle timer groundwork no more squiggles.. last build error updated maps for testing some issue with receiving arrivals setup networkpayload refactor. TODO: accurate timings timings accurate. TODO: backport old bugfix all set? cleaned up source. TODO: diff arrivals methods cleaned component. TODO: escape polish, docs first documentation pass escape timers work no more magic numbers BBQ rib sandwich (#21180) Fix missing toggle fullscreen loc string (#21264) Cave Decoration pack (#21265) * add chromite chasm * add desert chasm * snow chasm * finish fixes and tweaks (#21172) Automatic changelog update Fix ItemPlacer (#21160) This is going to lead to many entities being ticked unnecessarily and performance problems. headrev spawn music (#21119) * headrev spawn music * :trollface: * skill issue * double skill issue * :trollface: * :trollface: * :trollface: Automatic changelog update Techfab resprite + department fab sprites (#21136) Fix popup messages appearing when someone tries to open a door without a tool. (#21099) * The fixTM * typo fix * addressing review Show "departed and moved on" for when a ghost role is taken (#21092) * fix ghost role not counting for "departed and moved on" * I don't think that bit was needed so away it goes * hopefully finish the upsream merge Automatic changelog update Implant whitelist/blacklisting (#20678) * add whitelist and blacklist to implant and implanter components * handle whitelist and blacklist in systems * move hardcoded whitelist/blacklist to base implanter + add admeme implanter * give implants sensible whitelists * cleaner CheckTarget and fix * remove unused imports * network lists --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Automatic changelog update Ion storm event (#20277) * ion storm event prototype + locale * add lawsets * use lawsets, make borgs ion storm targets * ion storm rule and ion storm target * lawset prototype * use lawsets * update silicon law system to use lawsets and support ion storm event * new toys * fix * more fix * fixy * ion storm admin logging * assigning laws makes borg provide its own laws, other stuff * 1h reoccurence * 50% chance * better call saul * emagLaws is required * add announcment audio * fixy * family friendly gaming * fixy * address reviews * fixy * more fixy and no erp * pro --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Automatic changelog update A return to foam (foam rework) (#20831) Automatic changelog update ERT Loadout overhaul + Real deathsquad mobs + ERT fixes (#21230) * "assist with medical efforts" * CentComm official description change * give cburn ert mask * Ert medic hardsuit uses blood-red medic values * description changes, they all used to use the blood-red description * ert engineer hardsuit uses cburn values, good for handling all possible engineering problems. * janitor hardsuit uses cburn values for extreme messes, otherwise we'd send the non eva variant. * spawn suffix changes * shorten suffix * drop armor from ert jumpsuits * drop armor from DS jumpsuit * add more armor to death squad to make up for removed armor in the uniform. * give sec gas masks armor, give syndicate gas masks armor. ERT gas mask uses syndicate mask values * add nanotrasen * removed duplicate * give centcom IDs their hud icon * replace all ert bulletproof armor with basic universal armor * replace all oxygen tanks with air tanks; species is random. * remove gun and meds from ert engineer kit * give ert engineer materials * remove weapons and meds from janitor ert * give ert janitor light replacer * remove ert sec pulse weapons, admins will assign loadout. Either the lecter or enforcer, probably. * Give ert sec the security pistol kit * typo * give eva ert sec pistol * give eva janitor ert gas mask * give jani purple gloves * medical gloves for medical ert * replicate security loadout to leader * quick ert lecter spawns for lazy admins * better suffixes to find them easier * add cburn to ertspawn * Replace "Spawn" with "role" * Add death squad. Give ert engineer gas analyzer. * death squad using wrong equipment * typo * missing ghost roles on lecter loadouts * add freedom implanter to deathsquad * deathsquad ghost role text * Operative sounds better * give Ds flashbang box (why isn't it entirely filled?) * fix typo. add energy shield to DS * fix typos * all centcomm roles are now mindshielded. These cannot be removed. * Rider didnt include some of the changes ? * give zipties instead of cuffs for mass arrests! * upgrade ERT survival boxes to extended capacity * give cburn extended oxygen too Automatic changelog update Restore Leviathan's 80 pop cap (#21281) Un-revert IPlayerManager refactor (#21244) Update engine to v172.0.0 (#21288) Bandaid tests (#21292) rename the rocks (#21275) Make crystals noRot (#21279) IDK might be better. Ideally the anchoring would be offset 0-0 but this is the world we live in atm. Fix nukies sound not played (#21268) * Play sound and sending greeting message works for nukies now!!!!! * oops * silly change Automatic changelog update Fix hijack objective (#21241) * Fix hijack * Max difficulty Remove GridModifiedEvent (#21291) Update submodule to 173.0.0 (#21296) Fix namespace error (#21298) Update yaml sequence option in editorconfig (#21297) Fix namespace (#21299) fix cburn bag issue, make new bag entity for them and filled bag entity (#21295) Health analyzer UI improve (#17280) Automatic changelog update User accessible playtime (#21242) Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Automatic changelog update Moves cloning comp & cloning event to shared (#21253) Generalizes solution overflow & slightly increases space lube yield (#21094) * generalize SolutionSpikeOverflowEvent * let reactions overflow * spacelube: 3 -> 5 * restore TryMixAndOverflow threshold cap Automatic changelog update Move ActorComponent to shared (#21293) Update engine to v174.0.0 (#21311) Fix planet command help message (#21312) Wearable bee plush (#20623) * add * fix * temporary change, needs fixing * mayb fix * actually fix FR * yes Automatic changelog update remove pulse rifle from ert medic (#21310) Content audio (#20862) Automatic changelog update Update submodule to 175.0.0 (#21318) Revert "Update submodule to 175.0.0 (#21318)" (#21319) Atomic bomb add uranium (#21143) * fix: Incendiary bullets no longer deal cold, acid, or shock damage that ignores all armor. * Atomic bomb Action bugfixes (#21321) Disable OOC during round (#21323) Fix PDA notifications when creating a news entry using the Mass-Media console. (#21320) Automatic changelog update Update belt.yml (#21317) changes the chief engineer's belt to remove the lv wires (they take up a lot of space and are easy to get anyways) in exchange for a holofan, a t-ray, and a gas analyzer (first time coding ever this might have to be edited) New foam sprites (edge sprites) (#21308) * New foam sprites (icon smoothing) * changed to edge sprites for foam * fix * edges for metal foams * fix Fix bola stam damage, bring back old construction requirements (#21340) Automatic changelog update Added thermal insulation to flannel jackets (#21273) Automatic changelog update Space Asshole Gear (#21243) * Add Space Asshole Coat * Add sledgehammer * Adjust sledgehammer damage values * Add copyright string to sledgehammer Fix broken slot highlight in midnight theme. (#21331) * removed old ensurecomps, stubs removed dead code leads removed redundant ensurecomps sync sync no more squiggles.. last build error updated maps for testing some issue with receiving arrivals setup networkpayload refactor. TODO: accurate timings timings accurate. TODO: backport old bugfix all set? cleaned up source. TODO: diff arrivals methods cleaned component. TODO: escape polish, docs first documentation pass escape timers work no more magic numbers removed dead code leads textscreen documentation remove redundant ensurecomps * sync * new shuttletimer sprite/offset * sync * sync * sync * sync * sync * builds * sync * sync * sync * offset adjustments * timer pattern implemented * arrivals timer system seems to work * sync * sync * sync * sync * sync * sync * sync * sync * sync * sync * implements shuttletimers * centcomm timers * set color to tg 1097fb * textscreen -> signalscreen, shuttletimer -> screen * move screens out of timer.yml * pruned unused properties/imports * license+copyright for screen.rsi * forgor change signaltimer event handler doc * roundstart arrivals ftl logic * block signaltimer screentext updates during timing * merged robust 185 * centcomm carpet uid collision PLEASE MERGE MY PR * remove arrivalssystem entitymanager dependency * refactored magic strings, added roundend getters * specific vgstation sprite commit licensing ---------
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.TextScreen;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -6,6 +7,20 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.TextScreen;
|
||||
|
||||
/// overview:
|
||||
/// Data is passed from server to client through <see cref="SharedAppearanceSystem.SetData"/>,
|
||||
/// calling <see cref="OnAppearanceChange"/>, which calls almost everything else.
|
||||
|
||||
/// Data for the (at most one) timer is stored in <see cref="TextScreenTimerComponent"/>.
|
||||
|
||||
/// All screens have <see cref="TextScreenVisualsComponent"/>, but:
|
||||
/// the update method only updates the timers, so the timercomp is added/removed by appearance changes/timing out.
|
||||
|
||||
/// Because the sprite component stores layers in a dict with no nesting, individual layers
|
||||
/// have to be mapped to unique ids e.g. {"textMapKey01" : <first row, second char layerstate>}
|
||||
/// in either the visuals or timer component.
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The TextScreenSystem draws text in the game world using 3x5 sprite states for each character.
|
||||
/// </summary>
|
||||
@@ -33,13 +48,20 @@ public sealed class TextScreenSystem : VisualizerSystem<TextScreenVisualsCompone
|
||||
/// <summary>
|
||||
/// A string prefix for all text layers.
|
||||
/// </summary>
|
||||
private const string TextScreenLayerMapKey = "textScreenLayerMapKey";
|
||||
private const string TextMapKey = "textMapKey";
|
||||
/// <summary>
|
||||
/// A string prefix for all timer layers.
|
||||
/// </summary>
|
||||
private const string TimerMapKey = "timerMapKey";
|
||||
private const string TextPath = "Effects/text.rsi";
|
||||
private const int CharWidth = 4;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TextScreenVisualsComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<TextScreenTimerComponent, ComponentInit>(OnTimerInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, TextScreenVisualsComponent component, ComponentInit args)
|
||||
@@ -47,199 +69,201 @@ public sealed class TextScreenSystem : VisualizerSystem<TextScreenVisualsCompone
|
||||
if (!TryComp(uid, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
ResetTextLength(uid, component, sprite);
|
||||
PrepareLayerStatesToDraw(uid, component, sprite);
|
||||
UpdateLayersToDraw(uid, component, sprite);
|
||||
// awkward to specify a textoffset of e.g. 0.1875 in the prototype
|
||||
component.TextOffset = Vector2.Multiply(TextScreenVisualsComponent.PixelSize, component.TextOffset);
|
||||
component.TimerOffset = Vector2.Multiply(TextScreenVisualsComponent.PixelSize, component.TimerOffset);
|
||||
|
||||
ResetText(uid, component, sprite);
|
||||
BuildTextLayers(uid, component, sprite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all TextScreenComponent sprite layers, through removing them and then creating new ones.
|
||||
/// Instantiates <see cref="SpriteComponent.Layers"/> with {<see cref="TimerMapKey"/> + int : <see cref="DefaultState"/>} pairs.
|
||||
/// </summary>
|
||||
public void ResetTextLength(EntityUid uid, TextScreenVisualsComponent component, SpriteComponent? sprite = null)
|
||||
private void OnTimerInit(EntityUid uid, TextScreenTimerComponent timer, ComponentInit args)
|
||||
{
|
||||
if (!Resolve(uid, ref sprite))
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite) || !TryComp<TextScreenVisualsComponent>(uid, out var screen))
|
||||
return;
|
||||
|
||||
foreach (var (key, _) in component.LayerStatesToDraw)
|
||||
for (var i = 0; i < screen.RowLength; i++)
|
||||
{
|
||||
sprite.RemoveLayer(key);
|
||||
sprite.LayerMapReserveBlank(TimerMapKey + i);
|
||||
timer.LayerStatesToDraw.Add(TimerMapKey + i, null);
|
||||
sprite.LayerSetRSI(TimerMapKey + i, new ResPath(TextPath));
|
||||
sprite.LayerSetColor(TimerMapKey + i, screen.Color);
|
||||
sprite.LayerSetState(TimerMapKey + i, DefaultState);
|
||||
}
|
||||
|
||||
component.LayerStatesToDraw.Clear();
|
||||
|
||||
var length = component.TextLength;
|
||||
component.TextLength = 0;
|
||||
SetTextLength(uid, component, length, sprite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="TextScreenVisualsComponent.TextLength"/>, adding or removing sprite layers if necessary.
|
||||
/// Called by <see cref="SharedAppearanceSystem.SetData"/> to handle text updates,
|
||||
/// and spawn a <see cref="TextScreenTimerComponent"/> if necessary
|
||||
/// </summary>
|
||||
public void SetTextLength(EntityUid uid, TextScreenVisualsComponent component, int newLength, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (newLength == component.TextLength)
|
||||
return;
|
||||
|
||||
if (!Resolve(uid, ref sprite))
|
||||
return;
|
||||
|
||||
if (newLength > component.TextLength)
|
||||
{
|
||||
for (var i = component.TextLength; i < newLength; i++)
|
||||
{
|
||||
sprite.LayerMapReserveBlank(TextScreenLayerMapKey + i);
|
||||
component.LayerStatesToDraw.Add(TextScreenLayerMapKey + i, null);
|
||||
sprite.LayerSetRSI(TextScreenLayerMapKey + i, new ResPath("Effects/text.rsi"));
|
||||
sprite.LayerSetColor(TextScreenLayerMapKey + i, component.Color);
|
||||
sprite.LayerSetState(TextScreenLayerMapKey + i, DefaultState);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = component.TextLength; i > newLength; i--)
|
||||
{
|
||||
sprite.LayerMapGet(TextScreenLayerMapKey + (i - 1));
|
||||
component.LayerStatesToDraw.Remove(TextScreenLayerMapKey + (i - 1));
|
||||
sprite.RemoveLayer(TextScreenLayerMapKey + (i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateOffsets(uid, component, sprite);
|
||||
|
||||
component.TextLength = newLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the layers offsets based on the text length, so it is drawn correctly.
|
||||
/// </summary>
|
||||
public void UpdateOffsets(EntityUid uid, TextScreenVisualsComponent component, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (!Resolve(uid, ref sprite))
|
||||
return;
|
||||
|
||||
for (var i = 0; i < component.LayerStatesToDraw.Count; i++)
|
||||
{
|
||||
var offset = i - (component.LayerStatesToDraw.Count - 1) / 2.0f;
|
||||
sprite.LayerSetOffset(TextScreenLayerMapKey + i, new Vector2(offset * TextScreenVisualsComponent.PixelSize * 4f, 0.0f) + component.TextOffset);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, TextScreenVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
UpdateAppearance(uid, component, args.Component, args.Sprite);
|
||||
}
|
||||
|
||||
public void UpdateAppearance(EntityUid uid, TextScreenVisualsComponent component, AppearanceComponent? appearance = null, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (!Resolve(uid, ref appearance, ref sprite))
|
||||
if (!Resolve(uid, ref args.Sprite))
|
||||
return;
|
||||
|
||||
if (AppearanceSystem.TryGetData(uid, TextScreenVisuals.On, out bool on, appearance))
|
||||
{
|
||||
component.Activated = on;
|
||||
UpdateVisibility(uid, component, sprite);
|
||||
}
|
||||
|
||||
if (AppearanceSystem.TryGetData(uid, TextScreenVisuals.Mode, out TextScreenMode mode, appearance))
|
||||
{
|
||||
component.CurrentMode = mode;
|
||||
if (component.CurrentMode == TextScreenMode.Timer)
|
||||
EnsureComp<TextScreenTimerComponent>(uid);
|
||||
else
|
||||
RemComp<TextScreenTimerComponent>(uid);
|
||||
|
||||
UpdateText(component);
|
||||
}
|
||||
var appearance = args.Component;
|
||||
|
||||
if (AppearanceSystem.TryGetData(uid, TextScreenVisuals.TargetTime, out TimeSpan time, appearance))
|
||||
{
|
||||
component.TargetTime = time;
|
||||
}
|
||||
|
||||
if (AppearanceSystem.TryGetData(uid, TextScreenVisuals.ScreenText, out string text, appearance))
|
||||
if (time > _gameTiming.CurTime)
|
||||
{
|
||||
component.Text = text;
|
||||
var timer = EnsureComp<TextScreenTimerComponent>(uid);
|
||||
timer.Target = time;
|
||||
BuildTimerLayers(uid, timer, component);
|
||||
DrawLayers(uid, timer.LayerStatesToDraw);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnTimerFinish(uid, component);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateText(component);
|
||||
PrepareLayerStatesToDraw(uid, component, sprite);
|
||||
UpdateLayersToDraw(uid, component, sprite);
|
||||
if (AppearanceSystem.TryGetData(uid, TextScreenVisuals.ScreenText, out string?[] text, appearance))
|
||||
{
|
||||
component.TextToDraw = text;
|
||||
ResetText(uid, component);
|
||||
BuildTextLayers(uid, component, args.Sprite);
|
||||
DrawLayers(uid, component.LayerStatesToDraw);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If currently in <see cref="TextScreenMode.Text"/> mode: <br/>
|
||||
/// Sets <see cref="TextScreenVisualsComponent.TextToDraw"/> to the value of <see cref="TextScreenVisualsComponent.Text"/>
|
||||
/// Removes the timer component, clears the sprite layer dict,
|
||||
/// and draws <see cref="TextScreenVisualsComponent.Text"/>
|
||||
/// </summary>
|
||||
public static void UpdateText(TextScreenVisualsComponent component)
|
||||
private void OnTimerFinish(EntityUid uid, TextScreenVisualsComponent screen)
|
||||
{
|
||||
if (component.CurrentMode == TextScreenMode.Text)
|
||||
component.TextToDraw = component.Text;
|
||||
screen.TextToDraw = screen.Text;
|
||||
|
||||
if (!TryComp<TextScreenTimerComponent>(uid, out var timer) || !TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
foreach (var key in timer.LayerStatesToDraw.Keys)
|
||||
sprite.RemoveLayer(key);
|
||||
|
||||
RemComp<TextScreenTimerComponent>(uid);
|
||||
|
||||
ResetText(uid, screen);
|
||||
BuildTextLayers(uid, screen, sprite);
|
||||
DrawLayers(uid, screen.LayerStatesToDraw);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets visibility of text to <see cref="TextScreenVisualsComponent.Activated"/>.
|
||||
/// Clears <see cref="TextScreenVisualsComponent.LayerStatesToDraw"/>, and instantiates new blank defaults.
|
||||
/// </summary>
|
||||
public void UpdateVisibility(EntityUid uid, TextScreenVisualsComponent component, SpriteComponent? sprite = null)
|
||||
public void ResetText(EntityUid uid, TextScreenVisualsComponent component, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (!Resolve(uid, ref sprite))
|
||||
return;
|
||||
|
||||
foreach (var (key, _) in component.LayerStatesToDraw)
|
||||
foreach (var key in component.LayerStatesToDraw.Keys)
|
||||
sprite.RemoveLayer(key);
|
||||
|
||||
component.LayerStatesToDraw.Clear();
|
||||
|
||||
for (var row = 0; row < component.Rows; row++)
|
||||
for (var i = 0; i < component.RowLength; i++)
|
||||
{
|
||||
sprite.LayerSetVisible(key, component.Activated);
|
||||
sprite.LayerMapReserveBlank(TextMapKey + row + i);
|
||||
component.LayerStatesToDraw.Add(TextMapKey + row + i, null);
|
||||
sprite.LayerSetRSI(TextMapKey + row + i, new ResPath(TextPath));
|
||||
sprite.LayerSetColor(TextMapKey + row + i, component.Color);
|
||||
sprite.LayerSetState(TextMapKey + row + i, DefaultState);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the states in the <see cref="TextScreenVisualsComponent.LayerStatesToDraw"/> to match the component <see cref="TextScreenVisualsComponent.TextToDraw"/> string.
|
||||
/// Sets the states in the <see cref="TextScreenVisualsComponent.LayerStatesToDraw"/> to match the component
|
||||
/// <see cref="TextScreenVisualsComponent.TextToDraw"/> string?[].
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Remember to set <see cref="TextScreenVisualsComponent.TextToDraw"/> to a string first.
|
||||
/// Remember to set <see cref="TextScreenVisualsComponent.TextToDraw"/> to a string?[] first.
|
||||
/// </remarks>
|
||||
public void PrepareLayerStatesToDraw(EntityUid uid, TextScreenVisualsComponent component, SpriteComponent? sprite = null)
|
||||
public void BuildTextLayers(EntityUid uid, TextScreenVisualsComponent component, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (!Resolve(uid, ref sprite))
|
||||
return;
|
||||
|
||||
for (var i = 0; i < component.TextLength; i++)
|
||||
for (var rowIdx = 0; rowIdx < Math.Min(component.TextToDraw.Length, component.Rows); rowIdx++)
|
||||
{
|
||||
if (i >= component.TextToDraw.Length)
|
||||
{
|
||||
component.LayerStatesToDraw[TextScreenLayerMapKey + i] = DefaultState;
|
||||
var row = component.TextToDraw[rowIdx];
|
||||
if (row == null)
|
||||
continue;
|
||||
var min = Math.Min(row.Length, component.RowLength);
|
||||
|
||||
for (var chr = 0; chr < min; chr++)
|
||||
{
|
||||
component.LayerStatesToDraw[TextMapKey + rowIdx + chr] = GetStateFromChar(row[chr]);
|
||||
sprite.LayerSetOffset(
|
||||
TextMapKey + rowIdx + chr,
|
||||
Vector2.Multiply(
|
||||
new Vector2((chr - min / 2f + 0.5f) * CharWidth, -rowIdx * component.RowOffset),
|
||||
TextScreenVisualsComponent.PixelSize
|
||||
) + component.TextOffset
|
||||
);
|
||||
}
|
||||
component.LayerStatesToDraw[TextScreenLayerMapKey + i] = GetStateFromChar(component.TextToDraw[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates through <see cref="TextScreenVisualsComponent.LayerStatesToDraw"/>, setting sprite states to the appropriate layers.
|
||||
/// Populates timer.LayerStatesToDraw & the sprite component's layer dict with calculated offsets.
|
||||
/// </summary>
|
||||
public void UpdateLayersToDraw(EntityUid uid, TextScreenVisualsComponent component, SpriteComponent? sprite = null)
|
||||
public void BuildTimerLayers(EntityUid uid, TextScreenTimerComponent timer, TextScreenVisualsComponent screen)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
string time = TimeToString(
|
||||
(_gameTiming.CurTime - timer.Target).Duration(),
|
||||
false,
|
||||
screen.HourFormat, screen.MinuteFormat, screen.SecondFormat
|
||||
);
|
||||
|
||||
int min = Math.Min(time.Length, screen.RowLength);
|
||||
|
||||
for (int i = 0; i < min; i++)
|
||||
{
|
||||
timer.LayerStatesToDraw[TimerMapKey + i] = GetStateFromChar(time[i]);
|
||||
sprite.LayerSetOffset(
|
||||
TimerMapKey + i,
|
||||
Vector2.Multiply(
|
||||
new Vector2((i - min / 2f + 0.5f) * CharWidth, 0f),
|
||||
TextScreenVisualsComponent.PixelSize
|
||||
) + screen.TimerOffset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a LayerStates dict by setting the sprite states individually.
|
||||
/// </summary>
|
||||
private void DrawLayers(EntityUid uid, Dictionary<string, string?> layerStates, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (!Resolve(uid, ref sprite))
|
||||
return;
|
||||
|
||||
foreach (var (key, state) in component.LayerStatesToDraw)
|
||||
{
|
||||
if (state == null)
|
||||
continue;
|
||||
foreach (var (key, state) in layerStates.Where(pairs => pairs.Value != null))
|
||||
sprite.LayerSetState(key, state);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<TextScreenVisualsComponent, TextScreenTimerComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out _))
|
||||
var query = EntityQueryEnumerator<TextScreenTimerComponent, TextScreenVisualsComponent>();
|
||||
while (query.MoveNext(out var uid, out var timer, out var screen))
|
||||
{
|
||||
// Basically Abs(TimeSpan, TimeSpan) -> Gives the difference between the current time and the target time.
|
||||
var timeToShow = _gameTiming.CurTime > comp.TargetTime ? _gameTiming.CurTime - comp.TargetTime : comp.TargetTime - _gameTiming.CurTime;
|
||||
comp.TextToDraw = TimeToString(timeToShow, false);
|
||||
PrepareLayerStatesToDraw(uid, comp);
|
||||
UpdateLayersToDraw(uid, comp);
|
||||
if (timer.Target < _gameTiming.CurTime)
|
||||
{
|
||||
OnTimerFinish(uid, screen);
|
||||
continue;
|
||||
}
|
||||
|
||||
BuildTimerLayers(uid, timer, screen);
|
||||
DrawLayers(uid, timer.LayerStatesToDraw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,28 +272,29 @@ public sealed class TextScreenSystem : VisualizerSystem<TextScreenVisualsCompone
|
||||
/// </summary>
|
||||
/// <param name="timeSpan">TimeSpan to convert into string.</param>
|
||||
/// <param name="getMilliseconds">Should the string be ss:ms if minutes are less than 1?</param>
|
||||
public static string TimeToString(TimeSpan timeSpan, bool getMilliseconds = true)
|
||||
/// <remarks>
|
||||
/// hours, minutes, seconds, and centiseconds are each set to 2 decimal places by default.
|
||||
/// </remarks>
|
||||
public static string TimeToString(TimeSpan timeSpan, bool getMilliseconds = true, string hours = "D2", string minutes = "D2", string seconds = "D2", string cs = "D2")
|
||||
{
|
||||
string firstString;
|
||||
string lastString;
|
||||
|
||||
if (timeSpan.TotalHours >= 1)
|
||||
{
|
||||
firstString = timeSpan.Hours.ToString("D2");
|
||||
lastString = timeSpan.Minutes.ToString("D2");
|
||||
firstString = timeSpan.Hours.ToString(hours);
|
||||
lastString = timeSpan.Minutes.ToString(minutes);
|
||||
}
|
||||
else if (timeSpan.TotalMinutes >= 1 || !getMilliseconds)
|
||||
{
|
||||
firstString = timeSpan.Minutes.ToString("D2");
|
||||
// It's nicer to see a timer set at 5 seconds actually start at 00:05 instead of 00:04.
|
||||
var seconds = timeSpan.Seconds + (timeSpan.Milliseconds > 500 ? 1 : 0);
|
||||
lastString = seconds.ToString("D2");
|
||||
firstString = timeSpan.Minutes.ToString(minutes);
|
||||
lastString = timeSpan.Seconds.ToString(seconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
firstString = timeSpan.Seconds.ToString("D2");
|
||||
firstString = timeSpan.Seconds.ToString(seconds);
|
||||
var centiseconds = timeSpan.Milliseconds / 10;
|
||||
lastString = centiseconds.ToString("D2");
|
||||
lastString = centiseconds.ToString(cs);
|
||||
}
|
||||
|
||||
return firstString + ':' + lastString;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
namespace Content.Client.TextScreen;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Client.TextScreen;
|
||||
|
||||
/// <summary>
|
||||
/// This is an active component for tracking <see cref="TextScreenVisualsComponent"/>
|
||||
/// Added to an entity already containing a <see cref="TextScreenVisualsComponent"/> to track frame-by-frame timer updates
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TextScreenTimerComponent : Component
|
||||
{
|
||||
|
||||
[DataField("targetTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan Target = TimeSpan.Zero;
|
||||
public Dictionary<string, string?> LayerStatesToDraw = new();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.TextScreen;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Client.TextScreen;
|
||||
|
||||
@@ -16,52 +15,63 @@ public sealed partial class TextScreenVisualsComponent : Component
|
||||
/// <summary>
|
||||
/// The color of the text drawn.
|
||||
/// </summary>
|
||||
[DataField("color")]
|
||||
public Color Color { get; set; } = Color.FloralWhite;
|
||||
/// <remarks>
|
||||
/// 15,151,251 is the old ss13 color, from tg
|
||||
/// </remarks>
|
||||
[DataField("color"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public Color Color = new Color(15, 151, 251);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the screen is on.
|
||||
/// </summary>
|
||||
[DataField("activated")]
|
||||
public bool Activated;
|
||||
|
||||
/// <summary>
|
||||
/// The current mode of the screen - is it showing text, or currently counting?
|
||||
/// </summary>
|
||||
[DataField("currentMode")]
|
||||
public TextScreenMode CurrentMode = TextScreenMode.Text;
|
||||
|
||||
/// <summary>
|
||||
/// The time it is counting to or from.
|
||||
/// </summary>
|
||||
[DataField("targetTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan TargetTime = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Offset for drawing the text. <br/>
|
||||
/// (0, 8) pixels is the default for the Structures\Wallmounts\textscreen.rsi
|
||||
/// Offset for centering the text.
|
||||
/// </summary>
|
||||
[DataField("textOffset"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 TextOffset = new(0f, 8f * PixelSize);
|
||||
public Vector2 TextOffset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of characters this component can show.
|
||||
/// Offset for centering the timer.
|
||||
/// </summary>
|
||||
[DataField("textLength")]
|
||||
public int TextLength = 5;
|
||||
[DataField("timerOffset"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public Vector2 TimerOffset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Text the screen should show when it's not counting.
|
||||
/// Number of rows of text this screen can render.
|
||||
/// </summary>
|
||||
[DataField("rows")]
|
||||
public int Rows = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Spacing between each text row
|
||||
/// </summary>
|
||||
[DataField("rowOffset")]
|
||||
public int RowOffset = 7;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of characters this component can show per row.
|
||||
/// </summary>
|
||||
[DataField("rowLength")]
|
||||
public int RowLength = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Text the screen should show when it finishes a timer.
|
||||
/// </summary>
|
||||
[DataField("text"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Text = "";
|
||||
|
||||
public string TextToDraw = "";
|
||||
public string?[] Text = new string?[2];
|
||||
|
||||
/// <summary>
|
||||
/// The different layers for each character - this is the currently drawn states.
|
||||
/// Text the screen will draw whenever appearance is updated.
|
||||
/// </summary>
|
||||
public string?[] TextToDraw = new string?[2];
|
||||
|
||||
/// <summary>
|
||||
/// Per-character layers, for mapping into the sprite component.
|
||||
/// </summary>
|
||||
[DataField("layerStatesToDraw")]
|
||||
public Dictionary<string, string?> LayerStatesToDraw = new();
|
||||
}
|
||||
|
||||
[DataField("hourFormat")]
|
||||
public string HourFormat = "D2";
|
||||
[DataField("minuteFormat")]
|
||||
public string MinuteFormat = "D2";
|
||||
[DataField("secondFormat")]
|
||||
public string SecondFormat = "D2";
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed partial class SignalTimerComponent : Component
|
||||
/// The label, used for TextScreen visuals currently.
|
||||
/// </summary>
|
||||
[DataField("label"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Label = "";
|
||||
public string Label = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The port that gets signaled when the timer triggers.
|
||||
|
||||
@@ -52,14 +52,19 @@ public sealed class SignalTimerSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes a timer, triggering its main port, and removing its <see cref="ActiveSignalTimerComponent"/>.
|
||||
/// </summary>
|
||||
public void Trigger(EntityUid uid, SignalTimerComponent signalTimer)
|
||||
{
|
||||
RemComp<ActiveSignalTimerComponent>(uid);
|
||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, new string?[] { signalTimer.Label }, appearance);
|
||||
}
|
||||
|
||||
_signalSystem.InvokePort(uid, signalTimer.TriggerPort);
|
||||
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.Mode, TextScreenMode.Text);
|
||||
|
||||
if (_ui.TryGetUi(uid, SignalTimerUiKey.Key, out var bui))
|
||||
{
|
||||
_ui.SetUiState(bui, new SignalTimerBoundUserInterfaceState(signalTimer.Label,
|
||||
@@ -75,11 +80,6 @@ public sealed class SignalTimerSystem : EntitySystem
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
UpdateTimer();
|
||||
}
|
||||
|
||||
private void UpdateTimer()
|
||||
{
|
||||
var query = EntityQueryEnumerator<ActiveSignalTimerComponent, SignalTimerComponent>();
|
||||
while (query.MoveNext(out var uid, out var active, out var timer))
|
||||
{
|
||||
@@ -88,8 +88,7 @@ public sealed class SignalTimerSystem : EntitySystem
|
||||
|
||||
Trigger(uid, timer);
|
||||
|
||||
if (timer.DoneSound == null)
|
||||
continue;
|
||||
if (timer.DoneSound != null)
|
||||
_audio.PlayPvs(timer.DoneSound, uid);
|
||||
}
|
||||
}
|
||||
@@ -98,7 +97,6 @@ public sealed class SignalTimerSystem : EntitySystem
|
||||
/// Checks if a UI <paramref name="message"/> is allowed to be sent by the user.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity that is interacted with.</param>
|
||||
/// <param name="message"></param>
|
||||
private bool IsMessageValid(EntityUid uid, BoundUserInterfaceMessage message)
|
||||
{
|
||||
if (message.Session.AttachedEntity is not { Valid: true } mob)
|
||||
@@ -110,53 +108,53 @@ public sealed class SignalTimerSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by <see cref="SignalTimerTextChangedMessage"/> to both
|
||||
/// change the default component label, and propagate that change to the TextScreen.
|
||||
/// </summary>
|
||||
private void OnTextChangedMessage(EntityUid uid, SignalTimerComponent component, SignalTimerTextChangedMessage args)
|
||||
{
|
||||
if (!IsMessageValid(uid, args))
|
||||
return;
|
||||
|
||||
component.Label = args.Text[..Math.Min(5, args.Text.Length)];
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label);
|
||||
|
||||
if (!HasComp<ActiveSignalTimerComponent>(uid))
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, new string?[] { component.Label });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by <see cref="SignalTimerDelayChangedMessage"/> to change the <see cref="SignalTimerComponent"/>
|
||||
/// delay, and propagate that change to a textscreen.
|
||||
/// </summary>
|
||||
private void OnDelayChangedMessage(EntityUid uid, SignalTimerComponent component, SignalTimerDelayChangedMessage args)
|
||||
{
|
||||
if (!IsMessageValid(uid, args))
|
||||
return;
|
||||
|
||||
component.Delay = args.Delay.TotalSeconds;
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.TargetTime, component.Delay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by <see cref="SignalTimerStartMessage"/> to instantiate an <see cref="ActiveSignalTimerComponent"/>,
|
||||
/// clear <see cref="TextScreenVisuals.ScreenText"/>, propagate those changes, and invoke the start port.
|
||||
/// </summary>
|
||||
private void OnTimerStartMessage(EntityUid uid, SignalTimerComponent component, SignalTimerStartMessage args)
|
||||
{
|
||||
if (!IsMessageValid(uid, args))
|
||||
return;
|
||||
|
||||
TryComp<AppearanceComponent>(uid, out var appearance);
|
||||
|
||||
if (!HasComp<ActiveSignalTimerComponent>(uid))
|
||||
{
|
||||
var activeTimer = EnsureComp<ActiveSignalTimerComponent>(uid);
|
||||
activeTimer.TriggerTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.Delay);
|
||||
var timer = EnsureComp<ActiveSignalTimerComponent>(uid);
|
||||
timer.TriggerTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.Delay);
|
||||
|
||||
if (appearance != null)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.Mode, TextScreenMode.Timer, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.TargetTime, activeTimer.TriggerTime, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.TargetTime, timer.TriggerTime, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, new string?[] { }, appearance);
|
||||
}
|
||||
|
||||
_signalSystem.InvokePort(uid, component.StartPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemComp<ActiveSignalTimerComponent>(uid);
|
||||
|
||||
if (appearance != null)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.Mode, TextScreenMode.Text, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label, appearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,21 @@ using System.Threading;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Server.Chat;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -30,10 +34,14 @@ namespace Content.Server.RoundEnd
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
||||
[Dependency] private readonly EmergencyShuttleSystem _shuttle = default!;
|
||||
[Dependency] private readonly ShuttleTimerSystem _shuttleTimerSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||
|
||||
@@ -52,7 +60,7 @@ namespace Content.Server.RoundEnd
|
||||
public TimeSpan? ShuttleTimeLeft => ExpectedCountdownEnd - _gameTiming.CurTime;
|
||||
|
||||
public TimeSpan AutoCallStartTime;
|
||||
private bool AutoCalledBefore = false;
|
||||
private bool _autoCalledBefore = false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -83,10 +91,31 @@ namespace Content.Server.RoundEnd
|
||||
LastCountdownStart = null;
|
||||
ExpectedCountdownEnd = null;
|
||||
SetAutoCallTime();
|
||||
AutoCalledBefore = false;
|
||||
_autoCalledBefore = false;
|
||||
RaiseLocalEvent(RoundEndSystemChangedEvent.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the MapUid of the station using <see cref="StationSystem.GetLargestGrid"/>
|
||||
/// </summary>
|
||||
public EntityUid? GetStation()
|
||||
{
|
||||
AllEntityQuery<StationEmergencyShuttleComponent, StationDataComponent>().MoveNext(out _, out _, out var data);
|
||||
if (data == null)
|
||||
return null;
|
||||
var targetGrid = _stationSystem.GetLargestGrid(data);
|
||||
return targetGrid == null ? null : Transform(targetGrid.Value).MapUid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get centcomm's MapUid
|
||||
/// </summary>
|
||||
public EntityUid? GetCentcomm()
|
||||
{
|
||||
AllEntityQuery<StationCentcommComponent>().MoveNext(out var centcomm);
|
||||
return centcomm == null ? null : _mapManager.GetMapEntityId(centcomm.MapId);
|
||||
}
|
||||
|
||||
public bool CanCallOrRecall()
|
||||
{
|
||||
return _cooldownTokenSource == null;
|
||||
@@ -164,6 +193,21 @@ namespace Content.Server.RoundEnd
|
||||
|
||||
ActivateCooldown();
|
||||
RaiseLocalEvent(RoundEndSystemChangedEvent.Default);
|
||||
|
||||
var shuttle = _shuttle.GetShuttle();
|
||||
if (shuttle != null && TryComp<DeviceNetworkComponent>(shuttle, out var net))
|
||||
{
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = shuttle,
|
||||
[ShuttleTimerMasks.SourceMap] = GetCentcomm(),
|
||||
[ShuttleTimerMasks.DestMap] = GetStation(),
|
||||
[ShuttleTimerMasks.ShuttleTime] = countdownTime,
|
||||
[ShuttleTimerMasks.SourceTime] = countdownTime + TimeSpan.FromSeconds(_shuttle.TransitTime + _cfg.GetCVar(CCVars.EmergencyShuttleDockTime)),
|
||||
[ShuttleTimerMasks.DestTime] = countdownTime,
|
||||
};
|
||||
_deviceNetworkSystem.QueuePacket(shuttle.Value, null, payload, net.TransmitFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelRoundEndCountdown(EntityUid? requester = null, bool checkCooldown = true)
|
||||
@@ -193,6 +237,24 @@ namespace Content.Server.RoundEnd
|
||||
ExpectedCountdownEnd = null;
|
||||
ActivateCooldown();
|
||||
RaiseLocalEvent(RoundEndSystemChangedEvent.Default);
|
||||
|
||||
// remove all active shuttle timers
|
||||
var zero = TimeSpan.Zero;
|
||||
var shuttle = _shuttle.GetShuttle();
|
||||
if (shuttle != null && TryComp<DeviceNetworkComponent>(shuttle, out var net))
|
||||
{
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = shuttle,
|
||||
[ShuttleTimerMasks.SourceMap] = GetCentcomm(),
|
||||
[ShuttleTimerMasks.DestMap] = GetStation(),
|
||||
[ShuttleTimerMasks.ShuttleTime] = zero,
|
||||
[ShuttleTimerMasks.SourceTime] = zero,
|
||||
[ShuttleTimerMasks.DestTime] = zero,
|
||||
[ShuttleTimerMasks.Text] = new string?[] { string.Empty, string.Empty }
|
||||
};
|
||||
_deviceNetworkSystem.QueuePacket(shuttle.Value, null, payload, net.TransmitFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndRound(TimeSpan? countdownTime = null)
|
||||
@@ -284,14 +346,14 @@ namespace Content.Server.RoundEnd
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
// Check if we should auto-call.
|
||||
int mins = AutoCalledBefore ? _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallExtensionTime)
|
||||
int mins = _autoCalledBefore ? _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallExtensionTime)
|
||||
: _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallTime);
|
||||
if (mins != 0 && _gameTiming.CurTime - AutoCallStartTime > TimeSpan.FromMinutes(mins))
|
||||
{
|
||||
if (!_shuttle.EmergencyShuttleArrived && ExpectedCountdownEnd is null)
|
||||
{
|
||||
RequestRoundEnd(null, false, "round-end-system-shuttle-auto-called-announcement");
|
||||
AutoCalledBefore = true;
|
||||
_autoCalledBefore = true;
|
||||
}
|
||||
|
||||
// Always reset auto-call in case of a recall.
|
||||
@@ -306,7 +368,7 @@ namespace Content.Server.RoundEnd
|
||||
}
|
||||
|
||||
public enum RoundEndBehavior : byte
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantly end round
|
||||
/// </summary>
|
||||
@@ -321,5 +383,5 @@ namespace Content.Server.RoundEnd
|
||||
/// Do nothing
|
||||
/// </summary>
|
||||
Nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,16 @@ public sealed partial class ArrivalsShuttleComponent : Component
|
||||
[DataField("station")]
|
||||
public EntityUid Station;
|
||||
|
||||
[DataField("nextTransfer", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[DataField("nextTransfer", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextTransfer;
|
||||
|
||||
[DataField("nextArrivalsTime", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[DataField("nextArrivalsTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextArrivalsTime;
|
||||
|
||||
/// <summary>
|
||||
/// the first arrivals FTL originates from nullspace instead of the station
|
||||
/// </summary>
|
||||
[DataField("firstRun")]
|
||||
public bool FirstRun = true;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Content.Server.Shuttles.Systems;
|
||||
|
||||
namespace Content.Server.Shuttles.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Added to station emergency shuttles by <see cref="EmergencyShuttleSystem.AddEmergencyShuttle"/>,
|
||||
/// for FTL event handlers
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class EmergencyShuttleComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
24
Content.Server/Shuttles/Components/ShuttleTimerComponent.cs
Normal file
24
Content.Server/Shuttles/Components/ShuttleTimerComponent.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Content.Server.Shuttles.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ShuttleTimerComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Awkward hashable string consts because NetworkPayload requires string keys
|
||||
/// TODO: Refactor NetworkPayload to accept bytes from enums?
|
||||
/// </summary>
|
||||
public sealed class ShuttleTimerMasks
|
||||
{
|
||||
public static readonly string ShuttleTime = "ShuttleTime";
|
||||
public static readonly string DestTime = "DestTime";
|
||||
public static readonly string SourceTime = "SourceTime";
|
||||
public static readonly string ShuttleMap = "ShuttleMap";
|
||||
public static readonly string SourceMap = "SourceMap";
|
||||
public static readonly string DestMap = "DestMap";
|
||||
public static readonly string Docked = "Docked";
|
||||
public static readonly string Text = "Text";
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ using Content.Server.Administration;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Events;
|
||||
using Content.Server.Parallax;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Salvage;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Events;
|
||||
@@ -46,6 +49,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
[Dependency] private readonly BiomeSystem _biomes = default!;
|
||||
[Dependency] private readonly GameTicker _ticker = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
||||
[Dependency] private readonly RestrictedRangeSystem _restricted = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly ShuttleSystem _shuttles = default!;
|
||||
@@ -57,6 +61,11 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
/// </summary>
|
||||
public bool Enabled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The first arrival is a little early, to save everyone 10s
|
||||
/// </summary>
|
||||
private const float RoundStartFTLDuration = 10f;
|
||||
|
||||
private readonly List<ProtoId<BiomeTemplatePrototype>> _arrivalsBiomeOptions = new()
|
||||
{
|
||||
"Grasslands",
|
||||
@@ -68,7 +77,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PlayerSpawningEvent>(OnPlayerSpawn, before: new []{typeof(SpawnPointSystem)});
|
||||
SubscribeLocalEvent<PlayerSpawningEvent>(OnPlayerSpawn, before: new[] { typeof(SpawnPointSystem) });
|
||||
SubscribeLocalEvent<StationArrivalsComponent, ComponentStartup>(OnArrivalsStartup);
|
||||
|
||||
SubscribeLocalEvent<ArrivalsShuttleComponent, ComponentStartup>(OnShuttleStartup);
|
||||
@@ -77,6 +86,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStarting);
|
||||
SubscribeLocalEvent<ArrivalsShuttleComponent, FTLStartedEvent>(OnArrivalsFTL);
|
||||
SubscribeLocalEvent<ArrivalsShuttleComponent, FTLCompletedEvent>(OnArrivalsDocked);
|
||||
|
||||
// Don't invoke immediately as it will get set in the natural course of things.
|
||||
Enabled = _cfgManager.GetCVar(CCVars.ArrivalsShuttles);
|
||||
@@ -170,13 +180,49 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
_cfgManager.UnsubValueChanged(CCVars.ArrivalsShuttles, SetArrivals);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// First sends shuttle timer data, then kicks people off the shuttle if it isn't leaving the arrivals terminal
|
||||
/// </summary>
|
||||
private void OnArrivalsFTL(EntityUid shuttleUid, ArrivalsShuttleComponent component, ref FTLStartedEvent args)
|
||||
{
|
||||
if (!TryGetArrivals(out EntityUid arrivals))
|
||||
return;
|
||||
|
||||
var arrivalsMapUid = Transform(arrivals).MapUid;
|
||||
if (TryComp<DeviceNetworkComponent>(shuttleUid, out var netComp))
|
||||
{
|
||||
TryComp<FTLComponent>(shuttleUid, out var ftlComp);
|
||||
var ftlTime = TimeSpan.FromSeconds(ftlComp?.TravelTime ?? ShuttleSystem.DefaultTravelTime);
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = shuttleUid,
|
||||
[ShuttleTimerMasks.ShuttleTime] = ftlTime
|
||||
};
|
||||
|
||||
// unfortunate levels of spaghetti due to roundstart arrivals ftl behavior
|
||||
EntityUid? sourceMap;
|
||||
var arrivalsDelay = _cfgManager.GetCVar(CCVars.ArrivalsCooldown);
|
||||
|
||||
if (component.FirstRun)
|
||||
{
|
||||
var station = _station.GetLargestGrid(Comp<StationDataComponent>(component.Station));
|
||||
sourceMap = station == null ? null : Transform(station.Value)?.MapUid;
|
||||
arrivalsDelay += RoundStartFTLDuration;
|
||||
component.FirstRun = false;
|
||||
payload.Add(ShuttleTimerMasks.DestMap, Transform(args.TargetCoordinates.EntityId).MapUid);
|
||||
payload.Add(ShuttleTimerMasks.DestTime, ftlTime);
|
||||
}
|
||||
else
|
||||
sourceMap = args.FromMapUid;
|
||||
|
||||
payload.Add(ShuttleTimerMasks.SourceMap, sourceMap);
|
||||
payload.Add(ShuttleTimerMasks.SourceTime, ftlTime + TimeSpan.FromSeconds(arrivalsDelay));
|
||||
|
||||
_deviceNetworkSystem.QueuePacket(shuttleUid, null, payload, netComp.TransmitFrequency);
|
||||
}
|
||||
|
||||
// Don't do anything here when leaving arrivals.
|
||||
var arrivalsMapUid = Transform(arrivals).MapUid;
|
||||
if (args.FromMapUid == arrivalsMapUid)
|
||||
return;
|
||||
|
||||
@@ -205,7 +251,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
TryTeleportToMapSpawn(pUid, component.Station, xform);
|
||||
}
|
||||
|
||||
// Players who have remained at arrives keep their warp coupon (PendingClockInComponent) for now.
|
||||
// Players who have remained at arrivals keep their warp coupon (PendingClockInComponent) for now.
|
||||
if (xform.MapUid == arrivalsMapUid)
|
||||
continue;
|
||||
|
||||
@@ -215,6 +261,24 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnArrivalsDocked(EntityUid uid, ArrivalsShuttleComponent component, ref FTLCompletedEvent args)
|
||||
{
|
||||
TimeSpan dockTime = component.NextTransfer - _timing.CurTime + TimeSpan.FromSeconds(ShuttleSystem.DefaultStartupTime);
|
||||
|
||||
if (TryComp<DeviceNetworkComponent>(uid, out var netComp))
|
||||
{
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = uid,
|
||||
[ShuttleTimerMasks.ShuttleTime] = dockTime,
|
||||
[ShuttleTimerMasks.SourceMap] = args.MapUid,
|
||||
[ShuttleTimerMasks.SourceTime] = dockTime,
|
||||
[ShuttleTimerMasks.Docked] = true
|
||||
};
|
||||
_deviceNetworkSystem.QueuePacket(uid, null, payload, netComp.TransmitFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
private void DumpChildren(EntityUid uid,
|
||||
ref FTLStartedEvent args,
|
||||
EntityQuery<PendingClockInComponent> pendingEntQuery,
|
||||
@@ -259,7 +323,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
|
||||
var points = EntityQueryEnumerator<SpawnPointComponent, TransformComponent>();
|
||||
var possiblePositions = new List<EntityCoordinates>();
|
||||
while ( points.MoveNext(out var uid, out var spawnPoint, out var xform))
|
||||
while (points.MoveNext(out var uid, out var spawnPoint, out var xform))
|
||||
{
|
||||
if (spawnPoint.SpawnType != SpawnPointType.LateJoin || xform.MapID != mapId)
|
||||
continue;
|
||||
@@ -291,7 +355,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
var possiblePositions = new ValueList<EntityCoordinates>(32);
|
||||
|
||||
// Find a spawnpoint on the same map as the player is already docked with now.
|
||||
while ( points.MoveNext(out var uid, out var spawnPoint, out var xform))
|
||||
while (points.MoveNext(out var uid, out var spawnPoint, out var xform))
|
||||
{
|
||||
if (spawnPoint.SpawnType == SpawnPointType.LateJoin &&
|
||||
_station.GetOwningStation(uid, xform) == stationId)
|
||||
@@ -359,7 +423,6 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
// TODO: Docking should be per-grid rather than per dock and bump off when undocking.
|
||||
|
||||
// TODO: Stop dispatch if emergency shuttle has arrived.
|
||||
// TODO: Need server join message specifying shuttle wait time or smth.
|
||||
// TODO: Need maps
|
||||
// TODO: Need emergency suits on shuttle probs
|
||||
// TODO: Need some kind of comp to shunt people off if they try to get on?
|
||||
@@ -370,7 +433,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
if (comp.NextTransfer > curTime || !TryComp<StationDataComponent>(comp.Station, out var data))
|
||||
continue;
|
||||
|
||||
var tripTime = ShuttleSystem.DefaultTravelTime + ShuttleSystem.DefaultStartupTime ;
|
||||
var tripTime = ShuttleSystem.DefaultTravelTime + ShuttleSystem.DefaultStartupTime;
|
||||
|
||||
// Go back to arrivals source
|
||||
if (xform.MapUid != arrivalsXform.MapUid)
|
||||
@@ -506,7 +569,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
||||
var arrivalsComp = EnsureComp<ArrivalsShuttleComponent>(component.Shuttle);
|
||||
arrivalsComp.Station = uid;
|
||||
EnsureComp<ProtectedGridComponent>(uid);
|
||||
_shuttles.FTLTravel(component.Shuttle, shuttleComp, arrivals, hyperspaceTime: 10f, dock: true);
|
||||
_shuttles.FTLTravel(component.Shuttle, shuttleComp, arrivals, hyperspaceTime: RoundStartFTLDuration, dock: true);
|
||||
arrivalsComp.NextTransfer = _timing.CurTime + TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Threading;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Events;
|
||||
using Content.Server.UserInterface;
|
||||
@@ -374,6 +376,24 @@ public sealed partial class EmergencyShuttleSystem
|
||||
RaiseLocalEvent(new EmergencyShuttleAuthorizedEvent());
|
||||
AnnounceLaunch();
|
||||
UpdateAllEmergencyConsoles();
|
||||
|
||||
var time = TimeSpan.FromSeconds(_authorizeTime);
|
||||
var shuttle = GetShuttle();
|
||||
if (shuttle != null && TryComp<DeviceNetworkComponent>(shuttle, out var net))
|
||||
{
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = shuttle,
|
||||
[ShuttleTimerMasks.SourceMap] = _roundEnd.GetStation(),
|
||||
[ShuttleTimerMasks.DestMap] = _roundEnd.GetCentcomm(),
|
||||
[ShuttleTimerMasks.ShuttleTime] = time,
|
||||
[ShuttleTimerMasks.SourceTime] = time,
|
||||
[ShuttleTimerMasks.DestTime] = time + TimeSpan.FromSeconds(TransitTime),
|
||||
[ShuttleTimerMasks.Docked] = true
|
||||
};
|
||||
_deviceNetworkSystem.QueuePacket(shuttle.Value, null, payload, net.TransmitFrequency);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,14 @@ using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Communications;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.GameTicking.Events;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Events;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Access.Systems;
|
||||
@@ -45,7 +49,9 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
[Dependency] private readonly AccessReaderSystem _reader = default!;
|
||||
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
||||
[Dependency] private readonly CommunicationsConsoleSystem _commsConsole = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
||||
[Dependency] private readonly DockingSystem _dock = default!;
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IdCardSystem _idSystem = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _map = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
@@ -53,6 +59,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly ShuttleSystem _shuttle = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
@@ -70,10 +77,14 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
_emergencyShuttleEnabled = _configManager.GetCVar(CCVars.EmergencyShuttleEnabled);
|
||||
// Don't immediately invoke as roundstart will just handle it.
|
||||
_configManager.OnValueChanged(CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled);
|
||||
|
||||
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
|
||||
SubscribeLocalEvent<StationEmergencyShuttleComponent, ComponentStartup>(OnStationStartup);
|
||||
SubscribeLocalEvent<StationCentcommComponent, ComponentShutdown>(OnCentcommShutdown);
|
||||
SubscribeLocalEvent<StationCentcommComponent, ComponentInit>(OnCentcommInit);
|
||||
|
||||
SubscribeLocalEvent<EmergencyShuttleComponent, FTLStartedEvent>(OnEmergencyFTL);
|
||||
SubscribeLocalEvent<EmergencyShuttleComponent, FTLCompletedEvent>(OnEmergencyFTLComplete);
|
||||
SubscribeNetworkEvent<EmergencyShuttleRequestPositionMessage>(OnShuttleRequestPosition);
|
||||
InitializeEmergencyConsole();
|
||||
}
|
||||
@@ -98,6 +109,15 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
component.MapEntity = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get the EntityUid of the emergency shuttle
|
||||
/// </summary>
|
||||
public EntityUid? GetShuttle()
|
||||
{
|
||||
AllEntityQuery<EmergencyShuttleComponent>().MoveNext(out var shuttle, out _);
|
||||
return shuttle;
|
||||
}
|
||||
|
||||
private void SetEmergencyShuttleEnabled(bool value)
|
||||
{
|
||||
if (_emergencyShuttleEnabled == value)
|
||||
@@ -173,7 +193,56 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the emergency shuttle for the station.
|
||||
/// Escape shuttle FTL event handler. The only escape shuttle FTL transit should be from station to centcomm at round end
|
||||
/// </summary>
|
||||
private void OnEmergencyFTL(EntityUid uid, EmergencyShuttleComponent component, ref FTLStartedEvent args)
|
||||
{
|
||||
TimeSpan ftlTime = TimeSpan.FromSeconds
|
||||
(
|
||||
TryComp<FTLComponent>(uid, out var ftlComp) ?
|
||||
ftlComp.TravelTime : ShuttleSystem.DefaultTravelTime
|
||||
);
|
||||
|
||||
if (TryComp<DeviceNetworkComponent>(uid, out var netComp))
|
||||
{
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = uid,
|
||||
[ShuttleTimerMasks.SourceMap] = args.FromMapUid,
|
||||
[ShuttleTimerMasks.DestMap] = args.TargetCoordinates.GetMapUid(_entityManager),
|
||||
[ShuttleTimerMasks.ShuttleTime] = ftlTime,
|
||||
[ShuttleTimerMasks.SourceTime] = ftlTime,
|
||||
[ShuttleTimerMasks.DestTime] = ftlTime
|
||||
};
|
||||
_deviceNetworkSystem.QueuePacket(uid, null, payload, netComp.TransmitFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the escape shuttle finishes FTL (docks at centcomm), have the timers display the round end countdown
|
||||
/// </summary>
|
||||
private void OnEmergencyFTLComplete(EntityUid uid, EmergencyShuttleComponent component, ref FTLCompletedEvent args)
|
||||
{
|
||||
var countdownTime = TimeSpan.FromSeconds(_configManager.GetCVar(CCVars.RoundRestartTime));
|
||||
var shuttle = args.Entity;
|
||||
if (TryComp<DeviceNetworkComponent>(shuttle, out var net))
|
||||
{
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = shuttle,
|
||||
[ShuttleTimerMasks.SourceMap] = _roundEnd.GetCentcomm(),
|
||||
[ShuttleTimerMasks.DestMap] = _roundEnd.GetStation(),
|
||||
[ShuttleTimerMasks.ShuttleTime] = countdownTime,
|
||||
[ShuttleTimerMasks.SourceTime] = countdownTime,
|
||||
[ShuttleTimerMasks.DestTime] = countdownTime,
|
||||
[ShuttleTimerMasks.Text] = new string?[] { "BYE!" }
|
||||
};
|
||||
_deviceNetworkSystem.QueuePacket(shuttle, null, payload, net.TransmitFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to dock the emergency shuttle to the station.
|
||||
/// </summary>
|
||||
public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleComponent? stationShuttle = null)
|
||||
{
|
||||
@@ -206,6 +275,23 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir())), playDefaultSound: false);
|
||||
}
|
||||
|
||||
// shuttle timers
|
||||
var time = TimeSpan.FromSeconds(_consoleAccumulator);
|
||||
if (TryComp<DeviceNetworkComponent>(stationShuttle.EmergencyShuttle.Value, out var netComp))
|
||||
{
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[ShuttleTimerMasks.ShuttleMap] = stationShuttle.EmergencyShuttle.Value,
|
||||
[ShuttleTimerMasks.SourceMap] = targetXform?.MapUid,
|
||||
[ShuttleTimerMasks.DestMap] = _roundEnd.GetCentcomm(),
|
||||
[ShuttleTimerMasks.ShuttleTime] = time,
|
||||
[ShuttleTimerMasks.SourceTime] = time,
|
||||
[ShuttleTimerMasks.DestTime] = time + TimeSpan.FromSeconds(TransitTime),
|
||||
[ShuttleTimerMasks.Docked] = true
|
||||
};
|
||||
_deviceNetworkSystem.QueuePacket(stationShuttle.EmergencyShuttle.Value, null, payload, netComp.TransmitFrequency);
|
||||
}
|
||||
|
||||
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} docked with stations");
|
||||
// TODO: Need filter extensions or something don't blame me.
|
||||
_audio.PlayGlobal("/Audio/Announcements/shuttle_dock.ogg", Filter.Broadcast(), true);
|
||||
@@ -286,10 +372,8 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
var query = AllEntityQuery<StationEmergencyShuttleComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
AddEmergencyShuttle(uid, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCentcomm(StationCentcommComponent component)
|
||||
{
|
||||
@@ -410,6 +494,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
component.EmergencyShuttle = shuttle;
|
||||
EnsureComp<ProtectedGridComponent>(shuttle.Value);
|
||||
EnsureComp<PreventPilotComponent>(shuttle.Value);
|
||||
EnsureComp<EmergencyShuttleComponent>(shuttle.Value);
|
||||
}
|
||||
|
||||
private void OnEscapeUnpaused(EntityUid uid, EscapePodComponent component, ref EntityUnpausedEvent args)
|
||||
@@ -450,6 +535,6 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
||||
if (!Resolve(shuttle, ref grid, ref shuttleXform))
|
||||
return false;
|
||||
|
||||
return shuttleXform.WorldMatrix.TransformBox(grid.LocalAABB).Contains(xform.WorldPosition);
|
||||
return _transformSystem.GetWorldMatrix(shuttleXform).TransformBox(grid.LocalAABB).Contains(_transformSystem.GetWorldPosition(xform));
|
||||
}
|
||||
}
|
||||
|
||||
69
Content.Server/Shuttles/Systems/ShuttleTimerSystem.cs
Normal file
69
Content.Server/Shuttles/Systems/ShuttleTimerSystem.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Content.Shared.TextScreen;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
|
||||
namespace Content.Server.Shuttles.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Controls the wallmounted screens on stations and shuttles displaying e.g. FTL duration, ETA
|
||||
/// </summary>
|
||||
public sealed class ShuttleTimerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShuttleTimerComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if/how a broadcast packet affects this timer.
|
||||
/// All shuttle timer packets are broadcast in their network, and subnetting is implemented by filtering timer MapUid.
|
||||
/// </summary>
|
||||
private void OnPacketReceived(EntityUid uid, ShuttleTimerComponent component, DeviceNetworkPacketEvent args)
|
||||
{
|
||||
var timerXform = Transform(uid);
|
||||
|
||||
// no false positives.
|
||||
if (timerXform.MapUid == null)
|
||||
return;
|
||||
|
||||
string key;
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.ShuttleMap, out EntityUid? shuttleMap);
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.SourceMap, out EntityUid? source);
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.DestMap, out EntityUid? dest);
|
||||
args.Data.TryGetValue(ShuttleTimerMasks.Docked, out bool docked);
|
||||
string?[] text = new string?[] { docked ? "ETD" : "ETA" };
|
||||
|
||||
switch (timerXform.MapUid)
|
||||
{
|
||||
// sometimes the timer transforms on FTL shuttles have a hyperspace mapuid, so matching by grid works as a fallback.
|
||||
case var local when local == shuttleMap || timerXform.GridUid == shuttleMap:
|
||||
key = ShuttleTimerMasks.ShuttleTime;
|
||||
break;
|
||||
case var origin when origin == source:
|
||||
key = ShuttleTimerMasks.SourceTime;
|
||||
break;
|
||||
case var remote when remote == dest:
|
||||
key = ShuttleTimerMasks.DestTime;
|
||||
text = new string?[] { "ETA" };
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Data.TryGetValue(key, out TimeSpan duration))
|
||||
return;
|
||||
|
||||
if (args.Data.TryGetValue(ShuttleTimerMasks.Text, out string?[]? label))
|
||||
text = label;
|
||||
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.TargetTime, _gameTiming.CurTime + duration);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, text);
|
||||
}
|
||||
}
|
||||
@@ -10,26 +10,16 @@ public enum TextScreenVisuals : byte
|
||||
/// Expects a <see cref="bool"/>.
|
||||
/// </summary>
|
||||
On,
|
||||
/// <summary>
|
||||
/// Is this a timer or a text-screen? <br/>
|
||||
/// Expects a <see cref="TextScreenMode"/>.
|
||||
/// </summary>
|
||||
Mode,
|
||||
|
||||
/// <summary>
|
||||
/// What text to show? <br/>
|
||||
/// Expects a <see cref="string"/>.
|
||||
/// Expects a <see cref="string?[]"/>.
|
||||
/// </summary>
|
||||
ScreenText,
|
||||
|
||||
/// <summary>
|
||||
/// What is the target time? <br/>
|
||||
/// Expects a <see cref="TimeSpan"/>.
|
||||
/// </summary>
|
||||
TargetTime
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum TextScreenMode : byte
|
||||
{
|
||||
Text,
|
||||
Timer
|
||||
}
|
||||
|
||||
@@ -1123,6 +1123,28 @@ entities:
|
||||
- pos: 2.5,3.5
|
||||
parent: 818
|
||||
type: Transform
|
||||
- proto: ArrivalsShuttleTimer
|
||||
entities:
|
||||
- uid: 597
|
||||
components:
|
||||
- pos: -7.5,-5.5
|
||||
parent: 818
|
||||
type: Transform
|
||||
- uid: 633
|
||||
components:
|
||||
- pos: 6.5,-5.5
|
||||
parent: 818
|
||||
type: Transform
|
||||
- uid: 928
|
||||
components:
|
||||
- pos: -6.5,-14.5
|
||||
parent: 818
|
||||
type: Transform
|
||||
- uid: 929
|
||||
components:
|
||||
- pos: 5.5,-14.5
|
||||
parent: 818
|
||||
type: Transform
|
||||
- proto: AtmosDeviceFanTiny
|
||||
entities:
|
||||
- uid: 296
|
||||
|
||||
@@ -44,6 +44,8 @@ entities:
|
||||
type: BecomesStation
|
||||
- type: OccluderTree
|
||||
- type: Shuttle
|
||||
- type: DeviceNetwork
|
||||
transmitFrequencyId: ArrivalsShuttleTimer
|
||||
- gravityShakeSound: !type:SoundPathSpecifier
|
||||
path: /Audio/Effects/alert.ogg
|
||||
type: Gravity
|
||||
@@ -233,6 +235,18 @@ entities:
|
||||
pos: -0.5,3.5
|
||||
parent: 292
|
||||
type: Transform
|
||||
- proto: ArrivalsShuttleTimer
|
||||
entities:
|
||||
- uid: 294
|
||||
components:
|
||||
- pos: -4.5,3.5
|
||||
parent: 292
|
||||
type: Transform
|
||||
- uid: 295
|
||||
components:
|
||||
- pos: 3.5,3.5
|
||||
parent: 292
|
||||
type: Transform
|
||||
- proto: AtmosDeviceFanTiny
|
||||
entities:
|
||||
- uid: 164
|
||||
|
||||
@@ -35,6 +35,8 @@ entities:
|
||||
tiles: FwAAABcAAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAAABFAAAAXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXAAAARQAAAF8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwAAAEUAAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAAAAXAAAAXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFAAAAFwAAAEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQAAABcAAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUAAAAXAAAARQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFAAAAFwAAAF8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwAAABcAAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8AAABfAAAAXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFAAAAXwAAAF4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQAAAF8AAABeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8AAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
type: MapGrid
|
||||
- type: Broadphase
|
||||
- type: DeviceNetwork
|
||||
transmitFrequencyId: ShuttleTimer
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
fixedRotation: False
|
||||
@@ -2252,6 +2254,18 @@ entities:
|
||||
type: Transform
|
||||
- powerLoad: 0
|
||||
type: ApcPowerReceiver
|
||||
- proto: Screen
|
||||
entities:
|
||||
- uid: 411
|
||||
components:
|
||||
- pos: -1.5,0.5
|
||||
parent: 410
|
||||
type: Transform
|
||||
- uid: 412
|
||||
components:
|
||||
- pos: -1.5,10.5
|
||||
parent: 410
|
||||
type: Transform
|
||||
- proto: ShuttleWindow
|
||||
entities:
|
||||
- uid: 21
|
||||
|
||||
@@ -755,6 +755,13 @@ entities:
|
||||
pos: -5.5,-14.5
|
||||
parent: 179
|
||||
type: Transform
|
||||
- proto: ArrivalsShuttleTimer
|
||||
entities:
|
||||
- uid: 1191
|
||||
components:
|
||||
- pos: -7.5,5.5
|
||||
parent: 179
|
||||
type: Transform
|
||||
- proto: AtmosDeviceFanTiny
|
||||
entities:
|
||||
- uid: 992
|
||||
@@ -4815,6 +4822,13 @@ entities:
|
||||
- pos: -1.235331,4.739151
|
||||
parent: 179
|
||||
type: Transform
|
||||
- proto: Screen
|
||||
entities:
|
||||
- uid: 1192
|
||||
components:
|
||||
- pos: 4.5,-14.5
|
||||
parent: 179
|
||||
type: Transform
|
||||
- proto: SeedExtractor
|
||||
entities:
|
||||
- uid: 65
|
||||
|
||||
@@ -32647,6 +32647,18 @@ entities:
|
||||
- pos: -12.532179,11.55442
|
||||
parent: 1668
|
||||
type: Transform
|
||||
- proto: Screen
|
||||
entities:
|
||||
- uid: 6988
|
||||
components:
|
||||
- pos: 33.5,3.5
|
||||
parent: 1668
|
||||
type: Transform
|
||||
- uid: 6989
|
||||
components:
|
||||
- pos: 33.5,-4.5
|
||||
parent: 1668
|
||||
type: Transform
|
||||
- proto: SecurityTechFab
|
||||
entities:
|
||||
- uid: 2874
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
- type: deviceFrequency
|
||||
id: ShuttleTimer
|
||||
name: device-frequency-prototype-name-shuttle-timer
|
||||
frequency: 2450
|
||||
|
||||
- type: deviceFrequency
|
||||
id: ArrivalsShuttleTimer
|
||||
name: device-frequency-prototype-name-arrivals-shuttle-timer
|
||||
frequency: 2451
|
||||
|
||||
- type: deviceFrequency
|
||||
id: SurveillanceCamera
|
||||
name: device-frequency-prototype-name-surveillance-camera
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
- type: entity
|
||||
id: Screen
|
||||
name: screen
|
||||
description: Displays text or time.
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: WallMount
|
||||
arc: 360
|
||||
- type: InteractionOutline
|
||||
- type: Appearance
|
||||
- type: Rotatable
|
||||
- type: TextScreenVisuals
|
||||
textOffset: 0,3
|
||||
timerOffset: 0,-4
|
||||
rows: 2
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
sprite: Structures/Wallmounts/screen.rsi
|
||||
state: screen
|
||||
noRot: true
|
||||
- type: Construction
|
||||
graph: Timer
|
||||
node: screen
|
||||
- type: ApcPowerReceiver
|
||||
powerLoad: 100
|
||||
- type: Electrified
|
||||
enabled: false
|
||||
usesApcPower: true
|
||||
- type: ExtensionCableReceiver
|
||||
- type: ShuttleTimer
|
||||
- type: DeviceNetwork
|
||||
receiveFrequencyId: ShuttleTimer
|
||||
|
||||
- type: entity
|
||||
id: ArrivalsShuttleTimer
|
||||
parent: Screen
|
||||
name: arrivals screen
|
||||
components:
|
||||
- type: DeviceNetwork
|
||||
receiveFrequencyId: ArrivalsShuttleTimer
|
||||
@@ -51,10 +51,14 @@
|
||||
- type: SignalTimer
|
||||
canEditLabel: true
|
||||
- type: TextScreenVisuals
|
||||
color: FloralWhite
|
||||
textOffset: 0,8
|
||||
timerOffset: 0,8
|
||||
textLength: 5
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
sprite: Structures/Wallmounts/textscreen.rsi
|
||||
state: textscreen
|
||||
sprite: Structures/Wallmounts/signalscreen.rsi
|
||||
state: signalscreen
|
||||
noRot: true
|
||||
- type: Construction
|
||||
graph: Timer
|
||||
@@ -88,8 +92,8 @@
|
||||
anchored: true
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
sprite: Structures/Wallmounts/textscreen.rsi
|
||||
state: textscreen
|
||||
sprite: Structures/Wallmounts/signalscreen.rsi
|
||||
state: signalscreen
|
||||
- type: Construction
|
||||
graph: Timer
|
||||
node: frame
|
||||
|
||||
@@ -852,8 +852,8 @@
|
||||
category: construction-category-utilities
|
||||
description: "A wallmounted timer for sending timed signals to things. This one has a screen for displaying text."
|
||||
icon:
|
||||
sprite: Structures/Wallmounts/textscreen.rsi
|
||||
state: textscreen
|
||||
sprite: Structures/Wallmounts/signalscreen.rsi
|
||||
state: signalscreen
|
||||
objectType: Structure
|
||||
canRotate: false
|
||||
placementMode: SnapgridCenter
|
||||
@@ -870,8 +870,8 @@
|
||||
category: construction-category-utilities
|
||||
description: "A wallmounted timer for sending timed signals to things. This one has a screen for displaying text and requires security access to use."
|
||||
icon:
|
||||
sprite: Structures/Wallmounts/textscreen.rsi
|
||||
state: textscreen
|
||||
sprite: Structures/Wallmounts/signalscreen.rsi
|
||||
state: signalscreen
|
||||
objectType: Structure
|
||||
canRotate: false
|
||||
placementMode: SnapgridCenter
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "From vgstation: https://github.com/vgstation-coders/vgstation13/commit/a7290010020e541ed6b57817a07023ca6bef26fe#diff-20395160138bed693d15eee6f16d671531b5fa533ec52c50e8df6d52370dbecd",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "screen"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Structures/Wallmounts/screen.rsi/screen.png
Normal file
BIN
Resources/Textures/Structures/Wallmounts/screen.rsi/screen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 266 B |
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "textscreen"
|
||||
"name": "signalscreen"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 870 B After Width: | Height: | Size: 870 B |
Reference in New Issue
Block a user