PoP roomtest: окно Char вокруг control() + общая шина ввода — база под ИИ стража
Инфраструктура пункта 2. В оригинале у соперника НЕТ своего диспетчера: autocontrol_* (seg002) выставляет те же глобалы control_*, что и ввод игрока, а дальше исполняется тот же control() (seg005:252). Значит перед портом ИИ надо было привести к этому обе половины: - control_forward/backward/up/down/shift2 перестали быть static в pop_ctrl.c — это общая шина синтетического ввода, объявлена в pop_ctrl.h вместе с POP_CONTROL_*; - control() переименован в pop_control() и работает с Char, а не с Kid; - ввод игрока обёрнут в окно Char (loadkid/user_control/savekid), как в play_frame оригинала. Ловушка по дороге (ввод отвалился целиком, Kid не двигался): макрос seqtbl_offset_char вёл на kid_set_seq, который пишет прямо в Kid, а следом savekid затирал Kid копией Char со старой curr_seq. В оригинале seqtbl_offset_char работает именно с Char — макрос переведён на pop_char_set_seq. kid_set_seq остался для вызовов ВНЕ окна (pop_map). Добавлен pop_savekid_state (Kid = Char без кадра): control() кадр не трогает, а cur_frame в этот момент принадлежит тому, кто последним крутил play_seq. Проверено в MAME: бег и упор в стену работают как прежде. Бюджет (комната 3, 125 кадров): 382 584..383 064 против 380 292..381 282, то есть +2 300 тактов на копии окна. 0.891 кадра, запас 46 936. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -75,13 +75,20 @@
|
||||
* автомат held/released/ignore (авто-повтор); ctrl1_* — их память между
|
||||
* кадрами (rest/save). */
|
||||
static int8_t control_x, control_y, control_shift;
|
||||
static int8_t control_forward, control_backward, control_up, control_down, control_shift2;
|
||||
/* НЕ static: в оригинале это глобалы, через которые ИИ стража подаёт
|
||||
* СИНТЕТИЧЕСКИЙ ввод (autocontrol_*, seg002), а дальше работает тот же
|
||||
* control(), что и у игрока. Поэтому шина общая. */
|
||||
int8_t control_forward, control_backward, control_up, control_down, control_shift2;
|
||||
static int8_t ctrl1_forward, ctrl1_backward, ctrl1_up, ctrl1_down, ctrl1_shift2;
|
||||
|
||||
/* ---- seqtbl_offset_char: сменить последовательность Kid ------------ *
|
||||
* Обёртка над kid_set_seq (единая точка в pop_kid — kid_seq_off/kid_seqtbl
|
||||
* не дублируются в этот TU). */
|
||||
#define seqtbl_offset_char(seq) kid_set_seq(seq)
|
||||
/* В оригинале seqtbl_offset_char работает с АКТИВНЫМ персонажем, и
|
||||
* control() вызывается внутри окна Char. Раньше макрос вёл на
|
||||
* kid_set_seq (пишет прямо в Kid) — после введения окна это ломало ввод:
|
||||
* savekid затирал Kid копией Char со старой curr_seq. */
|
||||
#define seqtbl_offset_char(seq) pop_char_set_seq(seq)
|
||||
|
||||
static int8_t release_arrows(void)
|
||||
{
|
||||
@@ -125,7 +132,7 @@ static void run_jump(void)
|
||||
{
|
||||
/* Оригинал выравнивает Kid по краю пола (нужны tile-запросы) — это
|
||||
* полировка K3; K2b просто запускает run-jump. */
|
||||
if (Kid.frame >= FRAME_7_RUN) {
|
||||
if (Char.frame >= FRAME_7_RUN) {
|
||||
control_up = release_arrows();
|
||||
seqtbl_offset_char(SEQ_4_RUN_JUMP);
|
||||
}
|
||||
@@ -190,15 +197,15 @@ static void safe_step(void)
|
||||
control_forward = CONTROL_IGNORE;
|
||||
distance = pop_edge_distance();
|
||||
if (distance) {
|
||||
Kid.repeat = 1;
|
||||
Char.repeat = 1;
|
||||
seqtbl_offset_char((uint8_t)(distance + SEQ_STEP_BASE));
|
||||
} else if (pop_edge_type() == EDGE_WALL) {
|
||||
/* вплотную к стене: маленький шаг-1 — check_bumped отскочит назад
|
||||
* (шаг назад со звуком, как оригинал), НЕ seq_39 (шаг за край=падение). */
|
||||
Kid.repeat = 1;
|
||||
Char.repeat = 1;
|
||||
seqtbl_offset_char((uint8_t)(1 + SEQ_STEP_BASE));
|
||||
} else if (Kid.repeat != 0) {
|
||||
Kid.repeat = 0;
|
||||
} else if (Char.repeat != 0) {
|
||||
Char.repeat = 0;
|
||||
seqtbl_offset_char(SEQ_44_STEP_ON_EDGE);
|
||||
} else {
|
||||
seqtbl_offset_char(SEQ_39_UNSAFE_STEP); /* реальный обрыв (edge=CLOSER) */
|
||||
@@ -266,7 +273,7 @@ static void control_running(void)
|
||||
/* Стену НЕ обрабатываем тут — check_bumped (pop_phys_tick) даёт отскок
|
||||
* seq_47 при беге в стену (как оригинал), с корректной позицией. */
|
||||
if (control_x == CONTROL_RELEASED &&
|
||||
(Kid.frame == FRAME_7_RUN || Kid.frame == FRAME_11_RUN)) {
|
||||
(Char.frame == FRAME_7_RUN || Char.frame == FRAME_11_RUN)) {
|
||||
control_forward = release_arrows();
|
||||
seqtbl_offset_char(SEQ_13_STOP_RUN);
|
||||
} else if (control_x == CONTROL_HELD_BACKWARD) {
|
||||
@@ -318,16 +325,16 @@ static void control_jumpup(void)
|
||||
}
|
||||
|
||||
/* control() — диспетчер по кадру (seg005.c:252), Kid жив, без меча. */
|
||||
static void control(void)
|
||||
void pop_control(void)
|
||||
{
|
||||
uint8_t f = Kid.frame;
|
||||
uint8_t f = Char.frame;
|
||||
/* seg005 control(): в отскоке (bumped) и в свободном падении управление
|
||||
* НЕ обрабатывается вовсе — только гасятся стрелки. Без этого игрок
|
||||
* прерывает присед после удара (medland/плита по голове) нажатием ↑, и
|
||||
* последовательность не доигрывает свои dy(1)+dy(1), компенсирующие
|
||||
* стартовый dy(-2) → Kid навсегда остаётся на 2px выше пола (баг «Y на
|
||||
* пару пикселей меньше, чем должен»). */
|
||||
if (Kid.action == 5 || Kid.action == 4) { /* bumped / in_freefall */
|
||||
if (Char.action == 5 || Char.action == 4) { /* bumped / in_freefall */
|
||||
release_arrows();
|
||||
return;
|
||||
}
|
||||
@@ -406,12 +413,12 @@ static void flip_control_x(void)
|
||||
/* user_control (seg006:0DC0): «вперёд» относительно направления Kid. */
|
||||
static void user_control(void)
|
||||
{
|
||||
if (Kid.direction >= DIR_RIGHT) {
|
||||
if (Char.direction >= DIR_RIGHT) {
|
||||
flip_control_x();
|
||||
control();
|
||||
pop_control();
|
||||
flip_control_x();
|
||||
} else {
|
||||
control();
|
||||
pop_control();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,6 +458,12 @@ void pop_ctrl_tick(void)
|
||||
rest_ctrl_1();
|
||||
read_input();
|
||||
read_user_control();
|
||||
user_control(); /* -> control(): может сменить Kid.curr_seq */
|
||||
/* Окно Char вокруг диспетчера: control() работает с АКТИВНЫМ
|
||||
* персонажем (как в оригинале), поэтому ввод игрока применяется к
|
||||
* Киду только внутри loadkid/savekid. Кадр не перезагружаем —
|
||||
* play_seq в kid_tick сделает это следующим шагом. */
|
||||
pop_loadkid();
|
||||
user_control(); /* -> pop_control(): может сменить Char.curr_seq */
|
||||
pop_savekid_state();
|
||||
save_ctrl_1();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user