using Content.Client.Rotation; using Content.Shared.Buckle; using Content.Shared.Buckle.Components; using Content.Shared.Movement.Systems; using Content.Shared.Rotation; using Robust.Client.GameObjects; using Robust.Client.Graphics; namespace Content.Client.Buckle; internal sealed class BuckleSystem : SharedBuckleSystem { [Dependency] private readonly RotationVisualizerSystem _rotationVisualizerSystem = default!; [Dependency] private readonly IEyeManager _eye = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; [Dependency] private readonly SpriteSystem _sprite = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnAppearanceChange); SubscribeLocalEvent(OnStrapMoveEvent); SubscribeLocalEvent(OnBuckledEvent); SubscribeLocalEvent(OnUnbuckledEvent); SubscribeLocalEvent(OnMobCollide); } private void OnMobCollide(Entity ent, ref AttemptMobCollideEvent args) { if (ent.Comp.Buckled) { args.Cancelled = true; } } private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args) { // I'm moving this to the client-side system, but for the sake of posterity let's keep this comment: // > This is mega cursed. Please somebody save me from Mr Buckle's wild ride // The nice thing is its still true, this is quite cursed, though maybe not omega cursed anymore. // This code is garbage, it doesn't work with rotated viewports. I need to finally get around to reworking // sprite rendering for entity layers & direction dependent sorting. // Future notes: // Right now this doesn't handle: other grids, other grids rotating, the camera rotation changing, and many other fun rotation specific things // The entire thing should be a concern of the engine, or something engine helps to implement properly. // Give some of the sprite rotations their own drawdepth, maybe as an offset within the rsi, or something like this // And we won't ever need to set the draw depth manually if (args.NewRotation == args.OldRotation) return; if (!TryComp(uid, out var strapSprite)) return; var angle = _xformSystem.GetWorldRotation(uid) + _eye.CurrentEye.Rotation; // Get true screen position, or close enough var isNorth = angle.GetCardinalDir() == Direction.North; foreach (var buckledEntity in component.BuckledEntities) { if (!TryComp(buckledEntity, out var buckle)) continue; if (!TryComp(buckledEntity, out var buckledSprite)) continue; if (isNorth) { // This will only assign if empty, it won't get overwritten by new depth on multiple calls, which do happen easily buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth; _sprite.SetDrawDepth((buckledEntity, buckledSprite), strapSprite.DrawDepth - 1); } else if (buckle.OriginalDrawDepth.HasValue) { _sprite.SetDrawDepth((buckledEntity, buckledSprite), buckle.OriginalDrawDepth.Value); buckle.OriginalDrawDepth = null; } } } /// /// Lower the draw depth of the buckled entity without needing for the strap entity to rotate/move. /// Only do so when the entity is facing screen-local north /// private void OnBuckledEvent(Entity ent, ref BuckledEvent args) { if (!TryComp(args.Strap, out var strapSprite)) return; if (!TryComp(ent.Owner, out var buckledSprite)) return; var angle = _xformSystem.GetWorldRotation(args.Strap) + _eye.CurrentEye.Rotation; // Get true screen position, or close enough if (angle.GetCardinalDir() != Direction.North) return; ent.Comp.OriginalDrawDepth ??= buckledSprite.DrawDepth; _sprite.SetDrawDepth((ent.Owner, buckledSprite), strapSprite.DrawDepth - 1); } /// /// Was the draw depth of the buckled entity lowered? Reset it upon unbuckling. /// private void OnUnbuckledEvent(Entity ent, ref UnbuckledEvent args) { if (!TryComp(ent.Owner, out var buckledSprite)) return; if (!ent.Comp.OriginalDrawDepth.HasValue) return; _sprite.SetDrawDepth((ent.Owner, buckledSprite), ent.Comp.OriginalDrawDepth.Value); ent.Comp.OriginalDrawDepth = null; } private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args) { if (!TryComp(uid, out var rotVisuals)) return; if (!Appearance.TryGetData(uid, BuckleVisuals.Buckled, out var buckled, args.Component) || !buckled || args.Sprite == null) { _rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation); return; } // Animate strapping yourself to something at a given angle // TODO: Dump this when buckle is better _rotationVisualizerSystem.AnimateSpriteRotation(uid, args.Sprite, rotVisuals.HorizontalRotation, 0.125f); } }