#if UNITY_EDITOR
// Maeko Explodable Builder (free addon by Maeko Studio, maeko.studio)
// Menu: Maeko > Explodable
//   Make Selected Explodable : turns any object made of child pieces into an explodable
//   Eiffel Tower (Detailed)  : about 3,100 truss pieces
//   Eiffel Tower (Blocks)    : chunky voxel version
//   Leaning Tower of Pisa    : about 800 blocks
//   Castle (Detailed)        : about 3,100 bricks
// This file must live inside a folder named Editor.

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public static class MaekoExplodableBuilder
{
    const string Folder = "Assets/Maeko Studio/Explodable/Materials";

    struct Cell
    {
        public Vector3 pos;  // in block units
        public bool white;
        public Cell(float x, float y, float z, bool w) { pos = new Vector3(x, y, z); white = w; }
    }

    [MenuItem("Maeko/Explodable/Eiffel Tower (Blocks)")]
    static void BuildEiffel() => Build("EXPLODABLE_EiffelTower", EiffelCells(), 0.16f, new Vector3(0, 0.9f, 0));

    [MenuItem("Maeko/Explodable/Leaning Tower of Pisa")]
    static void BuildPisa() => Build("EXPLODABLE_Pisa", PisaCells(), 0.2f, new Vector3(0, 0.8f, 0));

    // ---------- Eiffel Tower: splayed legs with arches, three platforms, lattice upper body, spire ----------
    static List<Cell> EiffelCells()
    {
        var cells = new List<Cell>();
        var used = new HashSet<Vector3Int>();
        const float s = 0.16f;
        const int H = 34;
        const int p1 = 9, p2 = 18, p3 = 26; // platform layers

        void Add(int x, int y, int z, bool w)
        {
            if (used.Add(new Vector3Int(x, y, z))) cells.Add(new Cell(x, y, z, w));
        }

        for (int y = 0; y < H; y++)
        {
            float t = y / (float)(H - 1);
            float halfW = 0.02f + 1.55f * Mathf.Pow(1f - t, 2.4f); // concave taper like the real thing
            int n = Mathf.Max(0, Mathf.RoundToInt(halfW / s));

            // platforms
            if (y == p1 || y == p2 || y == p3)
            {
                int pn = n + 1;
                for (int i = -pn; i <= pn; i++)
                for (int k = -pn; k <= pn; k++)
                    if (y != p1 || Mathf.Max(Mathf.Abs(i), Mathf.Abs(k)) >= pn - 2) // first platform is a ring
                        Add(i, y, k, true);
                continue;
            }

            if (y < p1)
            {
                // four legs
                const int legCells = 2;
                int inner = n - legCells + 1;
                for (int i = -n; i <= n; i++)
                for (int k = -n; k <= n; k++)
                {
                    int ai = Mathf.Abs(i), ak = Mathf.Abs(k);
                    bool leg = ai >= inner && ak >= inner;
                    if (leg) { Add(i, y, k, false); continue; }

                    // arches on each face between the legs
                    bool onFace = (ai == n && ak < inner) || (ak == n && ai < inner);
                    if (!onFace) continue;
                    float along = ai == n ? ak : ai;
                    float span = Mathf.Max(1f, inner - 0.5f);
                    float need = 3f + (p1 - 1 - 3f) * Mathf.Sqrt(Mathf.Max(0f, 1f - (along / span) * (along / span)));
                    if (y >= need) Add(i, y, k, false);
                }
                continue;
            }

            if (n >= 2)
            {
                // corner columns plus diagonal lattice bracing on each face
                for (int i = -n; i <= n; i++)
                for (int k = -n; k <= n; k++)
                {
                    int ai = Mathf.Abs(i), ak = Mathf.Abs(k);
                    bool corner = ai == n && ak == n;
                    bool onFace = ai == n || ak == n;
                    int along = ai == n ? k : i;
                    bool brace = onFace && ((along + y + 64) % 3 == 0 || (-along + y + 64) % 3 == 0);
                    if (corner || brace) Add(i, y, k, false);
                }
            }
            else
            {
                // solid upper shaft
                for (int i = -n; i <= n; i++)
                for (int k = -n; k <= n; k++)
                    Add(i, y, k, false);
            }
        }

        // antenna
        for (int y = H; y < H + 3; y++) Add(0, y, 0, true);
        return cells;
    }

    // ---------- Leaning Tower of Pisa: round hollow tower, arcade openings, orange cornice bands, lean ----------
    static List<Cell> PisaCells()
    {
        var cells = new List<Cell>();
        const int layers = 26;
        const float radius = 5f;                    // in blocks
        float lean = Mathf.Tan(5f * Mathf.Deg2Rad); // slightly exaggerated so it reads on a phone

        for (int y = 0; y < layers; y++)
        {
            bool cornice = y % 4 == 3;
            bool belfry = y >= layers - 3;
            float r = belfry ? radius - 1.5f : (cornice ? radius + 0.8f : radius);
            float xOff = y * lean;
            int ext = Mathf.CeilToInt(r) + 1;

            for (int i = -ext; i <= ext; i++)
            for (int k = -ext; k <= ext; k++)
            {
                float d = Mathf.Sqrt(i * i + k * k);
                if (Mathf.Abs(d - r) > 0.55f) continue;

                // arcade openings on the middle stories
                int seg = Mathf.RoundToInt((Mathf.Atan2(k, i) + Mathf.PI) / (2f * Mathf.PI) * 16f);
                bool opening = y >= 4 && !belfry && (y % 4 == 1 || y % 4 == 2) && seg % 2 == 0;
                if (opening) continue;

                bool orangeBand = cornice || y == layers - 1; // cornices and the top cap are orange
                Add(cells, i + xOff, y, k, !orangeBand);    // the body is white marble
            }
        }
        return cells;
    }

    static void Add(List<Cell> list, float x, float y, float z, bool w) => list.Add(new Cell(x, y, z, w));

    // ---------- build ----------
    static void Build(string name, List<Cell> cells, float size, Vector3 blastOffset)
    {
        var old = GameObject.Find(name);
        if (old) Undo.DestroyObjectImmediate(old);

        var orange = GetMat("M_Orange", new Color(1f, 0.54f, 0.17f));
        var white = GetMat("M_White", new Color(0.95f, 0.96f, 0.97f));

        var root = new GameObject(name);
        Undo.RegisterCreatedObjectUndo(root, "Build " + name);
        var origin = GameObject.Find("SUBJECT_ORIGIN");
        root.transform.position = origin ? origin.transform.position : Vector3.zero;

        var blocks = new GameObject("Blocks").transform;
        blocks.SetParent(root.transform, false);

        float visual = size * 0.96f; // tiny seams so it reads as blocks
        int count = 0;
        foreach (var c in cells)
        {
            var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
            go.name = "Block";
            go.transform.SetParent(blocks, false);
            go.transform.localPosition = new Vector3(c.pos.x * size, c.pos.y * size + size * 0.5f, c.pos.z * size);
            go.transform.localScale = Vector3.one * visual;
            go.GetComponent<Renderer>().sharedMaterial = c.white ? white : orange;

            var rb = go.AddComponent<Rigidbody>();
            rb.mass = 0.3f;
            rb.interpolation = RigidbodyInterpolation.Interpolate;
            rb.isKinematic = true;
            count++;
        }

        var blast = new GameObject("BlastPoint").transform;
        blast.SetParent(root.transform, false);
        blast.localPosition = blastOffset;

        var ex = root.AddComponent<MaekoExplodable>();
        ex.blastPoint = blast;

        Selection.activeGameObject = root;
        Debug.Log($"{name} built with {count} blocks. Press Play and it explodes after {ex.delay}s.");
    }

    static Material GetMat(string name, Color fallback)
    {
        var mat = AssetDatabase.LoadAssetAtPath<Material>($"{Folder}/{name}.mat");
        if (mat) return mat;
        var shader = Shader.Find("Universal Render Pipeline/Lit");
        if (shader == null) shader = Shader.Find("Standard");
        mat = new Material(shader) { color = fallback };
        EnsureFolder(Folder);
        AssetDatabase.CreateAsset(mat, $"{Folder}/{name}.mat");
        return mat;
    }

    // =====================================================================================
    // Detailed Eiffel Tower, proportioned after the LEGO Icons set: slim truss legs, a big
    // arch on every side, tall defined balconies, a fuller upper shaft, and a crown with mast.
    // Built from about 3,100 separate pieces, roughly 6m tall.
    // =====================================================================================

    struct Piece { public Vector3 c; public Quaternion r; public Vector3 s; public bool white; public bool alt; }

    const float EScale = 6f / 330f;                       // real meters to scene meters
    const float L1 = 66f, L2 = 126f, L3 = 282f;           // deck heights
    const float LegBase = 22f;                            // leg width at the ground
    static float EW(float h) => 7f + 69f * Mathf.Exp(-h / 90f);            // outer half-width at height h
    static float ELeg(float h) => Mathf.Lerp(LegBase, EW(L2), h / L2);     // legs merge at level 2

    [MenuItem("Maeko/Explodable/Eiffel Tower (Detailed)")]
    static void BuildEiffelDetailed() =>
        BuildPieces("EXPLODABLE_EiffelTowerDetailed", EiffelDetailed(), new Vector3(0, 0.6f, 0));

    static List<Piece> EiffelDetailed()
    {
        var P = new List<Piece>();
        const float chord = 0.028f, diag = 0.012f, tie = 0.014f;

        void Beam(Vector3 aReal, Vector3 bReal, float t, bool white = false)
        {
            Vector3 a = aReal * EScale, b = bReal * EScale, d = b - a;
            float len = d.magnitude;
            if (len < 0.008f) return;
            Vector3 up = Mathf.Abs(Vector3.Dot(d / len, Vector3.up)) > 0.99f ? Vector3.forward : Vector3.up;
            P.Add(new Piece { c = (a + b) * 0.5f, r = Quaternion.LookRotation(d, up), s = new Vector3(t, t, len + t * 0.5f), white = white });
        }

        void Block(Vector3 centerReal, Vector3 sizeReal, bool white)
        {
            P.Add(new Piece { c = centerReal * EScale, r = Quaternion.identity, s = sizeReal * EScale, white = white });
        }

        void Panel(Vector3[] A, Vector3[] B)
        {
            for (int c = 0; c < 4; c++) Beam(A[c], B[c], chord);
            for (int f = 0; f < 4; f++)
            {
                int u = f, v = (f + 1) % 4;
                Beam(A[u], B[v], diag);
                Beam(A[v], B[u], diag);
                Beam(B[u], B[v], tie);
            }
        }

        // ---- four legs, ground to level 2 ----
        var hs = new List<float>();
        for (int i = 0; i <= 10; i++) hs.Add(L1 * i / 10f);
        for (int i = 1; i <= 8; i++) hs.Add(L1 + (L2 - L1) * i / 8f);

        for (int sx = -1; sx <= 1; sx += 2)
        for (int sz = -1; sz <= 1; sz += 2)
        {
            var A0 = LegCorners(0f, sx, sz);
            for (int f = 0; f < 4; f++) Beam(A0[f], A0[(f + 1) % 4], tie);
            for (int j = 0; j < hs.Count - 1; j++)
                Panel(LegCorners(hs[j], sx, sz), LegCorners(hs[j + 1], sx, sz));
        }

        // ---- upper shaft, level 2 to the top deck ----
        for (int j = 0; j < 24; j++)
            Panel(Square(L2 + (L3 - L2) * j / 24f), Square(L2 + (L3 - L2) * (j + 1) / 24f));

        // ---- big arches on all four sides: double ribbon with dense struts ----
        for (int face = 0; face < 4; face++)
        {
            const int N = 24;
            for (int i = 0; i < N; i++)
            {
                float t0 = i / (float)N, t1 = (i + 1) / (float)N;
                Beam(ArchPoint(face, t0, 52f), ArchPoint(face, t1, 52f), 0.03f);
                Beam(ArchPoint(face, t0, 45f), ArchPoint(face, t1, 45f), 0.02f);
            }
            for (int k = 1; k < N; k++)
            {
                float t = k / (float)N;
                Beam(ArchPoint(face, t, 52f), ArchPoint(face, t, 45f), 0.012f);
            }
        }

        // ---- decks: floor tiles, thick ledge lip, arcade frieze, railing ----
        void Deck(float h, float outer, float hole, float cell, float frieze, float ledge)
        {
            int n = Mathf.CeilToInt(outer / cell);
            for (int i = -n; i < n; i++)
            for (int k = -n; k < n; k++)
            {
                float cx = (i + 0.5f) * cell, cz = (k + 0.5f) * cell;
                float m = Mathf.Max(Mathf.Abs(cx), Mathf.Abs(cz));
                if (m > outer - cell * 0.5f || m < hole) continue;
                Block(new Vector3(cx, h, cz), new Vector3(cell * 0.96f, 1.4f, cell * 0.96f), true);
            }

            Vector3 At(int side, float pos, float y, float d)
            {
                float o = outer + d;
                switch (side)
                {
                    case 0: return new Vector3(pos, y, o);
                    case 1: return new Vector3(pos, y, -o);
                    case 2: return new Vector3(o, y, pos);
                    default: return new Vector3(-o, y, pos);
                }
            }
            Vector3 Size(int side, float along, float hgt, float depth) =>
                side < 2 ? new Vector3(along, hgt, depth) : new Vector3(depth, hgt, along);

            int segs = Mathf.RoundToInt(2f * outer / cell);
            for (int side = 0; side < 4; side++)
            {
                for (int sgi = 0; sgi < segs; sgi++)
                {
                    float pos = -outer + (sgi + 0.5f) * cell;
                    Block(At(side, pos, h, 0f), Size(side, cell * 0.96f, ledge, 2.2f), true); // ledge lip
                    if (frieze > 0f)
                    {
                        float y0 = h - ledge * 0.5f;
                        Block(At(side, pos - 0.38f * cell, y0 - frieze * 0.5f, -0.6f), Size(side, cell * 0.2f, frieze, 1f), true); // pier
                        Block(At(side, pos + 0.38f * cell, y0 - frieze * 0.5f, -0.6f), Size(side, cell * 0.2f, frieze, 1f), true); // pier
                        Block(At(side, pos, y0 - frieze * 0.18f, -0.6f), Size(side, cell * 0.96f, frieze * 0.36f, 1f), true);      // lintel
                    }
                    Block(At(side, pos, h + ledge * 0.5f + 1.6f, 0f), Size(side, 0.6f, 3.2f, 0.6f), true); // railing post
                }

                var corner = new[] { new Vector2(outer, outer), new Vector2(-outer, outer), new Vector2(-outer, -outer), new Vector2(outer, -outer) };
                var a = corner[side]; var b = corner[(side + 1) % 4];
                Beam(new Vector3(a.x, h + ledge * 0.5f + 3.3f, a.y), new Vector3(b.x, h + ledge * 0.5f + 3.3f, b.y), 0.012f, true); // top rail
                Beam(new Vector3(a.x, h + ledge * 0.5f + 1.8f, a.y), new Vector3(b.x, h + ledge * 0.5f + 1.8f, b.y), 0.008f, true); // mid rail
            }
        }

        Deck(L1, EW(L1) + 5f, 14f, 5f, 9f, 3.5f);
        Deck(L2, EW(L2) + 4f, 0f, 4f, 7f, 3f);
        Deck(L3, EW(L3) + 3f, 0f, 3f, 0f, 2f);

        // ---- crown: cabin, upper cabin, lantern, cap, mast with rings ----
        Block(new Vector3(0, L3 + 5f, 0), new Vector3(13f, 8f, 13f), true);
        Block(new Vector3(0, L3 + 10.5f, 0), new Vector3(9f, 3f, 9f), true);
        Block(new Vector3(0, 297f, 0), new Vector3(5f, 6f, 5f), false);
        Block(new Vector3(0, 301.5f, 0), new Vector3(6.5f, 3f, 6.5f), true);
        Beam(new Vector3(0, 304f, 0), new Vector3(0, 314f, 0), 0.018f, true);
        Beam(new Vector3(0, 314f, 0), new Vector3(0, 323f, 0), 0.013f, true);
        Beam(new Vector3(0, 323f, 0), new Vector3(0, 330f, 0), 0.009f, true);
        Block(new Vector3(0, 314f, 0), new Vector3(3.5f, 1.2f, 3.5f), true);
        Block(new Vector3(0, 323f, 0), new Vector3(2.2f, 0.9f, 2.2f), true);

        // ---- stone plinths under each leg ----
        float o0 = EW(0f), n0 = o0 - ELeg(0f), mid = (o0 + n0) * 0.5f;
        for (int sx = -1; sx <= 1; sx += 2)
        for (int sz = -1; sz <= 1; sz += 2)
            Block(new Vector3(sx * mid, 1.5f, sz * mid), new Vector3(LegBase + 4f, 3f, LegBase + 4f), true);

        return P;
    }

    static Vector3[] LegCorners(float h, int sx, int sz)
    {
        float o = EW(h), n = o - ELeg(h);
        return new[]
        {
            new Vector3(sx * o, h, sz * o), new Vector3(sx * n, h, sz * o),
            new Vector3(sx * n, h, sz * n), new Vector3(sx * o, h, sz * n)
        };
    }

    static Vector3[] Square(float h)
    {
        float o = EW(h);
        return new[] { new Vector3(o, h, o), new Vector3(-o, h, o), new Vector3(-o, h, -o), new Vector3(o, h, -o) };
    }

    static Vector3 ArchPoint(int face, float t, float apex)
    {
        const float spring = 12f;
        float xi = EW(spring) - ELeg(spring);
        float a = -xi + 2f * xi * t;
        float y = spring + (apex - spring) * Mathf.Sqrt(Mathf.Max(0f, 1f - (2f * t - 1f) * (2f * t - 1f)));
        float p = EW(y) + 0.8f; // sits just proud of the leg faces
        switch (face)
        {
            case 0: return new Vector3(a, y, p);
            case 1: return new Vector3(a, y, -p);
            case 2: return new Vector3(p, y, a);
            default: return new Vector3(-p, y, a);
        }
    }


    // =====================================================================================
    // Detailed castle: round corner towers with arrow slits, machicolations, merlons and
    // stepped cone roofs, curtain walls, a gatehouse with an arched gate and portcullis,
    // and a tall keep with windows, corner turrets and a central spire. About 3,100 bricks.
    // =====================================================================================

    const float BL = 0.24f, BH = 0.12f, BD = 0.16f; // brick length, height, depth
    static System.Random stoneRng;

    [MenuItem("Maeko/Explodable/Castle (Detailed)")]
    static void BuildCastle() => BuildPieces("EXPLODABLE_Castle", CastlePieces(), new Vector3(0, 0.5f, 0.3f));

    static List<Piece> CastlePieces()
    {
        var P = new List<Piece>();
        stoneRng = new System.Random(7);

        bool Alt() => stoneRng.NextDouble() < 0.18;
        void Box(Vector3 c, float yaw, Vector3 size, bool orange, bool alt = false)
        {
            P.Add(new Piece { c = c, r = Quaternion.Euler(0, yaw, 0), s = size, white = !orange, alt = alt && !orange });
        }
        void Stone(Vector3 c, float yaw, Vector3 size) => Box(c, yaw, size, false, Alt());

        // straight brick wall from A to B (ground positions), running bond, optional opening test (u along wall from center, y)
        void Wall(float ax, float az, float bx, float bz, float y0, int courses, System.Func<float, float, bool> opening = null, bool merlons = false, int shift = 0)
        {
            float L = Mathf.Sqrt((bx - ax) * (bx - ax) + (bz - az) * (bz - az));
            float ux = (bx - ax) / L, uz = (bz - az) / L;
            float yaw = Mathf.Atan2(ux, uz) * Mathf.Rad2Deg;
            for (int c = 0; c < courses; c++)
            {
                float y = y0 + c * BH + BH * 0.5f;
                float u = -(((c + shift) % 2) * BL * 0.5f);
                while (u < L - 0.01f)
                {
                    float u0 = Mathf.Max(u, 0f), u1 = Mathf.Min(u + BL, L);
                    u += BL;
                    if (u1 - u0 < BL * 0.3f) continue;
                    float um = (u0 + u1) * 0.5f;
                    if (opening != null && opening(um - L * 0.5f, y)) continue;
                    Stone(new Vector3(ax + ux * um, y, az + uz * um), yaw, new Vector3(BD, BH * 0.94f, (u1 - u0) * 0.96f));
                }
            }
            if (merlons)
            {
                float y = y0 + courses * BH + BH * 0.5f;
                int n = Mathf.Max(2, (int)(L / BL));
                for (int i = 0; i < n; i += 2)
                {
                    float um = (i + 0.5f) * L / n;
                    Stone(new Vector3(ax + ux * um, y, az + uz * um), yaw, new Vector3(BD, BH * 0.94f, L / n * 0.9f));
                }
            }
        }

        void Ring(float cx, float cz, float r, float y, int every = 1, float scale = 0.98f)
        {
            int n = Mathf.Max(6, Mathf.RoundToInt(2f * Mathf.PI * r / BL));
            for (int i = 0; i < n; i += every)
            {
                float a = 2f * Mathf.PI * i / n, chord = 2f * r * Mathf.Sin(Mathf.PI / n);
                Stone(new Vector3(cx + r * Mathf.Sin(a), y, cz + r * Mathf.Cos(a)), a * Mathf.Rad2Deg + 90f, new Vector3(BD, BH * 0.94f, chord * scale));
            }
        }

        void ConeRoof(float cx, float cz, float y0, float r, float h, int rings = 8, float tile = 0.2f)
        {
            for (int k = 0; k < rings; k++)
            {
                float rr = r * (1f - k / (float)rings), y = y0 + k * h / rings + h / rings * 0.5f;
                int n = Mathf.Max(3, Mathf.RoundToInt(2f * Mathf.PI * rr / tile));
                for (int i = 0; i < n; i++)
                {
                    float a = 2f * Mathf.PI * (i + 0.5f * (k % 2)) / n, chord = 2f * rr * Mathf.Sin(Mathf.PI / n);
                    Box(new Vector3(cx + rr * Mathf.Sin(a), y, cz + rr * Mathf.Cos(a)), a * Mathf.Rad2Deg + 90f,
                        new Vector3(Mathf.Max(0.08f, rr * 0.55f), h / rings * 1.02f, chord * 1.08f), true);
                }
            }
        }

        void Flag(float x, float z, float y)
        {
            Box(new Vector3(x, y + 0.3f, z), 0, new Vector3(0.025f, 0.6f, 0.025f), false);
            Box(new Vector3(x + 0.13f, y + 0.5f, z), 0, new Vector3(0.24f, 0.15f, 0.02f), true);
        }

        void RoundTower(float cx, float cz, float r, int courses, float roofH, float baseY = 0f)
        {
            int n = Mathf.Max(6, Mathf.RoundToInt(2f * Mathf.PI * r / BL));
            for (int c = 0; c < courses; c++)
            {
                float y = baseY + c * BH + BH * 0.5f;
                for (int i = 0; i < n; i++)
                {
                    float a = 2f * Mathf.PI * (i + 0.5f * (c % 2)) / n;
                    bool slitRow = (c >= 6 && c <= 8) || (c >= 13 && c <= 15);
                    if (slitRow && i % Mathf.Max(1, n / 4) == 0) continue; // arrow slits
                    float chord = 2f * r * Mathf.Sin(Mathf.PI / n);
                    Stone(new Vector3(cx + r * Mathf.Sin(a), y, cz + r * Mathf.Cos(a)), a * Mathf.Rad2Deg + 90f, new Vector3(BD, BH * 0.94f, chord * 0.98f));
                }
            }
            float top = baseY + courses * BH;
            Ring(cx, cz, r + 0.06f, top + BH * 0.5f);                 // machicolation
            Ring(cx, cz, r + 0.06f, top + BH * 1.5f, 2, 0.9f);        // merlons
            ConeRoof(cx, cz, top + BH * 2f, r + 0.14f, roofH);
            Flag(cx, cz, top + BH * 2f + roofH);
        }

        float SquareTower(float cx, float cz, float L, int courses, System.Func<int, float, float, bool> window = null, bool cap = true)
        {
            float h = L * 0.5f;
            for (int c = 0; c < courses; c++)
            {
                float y0 = c * BH;
                bool fz = c % 2 == 0; // alternate which pair of walls runs full length so corners interlock
                for (int side = 0; side < 4; side++)
                {
                    int s = side;
                    System.Func<float, float, bool> op = null;
                    if (window != null) op = (u, y) => window(s, u, y);
                    if (side < 2)
                    {
                        float z = cz + (side == 0 ? -h + BD * 0.5f : h - BD * 0.5f);
                        float ln = fz ? L : L - 2f * BD;
                        Wall(cx - ln * 0.5f, z, cx + ln * 0.5f, z, y0, 1, op, false, c);
                    }
                    else
                    {
                        float x = cx + (side == 2 ? -h + BD * 0.5f : h - BD * 0.5f);
                        float ln = fz ? L - 2f * BD : L;
                        Wall(x, cz - ln * 0.5f, x, cz + ln * 0.5f, y0, 1, op, false, c);
                    }
                }
            }
            float top = courses * BH;
            if (cap)
            {
                float H = h + 0.06f;
                for (int side = 0; side < 4; side++)
                {
                    if (side < 2) { float z = cz + (side == 0 ? -H + BD * 0.5f : H - BD * 0.5f); Wall(cx - H, z, cx + H, z, top, 1, null, true); }
                    else { float x = cx + (side == 2 ? -H + BD * 0.5f : H - BD * 0.5f); Wall(x, cz - H + BD, x, cz + H - BD, top, 1, null, true); }
                }
            }
            return top;
        }

        // ---------------- layout ----------------
        const float W = 1.7f, RT = 0.42f;
        const int WALLC = 12;

        for (int sx = -1; sx <= 1; sx += 2)
        for (int sz = -1; sz <= 1; sz += 2)
            RoundTower(sx * W, sz * W, RT, 20, 1.0f);

        float e = W - RT + 0.02f;
        Wall(-W, -e, -W, e, 0, WALLC, null, true);   // left
        Wall(W, -e, W, e, 0, WALLC, null, true);     // right
        Wall(-e, W, e, W, 0, WALLC, null, true);     // back

        // gatehouse
        const float GT = 0.5f, GX = 0.65f;
        Wall(-e, -W, -(GX + GT * 0.5f) + 0.02f, -W, 0, WALLC, null, true);
        Wall((GX + GT * 0.5f) - 0.02f, -W, e, -W, 0, WALLC, null, true);
        bool Gate(float u, float y)
        {
            const float hw = 0.28f, spring = 0.55f;
            return Mathf.Abs(u) < hw && (y < spring || u * u + (y - spring) * (y - spring) < hw * hw);
        }
        Wall(-(GX - GT * 0.5f), -W, GX - GT * 0.5f, -W, 0, WALLC + 2, Gate, true);
        for (int sx = -1; sx <= 1; sx += 2)
        {
            SquareTower(sx * GX, -W, GT, 17);
            Flag(sx * GX, -W, 17 * BH + BH * 2f);
        }
        for (int i = 0; i < 5; i++) Box(new Vector3(-0.22f + i * 0.11f, 0.36f, -W + 0.05f), 0, new Vector3(0.025f, 0.66f, 0.025f), true); // portcullis
        for (int j = 0; j < 4; j++) Box(new Vector3(0, 0.12f + j * 0.16f, -W + 0.05f), 0, new Vector3(0.5f, 0.025f, 0.025f), true);

        // keep
        const float KL = 1.5f, KZ = 0.3f;
        bool KeepWindow(int side, float u, float y) =>
            Mathf.Abs(u) < 0.14f && ((y > 1.2f && y < 1.6f) || (y > 2.4f && y < 2.8f) || (y > 3.4f && y < 3.8f));
        float ktop = SquareTower(0, KZ, KL, 35, KeepWindow);

        for (int sx = -1; sx <= 1; sx += 2)
        for (int sz = -1; sz <= 1; sz += 2)
        {
            float cx = sx * (KL * 0.5f - 0.05f), cz = KZ + sz * (KL * 0.5f - 0.05f);
            for (int c = 0; c < 4; c++) Ring(cx, cz, 0.14f, ktop + BH * 2f + c * BH + BH * 0.5f);
            ConeRoof(cx, cz, ktop + BH * 6f, 0.22f, 0.45f, 5, 0.14f);
        }
        RoundTower(0, KZ, 0.32f, 9, 1.2f, ktop + BH * 2f);

        return P;
    }

    static void BuildPieces(string name, List<Piece> pieces, Vector3 blastOffset)
    {
        var old = GameObject.Find(name);
        if (old) Undo.DestroyObjectImmediate(old);

        var orange = GetMat("M_Orange", new Color(1f, 0.54f, 0.17f));
        var white = GetMat("M_White", new Color(0.95f, 0.96f, 0.97f));
        var stoneAlt = GetMat("M_StoneAlt", new Color(0.84f, 0.83f, 0.80f));

        var root = new GameObject(name);
        Undo.RegisterCreatedObjectUndo(root, "Build " + name);
        var origin = GameObject.Find("SUBJECT_ORIGIN");
        root.transform.position = origin ? origin.transform.position : Vector3.zero;

        var parts = new GameObject("Pieces").transform;
        parts.SetParent(root.transform, false);

        foreach (var pc in pieces)
        {
            var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
            go.name = "Piece";
            go.transform.SetParent(parts, false);
            go.transform.localPosition = pc.c;
            go.transform.localRotation = pc.r;
            go.transform.localScale = pc.s;
            go.GetComponent<Renderer>().sharedMaterial = pc.alt ? stoneAlt : (pc.white ? white : orange);

            var rb = go.AddComponent<Rigidbody>();
            rb.mass = 0.3f;
            rb.interpolation = RigidbodyInterpolation.Interpolate;
            rb.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative; // thin beams won't fall through the floor
            rb.isKinematic = true;
        }

        var blast = new GameObject("BlastPoint").transform;
        blast.SetParent(root.transform, false);
        blast.localPosition = blastOffset;

        var ex = root.AddComponent<MaekoExplodable>();
        ex.blastPoint = blast;

        Selection.activeGameObject = root;
        Debug.Log($"{name} built with {pieces.Count} pieces. Press Play and it explodes after {ex.delay}s.");
    }


    // =====================================================================================
    // Make Selected Explodable: select the parent of any object built from separate pieces.
    // Every child with a mesh gets a Rigidbody (and a BoxCollider if it has no collider),
    // a BlastPoint is placed low in the middle, and MaekoExplodable is added to the parent.
    // =====================================================================================

    [MenuItem("Maeko/Explodable/Make Selected Explodable", priority = 0)]
    static void MakeSelectedExplodable()
    {
        var root = Selection.activeGameObject;
        if (!root) { EditorUtility.DisplayDialog("Maeko Explodable", "Select the parent object of your pieces first.", "OK"); return; }

        var renderers = root.GetComponentsInChildren<MeshRenderer>();
        if (renderers.Length < 2)
        {
            EditorUtility.DisplayDialog("Maeko Explodable",
                "This object needs to be made of separate child pieces (at least 2). Single meshes can't come apart on their own.", "OK");
            return;
        }

        Undo.RegisterFullObjectHierarchyUndo(root, "Make Explodable");
        Bounds bounds = renderers[0].bounds;
        int added = 0;

        foreach (var r in renderers)
        {
            bounds.Encapsulate(r.bounds);
            var go = r.gameObject;
            if (go == root) continue;
            if (!go.GetComponent<Collider>()) Undo.AddComponent<BoxCollider>(go);
            var rb = go.GetComponent<Rigidbody>();
            if (!rb) { rb = Undo.AddComponent<Rigidbody>(go); added++; }
            rb.interpolation = RigidbodyInterpolation.Interpolate;
            rb.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
            rb.isKinematic = true;
        }

        var ex = root.GetComponent<MaekoExplodable>();
        if (!ex) ex = Undo.AddComponent<MaekoExplodable>(root);

        if (!ex.blastPoint)
        {
            var blast = new GameObject("BlastPoint");
            Undo.RegisterCreatedObjectUndo(blast, "Make Explodable");
            blast.transform.SetParent(root.transform, false);
            blast.transform.position = new Vector3(bounds.center.x, bounds.min.y + bounds.size.y * 0.15f, bounds.center.z);
            ex.blastPoint = blast.transform;
        }
        ex.radius = Mathf.Max(ex.radius, bounds.extents.magnitude * 1.5f);

        Debug.Log($"{root.name} is now explodable ({renderers.Length} pieces, {added} rigidbodies added). Press Play!");
    }

    static void EnsureFolder(string path)
    {
        string[] parts = path.Split('/');
        string current = parts[0];
        for (int i = 1; i < parts.Length; i++)
        {
            string next = current + "/" + parts[i];
            if (!AssetDatabase.IsValidFolder(next)) AssetDatabase.CreateFolder(current, parts[i]);
            current = next;
        }
    }
}
#endif
