Grid/Base/FloorSpawner.cs
using Sandbox;
using Sandbox.Sboku;
using Sandbox.Sboku.Arena;
using System;
using System.Collections.Generic;
using System.Numerics;

/// <summary>
/// FloorSpawner – vastaa lattian spawnaamisesta maailmaan.
/// Sisältää yhtenäisen lattian spawnauksen (ulko- ja sisäosa),
/// lattiacolliderin luomisen sekä yksittäisten lattiaruutujen spawnauksen (ei käytössä).
/// </summary>
internal class FloorSpawner
{
    private readonly GridSpawner owner;

    private GridMap Map => owner.Map;
    private Model BoxModel => owner.BoxModel;
    private Material FloorIndoorMaterial => owner.FloorIndoorMaterial;
    private Material FloorOutdoorMaterial => owner.FloorOutdoorMaterial;
    private float FloorZ => owner.FloorZ;

    public FloorSpawner(GridSpawner owner)
    {
        this.owner = owner;
    }

    /// <summary>
    /// Spawnaa yhden ison ulkolattian ja erilliset sisälattiat jokaiselle huoneelle.
    /// </summary>
    public void SpawnFloorSingle()
    {
        if (!Networking.IsHost)
            return;

        float thickness = 4f;

        // =========================
        // OUTDOOR FLOOR
        // =========================
        {
            var go = new GameObject("Floor_Outdoor");

            float width = Map.Width * Map.GridSize;
            float height = Map.Height * Map.GridSize;

            float scaleX = width / BoxModel.Bounds.Size.x;
            float scaleY = height / BoxModel.Bounds.Size.y;
            float scaleZ = thickness / BoxModel.Bounds.Size.z;

            var floorMat = FloorOutdoorMaterial.CreateCopy();
            floorMat.Set("g_vTexCoordScale", new Vector2(Map.Width, Map.Height));

            var renderer = go.Components.Create<ModelRenderer>();
            renderer.Model = BoxModel;
            renderer.MaterialOverride = floorMat;

            go.Transform.Scale = new Vector3(scaleX, scaleY, scaleZ);

            Vector3 min = Map.GridToWorld(0, 0, 0f);
            Vector3 max = Map.GridToWorld(Map.Width - 1, Map.Height - 1, 0f);

            Vector3 center = (min + max) * 0.5f;
            center.z = FloorZ - thickness * 0.5f;

            go.Transform.Position = center;

            go.NetworkSpawn();
        }

        // =========================
        // INDOOR FLOORS (korjattu keskitys)
        // =========================

        var rooms = Map.FindRooms();

        foreach (var room in rooms)
        {
            var go = new GameObject("Floor_Indoor");

            int roomWidth = room.maxX - room.minX + 1;
            int roomHeight = room.maxY - room.minY + 1;

            float width = roomWidth * Map.GridSize;
            float height = roomHeight * Map.GridSize;

            float scaleX = width / BoxModel.Bounds.Size.x;
            float scaleY = height / BoxModel.Bounds.Size.y;
            float scaleZ = thickness / BoxModel.Bounds.Size.z;

            var floorMat = FloorIndoorMaterial.CreateCopy();
            floorMat.Set("g_vTexCoordScale", new Vector2(roomWidth, roomHeight));

            var renderer = go.Components.Create<ModelRenderer>();
            renderer.Model = BoxModel;
            renderer.MaterialOverride = floorMat;

            go.Transform.Scale = new Vector3(scaleX, scaleY, scaleZ);

            // 🔥 Lasketaan world min/max huoneelle
            Vector3 min = Map.GridToWorld(room.minX, room.minY, 0f);
            Vector3 max = Map.GridToWorld(room.maxX, room.maxY, 0f);

            Vector3 center = (min + max) * 0.5f;

            // 🔥 pieni sisäänveto ettei osu seinän sisään
            float inset = Map.GridSize * 0.02f;

            width -= inset * 2f;
            height -= inset * 2f;

            scaleX = width / BoxModel.Bounds.Size.x;
            scaleY = height / BoxModel.Bounds.Size.y;

            go.Transform.Scale = new Vector3(scaleX, scaleY, scaleZ);

            center.z = FloorZ - thickness * 0.5f + 2f;

            go.Transform.Position = center;

            go.NetworkSpawn();
        }
    }

    /// <summary>
    /// Spawnaa yhden ison staattisen colliderin koko lattian alle.
    /// </summary>
    public void SpawnSingleFloorCollider()
    {
        var go = new GameObject("FloorCollider");
        var collider = go.AddComponent<BoxCollider>();
        collider.Static = true;

        float sizeX = Map.Width * Map.GridSize;
        float sizeY = Map.Height * Map.GridSize;

        float floorThickness = 32f;
        collider.Scale = new Vector3(sizeX, sizeY, floorThickness);

        var center = Map.GridToWorld(
            Map.Width / 2,
            Map.Height / 2,
            0f
        );
        // 🔑 keskikohta = FloorZ - puolet paksuudesta
        go.Transform.Position = new Vector3(
    center.x,
    center.y,
    FloorZ - floorThickness * 0.5f);
    }

    /// <summary>
    /// Spawnaa yksittäisen lattiaruudun annettuun koordinaattiin (ei käytössä, säilytetään).
    /// </summary>
    public void SpawnFloorTile(int x, int y)
    {
        var go = new GameObject($"Floor_{x}_{y}");
        
        var renderer = go.AddComponent<ModelRenderer>();
        renderer.MaterialOverride = FloorIndoorMaterial;

        var visual = go.AddComponent<MaterialVisual>();
        visual.MaterialPath = owner.FloorIndoorMaterials[owner.FloorIndoorMaterialIndex];

        float scaleXY = Map.GridSize / BoxModel.Bounds.Size.x;
        float scaleZ = 0.05f;
        go.Transform.LocalScale = new Vector3(scaleXY, scaleXY, scaleZ);

        Vector3 pos = Map.GridToWorld(x, y, FloorZ);
       
        go.Transform.Position = pos;

        //go.NetworkSpawn();
    }
}