Files
Sprinter-SDCC/applications/PoP/roomtest/tests-host/t_mouse.c
T
snark13 361c9d9f78 Мышь уровня 8 + холодная половина главного цикла в банк 8
Спецсобытие уровня 8 (SDLPoP seg003:0545/0ABA, seg002:07EB): когда дверь
уровня открыта, а Кид остаётся в комнате 16, через 150 кадров приходит мышь,
пробегает справа налево по верхнему ряду, наступает на кнопку (0,7) —
решётка (0,3) открывается — и убегает.

Ассетов не потребовалось: кадры 186..188 идут по таблице Кида, их спрайты
(images 130..132) уже лежат в kid16.atl, отрисовка подхватывает мышь сама.

- guards.c: pop_check_mouse, autocontrol_mouse, ветки мыши в
  autocontrol_opponent / play_guard / check_can_guard_see_kid;
- pop_leveldoor_open стал word, как в оригинале (в него же считается
  задержка — байт переполнился бы через 255 кадров);
- leave_guard больше не записывает тень и мышь в данные уровня (seg002:02F5);
- полосы HP у мыши нет (seg000:1159);
- tests-host/t_mouse: 17 проверок, единственный набор с guards.c.

Разгрузка резидента (куча в huge упала до 879 Б): pop_start_level,
find_start_level_door, чит-навигация по комнатам и лейбл номера уехали в
roomtest_cold.c (--bank 8, n_banks = 8).  _CODE 25653 -> 24825, куча -> 1707 Б;
кадровый путь не тронут.

Проверено: tests-host 6/6, make size-check OK, живьём в MAME (мышь появилась,
нажала кнопку, решётка поехала вверх, мышь ушла).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 15:21:20 +03:00

179 lines
7.0 KiB
C

/*
* t_mouse — спецсобытие уровня 8: МЫШЬ (do_timers seg003:0545,
* do_mouse seg003:0ABA, autocontrol_mouse seg002:07EB).
*
* Единственный набор, куда линкуется guards.c: проверяется не физика (её
* держат t_phys/t_char), а связка «условие запуска → рождение → ИИ →
* нажатие кнопки → уход». Всё это ветки по charid внутри общего кода
* соперника, и сломать их можно, ничего не трогая в самой мыши.
*
* Кадр соперника здесь полный, с ИИ — pop_guard_tick() + pop_guard_phys_tick(),
* как в главном цикле; sc_guard_run для этого не годится (он намеренно без ИИ).
*/
#include <stdint.h>
#include "tcheck.h"
#include "pop_kid.h"
#include "pop_map.h"
#include "pop_guard.h"
#include "pop_geom.h"
#include "pop_state.h"
#include "pop_level.h" /* pop_current_level (под тестом — из stubs.c) */
#include "pop_tune.h"
#include "scene.h"
#include "stubs.h"
#define E 0 /* empty */
#define F 1 /* floor */
#define O 15 /* opener */
#define W 20 /* wall */
/* Верхний ряд комнаты 16 в упрощённом виде: сплошной пол и кнопка на
* колонке 7 — там же, где она стоит в данных уровня 8. Провал колонок
* 4..6 намеренно засыпан: он мешает Киду, а не мыши, и в тест на ИИ
* притащил бы падение соперника. */
static const uint8_t room_mouse[30] = {
F, F, F, F, F, F, F, O, F, F,
W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W,
};
/* Поставить сцену уровня 8: Кид в комнате 16, дверь уровня открыта. */
static void scene_level8(void)
{
sc_room(room_mouse, POP_MOUSE_ROOM);
sc_kid_at(1, 0, 1);
Kid.room = POP_MOUSE_ROOM;
pop_guard_reset();
pop_current_level = POP_MOUSE_LEVEL;
pop_leveldoor_open = 1;
tk_log_clear();
}
/* Один полный кадр соперника, как в главном цикле roomtest.c. */
static void mouse_frame(void)
{
pop_guard_tick();
pop_guard_phys_tick();
}
/* ---- рождение -------------------------------------------------------- */
TC_TEST(mouse_appears_after_delay)
{
uint16_t i;
scene_level8();
/* Счётчик стартует с 1 («дверь открыта») и растёт по кадру, срабатывая
* на значении POP_MOUSE_DELAY — то есть кадров до мыши на два меньше.
* Прогоняем всё, кроме последнего: слот обязан быть ещё пуст. */
for (i = 0; i < POP_MOUSE_DELAY - 2; i++) pop_check_mouse();
TC_EQ(Guard.charid, 0);
pop_check_mouse(); /* тот самый кадр */
TC_EQ(Guard.charid, CHARID_24_MOUSE);
TC_EQ(Guard.x, POP_MOUSE_START_X);
TC_EQ(Guard.curr_row, 0);
TC_EQ(Guard.direction, -1); /* dir_FF_left */
TC_EQ(guardhp_curr, 1);
TC_EQ(Guard.room, POP_MOUSE_ROOM); /* унаследована от Кида (loadkid) */
}
/* Счётчик тикает ТОЛЬКО пока Кид в комнате мыши: в оригинале это то же
* условие, что и запуск (Char.room == mouse_room), поэтому 12,5 секунды
* отсчитываются с возвращения игрока, а не с открытия двери. */
TC_TEST(mouse_timer_frozen_outside_room)
{
uint16_t i;
scene_level8();
Kid.room = POP_MOUSE_ROOM + 1;
for (i = 0; i < POP_MOUSE_DELAY * 2; i++) pop_check_mouse();
TC_EQ(Guard.charid, 0);
TC_EQ(pop_leveldoor_open, 1); /* не сдвинулся ни на кадр */
}
/* Закрытая дверь уровня — мыши нет вовсе (она подсказка на обратном пути). */
TC_TEST(mouse_needs_open_leveldoor)
{
uint16_t i;
scene_level8();
pop_leveldoor_open = 0;
for (i = 0; i < POP_MOUSE_DELAY * 2; i++) pop_check_mouse();
TC_EQ(Guard.charid, 0);
}
/* ---- ИИ: пробежала, нажала, ушла ------------------------------------- */
/* Родить мышь, не гоняя POP_MOUSE_DELAY пустых кадров. */
static void spawn_mouse(void)
{
scene_level8();
pop_leveldoor_open = POP_MOUSE_DELAY - 1;
pop_check_mouse();
}
TC_TEST(mouse_runs_left_and_presses_button)
{
uint8_t i;
spawn_mouse();
TC_EQ(Guard.charid, CHARID_24_MOUSE);
/* Бежит влево: до порога разворота ей ~35 пикселей по 3-5 за кадр. */
for (i = 0; i < 24; i++) mouse_frame();
TC_TRUE(Guard.x < POP_MOUSE_START_X);
/* По дороге наступила на кнопку (0,7) — общий check_press физики
* соперника, тот же, которым кнопки жмут страж и тень. */
TC_TRUE(tk_log_count(TK_BUTTON) >= 1);
}
TC_TEST(mouse_turns_back_and_disappears)
{
uint8_t i;
spawn_mouse();
/* 120 кадров с запасом покрывают путь туда (200→166), стойку seq_107
* (11 кадров) и путь обратно (166→200). */
for (i = 0; i < 120; i++) {
mouse_frame();
if (Guard.direction == DIR_56_NONE) break;
}
TC_EQ(Guard.direction, DIR_56_NONE); /* clear_char: ушла со сцены */
TC_EQ(guardhp_curr, 0);
}
/* ---- ветки по charid у соседнего кода -------------------------------- */
/* Мышь Кида «не видит»: иначе он выхватит на неё меч (seg003:0965). */
TC_TEST(mouse_is_not_seen_by_kid)
{
spawn_mouse();
Guard.curr_row = Kid.curr_row; /* один ряд — условие луча */
pop_check_can_guard_see_kid();
TC_EQ(can_guard_see_kid, 0);
}
/* leave_guard мышь не записывает в данные уровня (seg002:02F5): вернувшись
* в комнату, игрок не должен встретить «стража» с её параметрами.
* Комнату берём из диапазона таблицы заглушки (TK_ROOMS = 8). */
TC_TEST(mouse_is_not_saved_as_room_guard)
{
spawn_mouse();
Guard.room = 1;
tk_gs_tile[0] = 0xEE; /* метка «никто сюда не писал» */
pop_guard_leave();
TC_EQ(tk_gs_tile[0], 0xEE);
}
void main(void)
{
sc_init();
TC_RUN(mouse_appears_after_delay);
TC_RUN(mouse_timer_frozen_outside_room);
TC_RUN(mouse_needs_open_leveldoor);
TC_RUN(mouse_runs_left_and_presses_button);
TC_RUN(mouse_turns_back_and_disappears);
TC_RUN(mouse_is_not_seen_by_kid);
TC_RUN(mouse_is_not_saved_as_room_guard);
}