/* tank3d.c —— **3D 坦克对战**：第五个"用真游戏验接口"的探针。
 * tank3d.c — **3D tank battle**: the fifth "write a real game to test the API" probe.
 *
 * ## 它压的是**唯一一个此前一个游戏都没用到的号**：`ui_3d_obj_set_parent`
 * ## It stresses **the one syscall no earlier game touched**: `ui_3d_obj_set_parent`
 *
 * 前四个游戏（贪吃蛇/桌球/赛车/空战）全是**平铺**的物体 —— 每个物体的位姿都直接写在世界坐标里。
 * The first four games (snake / pool / race / air combat) all used **flat** objects -- every object's pose written straight in world coordinates.
 * 而坦克的经典结构是**三层父子**：
 * A tank's classic structure is a **three-level parent/child chain**:
 *
 *     车体（位置 + 航向）
 *     hull (position + heading)
 *       └─ 炮塔（**局部**旋转：独立瞄准）
 *       └─ turret (**local** rotation: independent aiming)
 *            └─ 炮管（局部偏移 + 仰角）
 *            └─ barrel (local offset + elevation)
 *
 * 这一条逼出两个接口事实：
 * That forced out two facts about the API:
 *
 * ① **子物体的 `ui_3d_obj_set_pos` / `ui_3d_obj_set_rot` 是"父的局部空间"里的** —— 传世界坐标进去会得到
 * (1) **a child's `ui_3d_obj_set_pos` / `ui_3d_obj_set_rot` are in "the parent's local space"** -- passing world coordinates gives
 *    "世界坐标再叠一次父变换"的荒谬结果（看着像"物体飞了"，但**不报错**）。
 *    the absurd "world coordinates with the parent transform applied once more" (it looks like the object flew away, yet **no error is reported**).
 * ② **父变换是每帧重新合成的**（`WorldMatrix` 递归），所以父一动、子**自动跟着动**，
 * (2) **the parent transform is recomposed every frame** (`WorldMatrix` recursion), so when the parent moves the children **follow automatically**,
 *    不需要每帧把子物体的世界坐标重算一遍 —— 这正是 `ui_3d_obj_set_parent` 的价值所在。
 *    with no need to recompute the children's world coordinates each frame -- that is exactly the value of `ui_3d_obj_set_parent`.
 *
 * 玩法上它体现为一句话：**车体转，炮塔纹丝不动地继续瞄着目标**（因为炮塔角是局部的，
 * In gameplay it comes down to one sentence: **the hull turns, the turret keeps aiming at the target without budging** (because the turret angle is local,
 * 自动把车体的旋转抵消掉了）。
 * it automatically cancels the hull's rotation out).
 *
 * ## 玩法
 * ## Gameplay
 *
 * 车自己往前开，←/→ 转向；**炮塔自动瞄准最近的目标**，空格/点屏开火。
 * The tank drives forward by itself; ←/→ steer; the **turret auto-aims at the nearest target**; Space / tap the screen fires.
 * 打光所有靶子就赢。Esc 退出。
 * Destroy all targets to win. Esc quits.
 *
 * ## 判据
 * ## Criteria
 *
 * 机械化验证：`RUN_FRAMES` > 0 时跑满帧数并打印
 * Mechanical check: with `RUN_FRAMES` > 0 it runs the full frame count and prints
 * `TANK-DONE frames=… kills=… shots=… turret=…`（行首锚定即可断）。
 * `TANK-DONE frames=... kills=... shots=... turret=...` (anchor on the line start to decide).
 */
#include <stdio.h>
#include <math.h>
#include <vml_3d.h>
#include <vml_ui.h>

#define RUN_FRAMES 0

#define N_TARGET  8
#define N_BULLET  16
#define SPEED     20.0f
#define TURN      1.6f           /* 车体转向速率（弧度/秒） */
                                 /* hull turn rate (radians/sec) */
#define TURRET_RATE 2.6f         /* 炮塔转速（弧度/秒）—— 比车体快，才有"追着目标转"的观感 */
                                 /* turret turn rate (radians/sec) -- faster than the hull, so it looks like it chases the target */
#define BULLET_SP 70.0f
#define FIRE_CD   14
#define HIT_R     3.0f
#define ARENA     70.0f          /* 场地半径（车开出去就绕回来） */
                                 /* arena radius (drive past it and you are pulled back) */

#define C_BG      0xFF141A12
#define C_GROUND  0xFF3E4A2E
#define C_HULL    0xFF6B7A4A
#define C_TURRET  0xFF8A9A5E
#define C_BARREL  0xFF4A5233
#define C_TARGET  0xFFC85A3A
#define C_BULLET  0xFFFFE066

/* ══ 网格装配 ══ */
/* ══ Mesh assembly ══ */
float MB[4400];
int g_vn;
int g_in;

void mb_vertex(int i, float px, float py, float pz, float nx, float ny, float nz) {
    int b;
    b = i * 8;
    MB[b + 0] = px; MB[b + 1] = py; MB[b + 2] = pz;
    MB[b + 3] = nx; MB[b + 4] = ny; MB[b + 5] = nz;
    MB[b + 6] = 0.0f; MB[b + 7] = 0.0f;
}

void mb_index(int k, int v) {
    int* ip;
    ip = (int*)&MB[0];
    ip[g_vn * 8 + k] = v;
}

int mb_ptr(void) { return (int)&MB[0]; }

void gen_box(float hx, float hy, float hz) {
    int f; int v; int k;
    float nx[6]; float ny[6]; float nz[6];
    nx[0] = 1.0f;  ny[0] = 0.0f;  nz[0] = 0.0f;
    nx[1] = -1.0f; ny[1] = 0.0f;  nz[1] = 0.0f;
    nx[2] = 0.0f;  ny[2] = 1.0f;  nz[2] = 0.0f;
    nx[3] = 0.0f;  ny[3] = -1.0f; nz[3] = 0.0f;
    nx[4] = 0.0f;  ny[4] = 0.0f;  nz[4] = 1.0f;
    nx[5] = 0.0f;  ny[5] = 0.0f;  nz[5] = -1.0f;
    v = 0;
    for (f = 0; f < 6; f = f + 1) {
        int axisU; int axisW; int q;
        if (f == 0 || f == 1) { axisU = 1; axisW = 2; }
        else if (f == 2 || f == 3) { axisU = 0; axisW = 2; }
        else { axisU = 0; axisW = 1; }
        for (q = 0; q < 4; q = q + 1) {
            float hu; float hw; float cx; float cy; float cz;
            hu = ((q == 0 || q == 3) ? 1.0f : -1.0f) *
                 ((axisU == 0) ? hx : ((axisU == 1) ? hy : hz));
            hw = ((q == 0 || q == 1) ? 1.0f : -1.0f) *
                 ((axisW == 0) ? hx : ((axisW == 1) ? hy : hz));
            if (f == 0)      { cx = hx;  cy = hu; cz = hw; }
            else if (f == 1) { cx = -hx; cy = hu; cz = hw; }
            else if (f == 2) { cx = hu; cy = hy;  cz = hw; }
            else if (f == 3) { cx = hu; cy = -hy; cz = hw; }
            else if (f == 4) { cx = hu; cy = hw;  cz = hz; }
            else             { cx = hu; cy = hw;  cz = -hz; }
            mb_vertex(v, cx, cy, cz, nx[f], ny[f], nz[f]);
            v = v + 1;
        }
    }
    g_vn = v;
    k = 0;
    for (f = 0; f < 6; f = f + 1) {
        int base;
        base = f * 4;
        mb_index(k, base);     k = k + 1;
        mb_index(k, base + 1); k = k + 1;
        mb_index(k, base + 2); k = k + 1;
        mb_index(k, base);     k = k + 1;
        mb_index(k, base + 2); k = k + 1;
        mb_index(k, base + 3); k = k + 1;
    }
    g_in = k;
}

void gen_plane(float half, int segs) {
    int i; int j; int k; int v;
    v = 0;
    for (i = 0; i <= segs; i = i + 1) {
        for (j = 0; j <= segs; j = j + 1) {
            float x = ((float)i / segs - 0.5f) * 2.0f * half;
            float z = ((float)j / segs - 0.5f) * 2.0f * half;
            mb_vertex(v, x, 0.0f, z, 0.0f, 1.0f, 0.0f);
            v = v + 1;
        }
    }
    g_vn = v;
    k = 0;
    for (i = 0; i < segs; i = i + 1) {
        for (j = 0; j < segs; j = j + 1) {
            int a = i * (segs + 1) + j;
            int b = a + segs + 1;
            mb_index(k, a);     k = k + 1;
            mb_index(k, b);     k = k + 1;
            mb_index(k, a + 1); k = k + 1;
            mb_index(k, a + 1); k = k + 1;
            mb_index(k, b);     k = k + 1;
            mb_index(k, b + 1); k = k + 1;
        }
    }
    g_in = k;
}

/* ══ 状态 ═══════════════════════════════════════════════════════════════ */
/* ══ State ════════════════════════════════════════════════════════════ */
int vp;
int o_hull; int o_turret; int o_barrel;
int o_target[N_TARGET];
int o_bullet[N_BULLET];

float hull_x; float hull_z; float hull_yaw;       /* 世界位姿 */
                                                  /* world pose */
float turret_local;                                /* **局部**偏航（相对车体） */
                                                   /* **local** yaw (relative to the hull) */
float barrel_local;                                /* **局部**仰角 */
                                                   /* **local** elevation */

float tx[N_TARGET]; float tz[N_TARGET]; int t_alive[N_TARGET];
float bxp[N_BULLET]; float byp[N_BULLET]; float bzp[N_BULLET];
float bvx[N_BULLET]; float bvy[N_BULLET]; float bvz[N_BULLET];
int b_alive[N_BULLET];
int kills; int shots; int fire_cd; int frame;
int lastMs; int nowMs;   /* 墙钟步长（见主循环顶部） */
                         /* wall-clock step (see the top of the main loop) */
float dt;

float ang_norm(float a) {
    while (a > 3.14159265f) a = a - 6.28318531f;
    while (a < -3.14159265f) a = a + 6.28318531f;
    return a;
}

void reset_game(void) {
    int i;
    hull_x = 0.0f; hull_z = 0.0f; hull_yaw = 0.0f;
    turret_local = 0.0f; barrel_local = -6.0f;
    kills = 0; shots = 0; fire_cd = 0;
    for (i = 0; i < N_TARGET; i = i + 1) {
        float th = 6.28318531f * i / N_TARGET;
        tx[i] = cos(th) * 40.0f;
        tz[i] = sin(th) * 40.0f;
        t_alive[i] = 1;
        ui_3d_obj_set_visible(o_target[i], 1);
    }
    for (i = 0; i < N_BULLET; i = i + 1) {
        b_alive[i] = 0;
        ui_3d_obj_set_visible(o_bullet[i], 0);
    }
}

int main(void) {
    int sw; int sh;
    int mesh_ground; int mesh_hull; int mesh_turret; int mesh_barrel; int mesh_target; int mesh_bullet;
    int i;
    int touch[3];
    float aim_world; float best; int best_i;

    ui_win_open_ex("3D 坦克 Tank 3D", 0, 0, VML_WIN_ROTATABLE, VML_WIN_NO_GAMEPAD);
    sw = ui_scr_get_w_ex(ui_scr_get_orientation(), VML_CHROME_NONE);
    sh = ui_scr_get_h_ex(ui_scr_get_orientation(), VML_CHROME_NONE);
    if (sw <= 0) sw = 640;
    if (sh <= 0) sh = 640;

    vp = ui_3d_viewport_create(0, 0, sw, sh, 0);
    if (vp <= 0) { printf("NO_3D_DEVICE=1\n"); return 0; }
    ui_3d_viewport_set_bg(vp, C_BG);
    ui_3d_viewport_set_fov(vp, 62.0f, 0.4f, 3000.0f);
    /* ⚠ 跟随相机**不能**配 `vp_fit`（见 race3d 的说明），这里只用 `ui_3d_viewport_set_camera` */
    /* ⚠ a follow camera must **not** use `vp_fit` (see the note in race3d); here only `ui_3d_viewport_set_camera` is used */
    ui_3d_viewport_set_light(vp, 0.35f, 1.0f, 0.30f, 0xFFFFFFFF, 0.52f);

    /* ── 网格 ─────────────────────────────────────────────────────────── */
    /* ── Mesh ───────────────────────────────────────────────────────── */
    gen_plane(400.0f, 6);
    mesh_ground = ui_3d_mesh_create(mb_ptr(), g_vn, g_in);
    gen_box(1.4f, 0.5f, 2.2f);           /* 车体：长轴 Z */
                                         /* hull: long axis Z */
    mesh_hull = ui_3d_mesh_create(mb_ptr(), g_vn, g_in);
    gen_box(1.0f, 0.45f, 1.0f);          /* 炮塔 */
                                         /* turret */
    mesh_turret = ui_3d_mesh_create(mb_ptr(), g_vn, g_in);
    gen_box(0.18f, 0.18f, 1.5f);         /* 炮管：长轴 Z，局部 +Z 指向前方 */
                                         /* barrel: long axis Z, local +Z points forward */
    mesh_barrel = ui_3d_mesh_create(mb_ptr(), g_vn, g_in);
    gen_box(1.2f, 1.6f, 1.2f);           /* 靶子 */
                                         /* target */
    mesh_target = ui_3d_mesh_create(mb_ptr(), g_vn, g_in);
    gen_box(0.25f, 0.25f, 0.7f);
    mesh_bullet = ui_3d_mesh_create(mb_ptr(), g_vn, g_in);
    printf("TANK-MESH ground=%d hull=%d turret=%d barrel=%d target=%d bullet=%d\n",
           mesh_ground, mesh_hull, mesh_turret, mesh_barrel, mesh_target, mesh_bullet);
    if (mesh_ground <= 0 || mesh_hull <= 0 || mesh_turret <= 0 || mesh_barrel <= 0
        || mesh_target <= 0 || mesh_bullet <= 0)
    { printf("NO_MESH=1\n"); return 0; }

    /* 地面 */
    /* Ground */
    {
        int o;
        o = ui_3d_obj_create(vp, mesh_ground);
        ui_3d_obj_set_pos(o, 0.0f, 0.0f, 0.0f);
        ui_3d_obj_set_color(o, C_GROUND);
    }

    /* ── 三层父子：车体 → 炮塔 → 炮管 ────────────────────────────────────
    /* ── Three-level parent/child: hull -> turret -> barrel ────────────────
     * ⚠ 炮塔与炮管的坐标是**父的局部空间**：炮塔在车体上方 0.9、炮管在炮塔前方 1.2。
     * ⚠ The turret's and barrel's coordinates are in the **parent's local space**: the turret 0.9 above the hull, the barrel 1.2 in front of the turret.
     *   写世界坐标会得到"世界坐标再叠一次父变换"的结果 —— 看着像物体飞了，**但不报错**。
     *   Writing world coordinates gives "world coordinates with the parent transform applied once more" -- looks like the object flew away, **but no error is reported**. */
    o_hull = ui_3d_obj_create(vp, mesh_hull);
    ui_3d_obj_set_color(o_hull, C_HULL);

    o_turret = ui_3d_obj_create(vp, mesh_turret);
    ui_3d_obj_set_pos(o_turret, 0.0f, 0.9f, 0.0f);      /* 局部：车体上方 */
                                                        /* local: above the hull */
    ui_3d_obj_set_color(o_turret, C_TURRET);
    ui_3d_obj_set_parent(o_turret, o_hull);             /* ← 本文件的主角 */
                                                        /* <- the star of this file */

    o_barrel = ui_3d_obj_create(vp, mesh_barrel);
    ui_3d_obj_set_pos(o_barrel, 0.0f, 0.0f, 1.2f);      /* 局部：炮塔前方 */
                                                        /* local: in front of the turret */
    ui_3d_obj_set_color(o_barrel, C_BARREL);
    ui_3d_obj_set_parent(o_barrel, o_turret);           /* ← 三层 */
                                                        /* <- three levels deep */

    for (i = 0; i < N_TARGET; i = i + 1) {
        o_target[i] = ui_3d_obj_create(vp, mesh_target);
        ui_3d_obj_set_pos(o_target[i], 0.0f, 1.6f, 0.0f);
        ui_3d_obj_set_color(o_target[i], C_TARGET);
    }
    for (i = 0; i < N_BULLET; i = i + 1) {
        o_bullet[i] = ui_3d_obj_create(vp, mesh_bullet);
        ui_3d_obj_set_color(o_bullet[i], C_BULLET);
        ui_3d_obj_set_visible(o_bullet[i], 0);
    }
    printf("TANK-OBJS=%d\n", 1 + 3 + N_TARGET + N_BULLET);

    reset_game();

    frame = 0;

    lastMs = ui_time_get_ms();
    dt = 0.04f;

    while (1) {
        float steer;
        int firing;

        /* 步长**按墙钟算**（用户 2026-10-04：帧率不该是玩法的一部分）。
        /* The step is **computed from the wall clock** (user, 2026-10-04: frame rate should not be part of the gameplay).
         * 老写法把 0.04（=1/25 秒）写死，宿主改成跟着显示走之后会快 2.4 倍。
         * The old form hard-coded 0.04 (= 1/25 s), which runs 2.4x too fast once the host follows the display. */
        nowMs = ui_time_get_ms();
        dt = (nowMs - lastMs) * 0.001f;
        if (dt > 0.1f) dt = 0.1f;
        if (dt <= 0.0f) dt = 0.0001f;
        lastMs = nowMs;

        /* ── 输入 ──────────────────────────────────────────────────────── */
        /* ── Input ───────────────────────────────────────────────────── */
        steer = 0.0f;
        if (ui_key_is_down(VML_KEY_LEFT)) steer = -1.0f;
        if (ui_key_is_down(VML_KEY_RIGHT)) steer = 1.0f;
        if (ui_key_is_down(65)) steer = -1.0f;   /* 'A' */
        if (ui_key_is_down(68)) steer = 1.0f;    /* 'D' */
        if (ui_key_is_down(VML_KEY_ESCAPE)) break;

        firing = 0;
        if (ui_key_is_down(VML_KEY_SPACE)) firing = 1;
        if (ui_touch_read(0, touch)) {
            if (touch[2]) {
                if (touch[0] < sw / 2) steer = -1.0f; else steer = 1.0f;
                firing = 1;
            }
        }

        /* ── 车体：只动**世界**位姿 ───────────────────────────────────── */
        /* ── Hull: moves its **world** pose only ─────────────── */
        hull_yaw = hull_yaw + steer * TURN * dt;
        if (hull_yaw > 3.14159265f) hull_yaw = hull_yaw - 6.28318531f;
        if (hull_yaw < -3.14159265f) hull_yaw = hull_yaw + 6.28318531f;
        hull_x = hull_x + sin(hull_yaw) * SPEED * dt;
        hull_z = hull_z + cos(hull_yaw) * SPEED * dt;
        /* 场地边界：开出去就贴边绕（**注意钳的是父**，两个子会自动跟着走） */
        /* Arena boundary: drive out and you hug the edge (**note it clamps the parent**; both children follow automatically) */
        {
            float d = sqrt(hull_x * hull_x + hull_z * hull_z);
            if (d > ARENA) { hull_x = hull_x * ARENA / d; hull_z = hull_z * ARENA / d; }
        }
        ui_3d_obj_set_pos(o_hull, hull_x, 0.5f, hull_z);
        ui_3d_obj_set_rot(o_hull, 0.0f, hull_yaw * 57.29577951f, 0.0f);

        /* ── 炮塔：**只写局部角** —— 车体怎么转，它自动把那份旋转抵消掉 ──── */
        /* ── Turret: **writes only the local angle** -- it cancels the hull rotation out ── */
        best = 1e9f; best_i = -1;
        for (i = 0; i < N_TARGET; i = i + 1) {
            float dx; float dz; float d2;
            if (t_alive[i] == 0) continue;
            dx = tx[i] - hull_x; dz = tz[i] - hull_z;
            d2 = dx * dx + dz * dz;
            if (d2 < best) { best = d2; best_i = i; }
        }
        if (best_i >= 0) {
            float want; float diff;
            /* 目标的**世界**方位角 → 减去车体航向 = 炮塔该有的**局部**角 */
            /* target's **world** azimuth -> minus the hull heading = the **local** angle the turret should have */
            want = atan2(tx[best_i] - hull_x, tz[best_i] - hull_z);
            diff = ang_norm(want - hull_yaw);
            /* 慢慢转过去（每帧最多 TURRET_RATE×dt），转到位就停 */
            /* turn slowly toward it (at most TURRET_RATE x dt per frame); stop once aligned */
            if (diff > TURRET_RATE * dt) diff = TURRET_RATE * dt;
            if (diff < -TURRET_RATE * dt) diff = -TURRET_RATE * dt;
            turret_local = ang_norm(turret_local + diff);
            barrel_local = -6.0f;
        }
        ui_3d_obj_set_rot(o_turret, 0.0f, turret_local * 57.29577951f, 0.0f);
        ui_3d_obj_set_rot(o_barrel, barrel_local, 0.0f, 0.0f);

        /* ── 开火：**炮口的世界位置要自己算**（父变换不会替我们求世界坐标）──
        /* ── Fire: **the muzzle's world position must be computed by hand** (the parent transform does not give us world coordinates) --
         * 世界角 = 车体航向 + 炮塔局部角；再沿它推出炮口位置。
         * world angle = hull heading + turret local angle; push the muzzle position out along it. */
        if (fire_cd > 0) fire_cd = fire_cd - 1;
        if (firing && fire_cd == 0) {
            int b;
            float wa; float mx; float mz;
            for (b = 0; b < N_BULLET; b = b + 1) { if (b_alive[b] == 0) break; }
            if (b < N_BULLET) {
                wa = hull_yaw + turret_local;
                mx = hull_x + sin(wa) * 3.2f;
                mz = hull_z + cos(wa) * 3.2f;
                b_alive[b] = 1;
                bxp[b] = mx; byp[b] = 1.4f; bzp[b] = mz;
                bvx[b] = sin(wa) * BULLET_SP;
                bvy[b] = 0.0f;
                bvz[b] = cos(wa) * BULLET_SP;
                ui_3d_obj_set_pos(o_bullet[b], bxp[b], byp[b], bzp[b]);
                ui_3d_obj_set_visible(o_bullet[b], 1);
                shots = shots + 1;
                fire_cd = FIRE_CD;
            }
        }

        /* ── 子弹 ─────────────────────────────────────────────────────── */
        /* ── Bullets ────────────────────────────────────────────────── */
        for (i = 0; i < N_BULLET; i = i + 1) {
            if (b_alive[i] == 0) continue;
            bxp[i] = bxp[i] + bvx[i] * dt;
            bzp[i] = bzp[i] + bvz[i] * dt;
            {
                float d = sqrt(bxp[i] * bxp[i] + bzp[i] * bzp[i]);
                if (d > ARENA + 8.0f) { b_alive[i] = 0; ui_3d_obj_set_visible(o_bullet[i], 0); continue; }
            }
            ui_3d_obj_set_pos(o_bullet[i], bxp[i], byp[i], bzp[i]);
        }

        /* ── 命中 ─────────────────────────────────────────────────────── */
        /* ── Hits ───────────────────────────────────────────────────── */
        for (i = 0; i < N_BULLET; i = i + 1) {
            int k;
            if (b_alive[i] == 0) continue;
            for (k = 0; k < N_TARGET; k = k + 1) {
                float ddx; float ddz;
                if (t_alive[k] == 0) continue;
                ddx = bxp[i] - tx[k];
                ddz = bzp[i] - tz[k];
                if (ddx * ddx + ddz * ddz < HIT_R * HIT_R) {
                    b_alive[i] = 0;
                    ui_3d_obj_set_visible(o_bullet[i], 0);
                    t_alive[k] = 0;
                    ui_3d_obj_set_visible(o_target[k], 0);
                    kills = kills + 1;
                    break;
                }
            }
        }

        /* ── 跟随相机 ─────────────────────────────────────────────────── */
        /* ── Follow camera ────────────────────────────────────────── */
        {
            float fx = sin(hull_yaw); float fz = cos(hull_yaw);
            ui_3d_viewport_set_camera(vp, hull_x - fx * 11.0f, 6.4f, hull_z - fz * 11.0f,
                          hull_x + fx * 14.0f, 1.2f, hull_z + fz * 14.0f);
        }

        /* ── HUD ──────────────────────────────────────────────────────── */
        ui_2d_clear(0);
        ui_2d_text(12, 10, "3D 坦克 Tank 3D", -1, 15, VML_ANCHOR_LEFT);
        {
            char buf[80];
            sprintf(buf, "击毁 %d/%d   开火 %d   炮塔 %.0f°", kills, N_TARGET, shots,
                    turret_local * 57.29577951f);
            ui_2d_text(12, 30, buf, -8355712, 12, VML_ANCHOR_LEFT);
        }
        ui_2d_text(12, 48, "←/→ 驾驶   空格/点屏 开火（炮塔自动瞄准）", -8355712, 11, VML_ANCHOR_LEFT);
        if (kills >= N_TARGET) {
            ui_2d_text(12, 68, "全歼！ALL CLEAR", -65281, 14, VML_ANCHOR_LEFT);
        }

        ui_2d_present();
        ui_msg_fetch_wait(25);

        frame = frame + 1;
        if (RUN_FRAMES > 0) { if (frame >= RUN_FRAMES) break; }
    }

    printf("TANK-DONE frames=%d kills=%d shots=%d turret=%d\n",
           frame, kills, shots, (int)(turret_local * 57.29577951f));
    ui_3d_mesh_free(mesh_ground); ui_3d_mesh_free(mesh_hull); ui_3d_mesh_free(mesh_turret);
    ui_3d_mesh_free(mesh_barrel); ui_3d_mesh_free(mesh_target); ui_3d_mesh_free(mesh_bullet);
    ui_3d_viewport_free(vp);
    return 0;
}
