Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: 2025 jhrushbe <capnmerry@gmail.com>
// SPDX-FileCopyrightText: 2025 rottenheadphones <juaelwe@outlook.com>
// SPDX-FileCopyrightText: 2025 taydeo <td12233a@gmail.com>
//
// SPDX-License-Identifier: CC-BY-NC-SA-3.0

using Content.Client.NodeContainer;
using Content.Shared._CorvaxGoob.Power.Generation.FissionGenerator;
using Robust.Shared.Map;
using Content.Client.Examine;
using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;

namespace Content.Client._CorvaxGoob.Power.Generation.FissionGenerator;

public sealed class NuclearReactorSystem : SharedNuclearReactorSystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<NuclearReactorComponent, ComponentInit>(OnInit);

SubscribeLocalEvent<NuclearReactorComponent, ClientExaminedEvent>(ReactorExamined);
SubscribeLocalEvent<NuclearReactorComponent, AppearanceChangeEvent>(OnAppearanceChange);
}

private void OnInit(EntityUid uid, NuclearReactorComponent comp, ref ComponentInit args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;

if (!_resourceCache.TryGetResource("/Textures/_CorvaxGoob/Structures/Power/Generation/FissionGenerator/reactor_component_cap.rsi", out RSIResource? resource))
return;

Entity<SpriteComponent?> entSprite = (uid, sprite);
var xspace = comp.Gridbounds[0] / 32f;
var yspace = comp.Gridbounds[1] / 32f;
var xoff = comp.Gridbounds[2] / 32f;
var yoff = comp.Gridbounds[3] / 32f;

var gridWidth = comp.ReactorGridWidth;
var gridHeight = comp.ReactorGridHeight;

var xAdj = (gridWidth - 1) / 2f;
var yAdj = (gridHeight - 1) / 2f;

for (var x = 0; x < gridWidth; x++)
{
for (var y = 0; y < gridHeight; y++)
{
var layerID = _sprite.AddRsiLayer(entSprite, "empty_cap", resource.RSI);
_sprite.LayerMapSet(entSprite, FormatMap(x, y), layerID);
_sprite.LayerSetOffset(entSprite, layerID, new((xspace * (y - yAdj)) - xoff, (-yspace * (x - xAdj)) - yoff));
_sprite.LayerSetColor(entSprite, layerID, Color.Black);
}
}
}

private static string FormatMap(int x, int y) => "NuclearReactorCap" + x + "/" + y;

private void ReactorExamined(EntityUid uid, NuclearReactorComponent comp, ClientExaminedEvent args) => Spawn(comp.ArrowPrototype, new EntityCoordinates(uid, 0, 0));

private void OnAppearanceChange(EntityUid uid, NuclearReactorComponent comp, ref AppearanceChangeEvent args)
{
for (var x = 0; x < comp.ReactorGridWidth; x++)
{
for (var y = 0; y < comp.ReactorGridHeight; y++)
{
if(comp.VisualData.TryGetValue(new(x,y), out var data))
UpdateRodAppearance(uid, FormatMap(x,y), data.cap, data.color);
else
UpdateRodAppearance(uid, FormatMap(x, y), "empty_cap", Color.Black);
}
}
}

private void UpdateRodAppearance(EntityUid uid, string map, string state, Color color)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;

Entity<SpriteComponent?> entSprite = (uid, sprite);

if (!_sprite.LayerMapTryGet(entSprite, map, out var layer, false))
return;

_sprite.LayerSetRsiState(entSprite, layer, state);
_sprite.LayerSetColor(entSprite, layer, color);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2025 jhrushbe <capnmerry@gmail.com>
// SPDX-FileCopyrightText: 2025 rottenheadphones <juaelwe@outlook.com>
// SPDX-FileCopyrightText: 2025 taydeo <td12233a@gmail.com>
//
// SPDX-License-Identifier: CC-BY-NC-SA-3.0

using Content.Shared._CorvaxGoob.Power.Generation.FissionGenerator;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;

namespace Content.Client._CorvaxGoob.Power.Generation.FissionGenerator;

public sealed class ReactorPartSystem : SharedReactorPartSystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ReactorPartComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<ReactorPartComponent, ComponentInit>(OnComponentInit);
}

private void OnAppearanceChange(EntityUid uid, ReactorPartComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;

// Re-enable if/when there are multiple sprites
//if (!_sprite.LayerMapTryGet((uid, args.Sprite), ReactorCapVisualLayers.Sprite, out var layer, false))
// return;

_sprite.LayerSetColor((uid, args.Sprite), 0, _proto.Index(component.Material).Color);
}

private void OnComponentInit(Entity<ReactorPartComponent> ent, ref ComponentInit args)
=> _sprite.LayerSetColor((ent.Owner, EntityManager.GetComponent<SpriteComponent>(ent.Owner)), 0, _proto.Index(ent.Comp.Material).Color);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using Robust.Shared.Map;
using Robust.Client.GameObjects;
using Content.Shared.Repairable;
using Content.Shared._CorvaxGoob.Power.Generation.FissionGenerator;
using Content.Client.Popups;
using Content.Client.Examine;
using Robust.Client.Animations;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Popups;

namespace Content.Client._CorvaxGoob.Power.Generation.FissionGenerator;

// Ported and modified from goonstation by Jhrushbe.
// CC-BY-NC-SA-3.0
// https://github.com/goonstation/goonstation/blob/ff86b044/code/obj/nuclearreactor/turbine.dm

public sealed class TurbineSystem : SharedTurbineSystem
{
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;

private readonly float _threshold = 1f;
private float _accumulator = 0;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<TurbineComponent, ClientExaminedEvent>(TurbineExamined);

SubscribeLocalEvent<TurbineComponent, AnimationCompletedEvent>(OnAnimationCompleted);

SubscribeLocalEvent<TurbineComponent, ItemSlotInsertAttemptEvent>(OnInsertAttempt);
SubscribeLocalEvent<TurbineComponent, ItemSlotEjectAttemptEvent>(OnEjectAttempt);
}

protected override void OnRepairTurbineFinished(EntityUid uid, TurbineComponent comp, ref RepairDoAfterEvent args)
{
if (args.Cancelled)
return;

_popupSystem.PopupClient(Loc.GetString("turbine-repair", ("target", uid), ("tool", args.Used!)), uid, args.User);
}

private void TurbineExamined(EntityUid uid, TurbineComponent comp, ClientExaminedEvent args) => Spawn(comp.ArrowPrototype, new EntityCoordinates(uid, 0, 0));

#region Animation
private void OnAnimationCompleted(EntityUid uid, TurbineComponent comp, ref AnimationCompletedEvent args) => PlayAnimation(uid, comp);

public override void FrameUpdate(float frameTime)
{
_accumulator += frameTime;
if (_accumulator >= _threshold)
{
AccUpdate();
_accumulator = 0;
}
}

private void AccUpdate()
{
var query = EntityQueryEnumerator<TurbineComponent>();
while (query.MoveNext(out var uid, out var component))
{
// Makes sure the anim doesn't get stuck at low RPM
PlayAnimation(uid, component);
}
}

private void PlayAnimation(EntityUid uid, TurbineComponent comp)
{
if (!TryComp<SpriteComponent>(uid, out var sprite) || !_sprite.TryGetLayer((uid,sprite), TurbineVisualLayers.TurbineSpeed, out var layer, false))
return;

var state = "speedanim";
if (comp.RPM < 1)
{
_animationPlayer.Stop(uid, state);
_sprite.LayerSetRsiState(layer, "turbine");
comp.AnimRPM = -comp.BestRPM; // Primes it to start the instant it's spinning again
return;
}

if (Math.Abs(comp.RPM - comp.AnimRPM) > comp.BestRPM * 0.1)
_animationPlayer.Stop(uid, state); // Current anim is stale, time for a new one

if (_animationPlayer.HasRunningAnimation(uid, state))
return;

comp.AnimRPM = comp.RPM;
var layerKey = TurbineVisualLayers.TurbineSpeed;
var time = 0.5f * comp.BestRPM / comp.RPM;
var timestep = time / 12;
var animation = new global::Robust.Client.Animations.Animation
{
Length = TimeSpan.FromSeconds(time),
AnimationTracks =
{
new AnimationTrackSpriteFlick
{
LayerKey = layerKey,
KeyFrames =
{
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_00", 0),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_01", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_02", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_03", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_04", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_05", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_06", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_07", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_08", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_09", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_10", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_11", timestep)
}
}
}
};
_sprite.LayerSetVisible(layer, true);
_animationPlayer.Play(uid, animation, state);
}
#endregion

private void OnEjectAttempt(EntityUid uid, TurbineComponent comp, ref ItemSlotEjectAttemptEvent args)
{
if (args.Cancelled)
return;

if (comp.RPM < 1)
return;

args.Cancelled = true;
}

private void OnInsertAttempt(EntityUid uid, TurbineComponent comp, ref ItemSlotInsertAttemptEvent args)
{
if (args.Cancelled)
return;

if (comp.RPM < 1)
return;

args.Cancelled = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: 2025 jhrushbe <capnmerry@gmail.com>
// SPDX-FileCopyrightText: 2025 rottenheadphones <juaelwe@outlook.com>
// SPDX-FileCopyrightText: 2025 taydeo <td12233a@gmail.com>
//
// SPDX-License-Identifier: CC-BY-NC-SA-3.0

using System.Numerics;
using Content.Client.UserInterface;
using Content.Shared._CorvaxGoob.Power.Generation.FissionGenerator;
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.IdentityManagement;
using JetBrains.Annotations;
using Robust.Client.Timing;
using Robust.Client.UserInterface;

namespace Content.Client._CorvaxGoob.Power.UI;

/// <summary>
/// Initializes a <see cref="NuclearReactorWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public sealed class NuclearReactorBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IEntityManager _entityManager = default!;

[ViewVariables]
private NuclearReactorWindow? _window;

public NuclearReactorBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}

protected override void Open()
{
EntityUid? reactorUid = null;
if (_entityManager.TryGetComponent<NuclearReactorMonitorComponent>(Owner, out var reactorMonitorComponent))
{
if (!_entityManager.TryGetEntity(reactorMonitorComponent.reactor, out reactorUid) || reactorUid == null
|| !_entityManager.TryGetComponent<NuclearReactorComponent>(reactorUid, out var monitoredReactorComponent) || monitoredReactorComponent.Melted)
return;
}
else if (!_entityManager.TryGetComponent<NuclearReactorComponent>(Owner, out var reactorComponent) || reactorComponent.Melted)
return;

base.Open();

_window = this.CreateWindow<NuclearReactorWindow>();
if (_entityManager.EntityExists(reactorUid))
_window.SetEntity(reactorUid.Value, Owner);
else
_window.SetEntity(Owner);

_window.ItemActionButtonPressed += OnActionButtonPressed;
_window.EjectButtonPressed += OnEjectButtonPressed;
_window.ControlRodModify += OnControlRodModify;

Update();
}

protected override void UpdateState(BoundUserInterfaceState state)
{
if (state is not NuclearReactorBuiState reactorState)
return;

_window?.Update(reactorState);
}

private void OnActionButtonPressed(Vector2d vector)
{
if (_window is null ) return;

SendPredictedMessage(new ReactorItemActionMessage(vector));
}

private void OnEjectButtonPressed()
{
if (_window is null) return;

SendPredictedMessage(new ReactorEjectItemMessage());
}

private void OnControlRodModify(float amount)
{
if (_window is null) return;

SendPredictedMessage(new ReactorControlRodModifyMessage(amount));
}
}
Loading
Loading