using Content.Shared._Sunrise.Footprints; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Random; namespace Content.Client._Sunrise.Footprints; /// /// Handles the visual appearance and updates of footprint entities on the client /// public sealed class FootprintVisualizerSystem : VisualizerSystem { [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; /// public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnFootprintInitialized); SubscribeLocalEvent(OnFootprintShutdown); } /// /// Initializes the visual appearance of a new footprint /// private void OnFootprintInitialized(EntityUid uid, FootprintComponent component, ComponentInit args) { if (!TryComp(uid, out var sprite)) return; InitializeSpriteLayers(sprite); UpdateFootprintVisuals(uid, component, sprite); } /// /// Cleans up the visual elements when a footprint is removed /// private void OnFootprintShutdown(EntityUid uid, FootprintComponent component, ComponentShutdown args) { if (!TryComp(uid, out var sprite)) return; RemoveSpriteLayers(sprite); } /// /// Sets up the initial sprite layers for the footprint /// private void InitializeSpriteLayers(SpriteComponent sprite) { sprite.LayerMapReserveBlank(FootprintSpriteLayer.MainLayer); } /// /// Removes sprite layers when cleaning up footprint /// private void RemoveSpriteLayers(SpriteComponent sprite) { if (sprite.LayerMapTryGet(FootprintSpriteLayer.MainLayer, out var layer)) { sprite.RemoveLayer(layer); } } /// /// Updates the visual appearance of a footprint based on its current state /// private void UpdateFootprintVisuals(EntityUid uid, FootprintComponent footprint, SpriteComponent sprite) { if (!sprite.LayerMapTryGet(FootprintSpriteLayer.MainLayer, out var layer) || !TryComp(footprint.CreatorEntity, out var emitterComponent) || !TryComp(uid, out var appearance)) return; if (!_appearanceSystem.TryGetData( uid, FootprintVisualParameter.VisualState, out var visualType, appearance)) return; UpdateSpriteState(sprite, layer, visualType, emitterComponent); UpdateSpriteColor(sprite, layer, uid, appearance); } /// /// Updates the sprite state based on the footprint type /// private void UpdateSpriteState( SpriteComponent sprite, int layer, FootprintVisualType visualType, FootprintEmitterComponent emitter) { var stateId = new RSI.StateId(GetStateId(visualType, emitter)); sprite.LayerSetState(layer, stateId, emitter.SpritePath); } /// /// Determines the appropriate state ID for the footprint based on its type /// private string GetStateId(FootprintVisualType visualType, FootprintEmitterComponent emitter) { return visualType switch { FootprintVisualType.BareFootprint => emitter.IsRightStep ? _random.Pick(emitter.RightBareFootState) : _random.Pick(emitter.LeftBareFootState), FootprintVisualType.ShoeFootprint => _random.Pick(emitter.ShoeFootState), FootprintVisualType.SuitFootprint => _random.Pick(emitter.PressureSuitFootState), FootprintVisualType.DragMark => _random.Pick(emitter.DraggingStates), _ => throw new ArgumentOutOfRangeException( $"Unknown footprint visual type: {visualType}") }; } /// /// Updates the sprite color based on appearance data /// private void UpdateSpriteColor( SpriteComponent sprite, int layer, EntityUid uid, AppearanceComponent appearance) { if (_appearanceSystem.TryGetData(uid, FootprintVisualParameter.TrackColor, out var color, appearance)) { sprite.LayerSetColor(layer, color); } } /// protected override void OnAppearanceChange( EntityUid uid, FootprintComponent component, ref AppearanceChangeEvent args) { if (args.Sprite is not { } sprite) return; UpdateFootprintVisuals(uid, component, sprite); } }