Grid/Base/GridMovement.cs
using Sandbox;
using Sandbox.Sboku;
using SWB.Demo;
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Numerics;

public sealed class GridMovement : Component
{
    [Property] public float GridSize { get; set; } = 128f;
    [Property] public float MoveDuration { get; set; } = 0.15f;
    [Property] public float TurnDuration { get; set; } = 0.1f;
    [Property] public float StepCooldown { get; set; } = 0.15f;
    [Property] public float SnapBackDuration { get; set; } = 0.1f;

    [Property] public bool IgnoreInput { get; set; } = false;

    private GridMap GridMap;
    private DemoPlayer Player;

    private Component mover;
    private GameTransform moverTransform;

    private Vector3 moveFrom;
    private Vector3 moveTo;
    private float moveT;

    private Rotation turnFrom;
    private Rotation turnTo;
    private float turnT;
    private bool isTurning;

    private float stepTimer;

    private Vector3 safeGridPosition;
    private float safeYaw;

    private bool snapInProgress;
    private Vector3 snapStartPos;
    private float snapStartYaw;
    private float snapT;

    private float moveStartYaw;
    private float moveTargetYaw;
    
    protected override void OnStart()
    {
        Player = Components.Get<DemoPlayer>();

        if (Player == null)
        {
            Enabled = false; // 🔑 älä käytä GridMovementia botille
            return;
        }
        GridMap = Scene.GetComponentInChildren<GridSpawner>()?.Map;


        moverTransform = Player.Transform;

        safeGridPosition = moverTransform.Position;
        safeYaw = moverTransform.Rotation.Angles().yaw;
    }

    protected override void OnUpdate()
    {
        if (stepTimer > 0f) stepTimer -= Time.Delta;

        if (snapInProgress) { AnimateSnapBack(); return; }
        if (isTurning) { AnimateTurn(); return; }
        if (moveT > 0f) { AnimateMove(); return; }

        if (Player != null && !Player.IsProxy && !Player.IsBot)
            HandlePlayerInput();
    }

    public bool Move(Vector2Int dir, bool shouldTurn)
    {
        if (stepTimer > 0f || dir == Vector2Int.Zero)
            return false;

        StartMove(new Vector3(dir.x, dir.y, 0), shouldTurn);
        stepTimer = StepCooldown;

        return true;
    }

    private void HandlePlayerInput()
    {
        if (IgnoreInput) return;

        Vector3 dir = Vector3.Zero;
        bool shouldTurn = false;

        if (Input.Pressed("forward")) { dir = GetCameraGridForward(); shouldTurn = true; }
        else if (Input.Pressed("backward")) { dir = -GetCameraGridForward(); }
        else if (Input.Pressed("left")) { dir = -GetCameraGridRight(); }
        else if (Input.Pressed("right")) { dir = GetCameraGridRight(); }

        if (dir != Vector3.Zero)
            Move(new Vector2Int((int)dir.x, (int)dir.y), shouldTurn);

        if (Input.Pressed("turnleft")) TurnBy(90);
        if (Input.Pressed("turnright")) TurnBy(-90);
    }

    private void StartMove(Vector3 dir, bool shouldTurn)
    {
        var currentGrid = GridMap.WorldToGrid(moverTransform.Position);

        int gx = currentGrid.x + (int)dir.x;
        int gy = currentGrid.y + (int)dir.y;

        moveFrom = GridMap.GridToWorld(currentGrid.x, currentGrid.y, moverTransform.Position.z);

        if (!CanMoveTo(gx, gy))
        {
            snapStartPos = moverTransform.Position;
            snapStartYaw = moverTransform.Rotation.Angles().yaw;
            snapT = 0f;
            snapInProgress = true;
            moveTo = safeGridPosition;
            moveTargetYaw = snapStartYaw;
            return;
        }

        moveTo = GridMap.GridToWorld(gx, gy, moverTransform.Position.z);
        moveStartYaw = moverTransform.Rotation.Angles().yaw;
        moveTargetYaw = shouldTurn ? MathF.Atan2(dir.y, dir.x) * 180f / MathF.PI : moveStartYaw;
        moveT = 0.001f;



    }

    private bool CanMoveTo(int x, int y)
    {
        if (!GridMap.InBounds(x, y))
            return false;

        // Seinät estävät aina liikkumisen
        if (GridMap.IsWall(x, y))
            return false;

        // Tarkistetaan onko ruudussa ovi
        var door = Scene.GetAllComponents<DoorComponent>()
                        .FirstOrDefault(d => d.GridX == x && d.GridY == y);
        if (GridMap.IsDoor(x, y))
            return door != null && door.State == DoorComponent.DoorState.Open; // Liikkuu vain jos ovi on auki

        // 🔹 Tarkistetaan onko ruudussa aktivoitu ansa
        var trap = Scene.GetAllComponents<TrapComponent>()
                        .FirstOrDefault(t => t.GridX == x && t.GridY == y);
        if (trap != null && trap.IsActive) // lisätään IsActive property TrapComponentiin
            return false;

        // Tyhjät ruudut ja painikkeet läpäiseviä
        return true;
    }

    private void AnimateMove()
    {
        moveT = MathF.Min(moveT + Time.Delta / MoveDuration, 1f);
        SetPosition(Vector3.Lerp(moveFrom, moveTo, moveT));

        float yaw = moveStartYaw + DeltaAngle(moveStartYaw, moveTargetYaw) * moveT;
        moverTransform.Rotation = Rotation.FromYaw(yaw);

        if (moveT >= 1f)
        {
            moveT = 0f;
            safeGridPosition = moveTo;
            safeYaw = moveTargetYaw;
        }
    }

    private void AnimateTurn()
    {
        turnT += Time.Delta / TurnDuration;
        turnT = MathF.Min(turnT, 1f);

        moverTransform.Rotation = Rotation.Slerp(turnFrom, turnTo, turnT);

        if (Player != null)
        {
            Player.EyeAngles = moverTransform.Rotation.Angles();
        }

        if (turnT >= 1f)
        {
            isTurning = false;

            // varmista lopullinen synkka
            if (Player != null)
                Player.EyeAngles = moverTransform.Rotation.Angles();
        }
    }

    private void AnimateSnapBack()
    {
        snapT = MathF.Min(snapT + Time.Delta / SnapBackDuration, 1f);
        SetPosition(Vector3.Lerp(snapStartPos, moveTo, snapT));
        moverTransform.Rotation = Rotation.FromYaw(
            snapStartYaw + DeltaAngle(snapStartYaw, moveTargetYaw) * snapT
        );
        if (snapT >= 1f) snapInProgress = false;
    }

    private void TurnBy(float degrees)
    {
        if (isTurning) return;
        turnFrom = moverTransform.Rotation;
        turnTo = Rotation.FromYaw(turnFrom.Angles().yaw + degrees);
        turnT = 0f;
        isTurning = true;
    }

    private Vector3 GetCameraGridForward()
    {
        float yaw = Player != null ? Player.EyeAngles.yaw : moverTransform.Rotation.Angles().yaw;
        yaw = MathF.Round(yaw / 90f) * 90f;
        float rad = yaw * MathF.PI / 180f;
        return new Vector3(MathF.Round(MathF.Cos(rad)), MathF.Round(MathF.Sin(rad)), 0);
    }

    private Vector3 GetCameraGridRight()
    {
        var fwd = GetCameraGridForward();
        return new Vector3(fwd.y, -fwd.x, 0);
    }

    private float DeltaAngle(float a, float b)
    {
        float delta = (b - a) % 360f;
        if (delta > 180f) delta -= 360f;
        if (delta < -180f) delta += 360f;
        return delta;
    }

    private void SetPosition(Vector3 pos)
    {
        var cc = Components.Get<CharacterController>();
        if (cc != null) cc.WorldPosition = pos;
        else moverTransform.Position = pos;
    }

    public void SnapToGrid()
    {
        if (GridMap == null)
            return;

        var g = GridMap.WorldToGrid(moverTransform.Position);
        var snapped = GridMap.GridToWorld(g.x, g.y, moverTransform.Position.z);

        SetPosition(snapped);

        safeGridPosition = snapped;
        safeYaw = moverTransform.Rotation.Angles().yaw;
    }
    public Vector2Int GetCurrentGrid()
    {
        var g = GridMap.WorldToGrid(moverTransform.Position);
        return new Vector2Int(g.x, g.y);
    }

    public void EnterFreeMode()
    {
        IgnoreInput = true;

        // Katkaise kaikki grid-animaatiot
        moveT = 0f;
        turnT = 0f;
        isTurning = false;
        snapInProgress = false;
    }

    public void EnterGridMode()
    {
        IgnoreInput = false;

        // Pakota takaisin gridiin
        SnapToGrid();

        // Lukitse yaw 90° välein
        float yaw = moverTransform.Rotation.Angles().yaw;
        yaw = MathF.Round(yaw / 90f) * 90f;

        moverTransform.Rotation = Rotation.FromYaw(yaw);

        if (Player != null)
            Player.EyeAngles = moverTransform.Rotation.Angles();

        // Nollaa liike
        moveT = 0f;
        turnT = 0f;
        isTurning = false;
        snapInProgress = false;
    }
}