PoP roomtest: убитый страж остаётся мёртвым; меч только по подбору или читу S
Смерть стража теперь персистентна между входами в комнату — по механизму оригинала, а не отдельной таблицей «убит/не убит». В SDLPoP массивы level.guards_* лежат в ОЗУ и движок их ПЕРЕПИСЫВАЕТ: leave_guard (seg002:02F5) кладёт туда позицию/направление/мастерство, а у МЁРТВОГО ещё и curr_seq; enter_guard, увидев непустой seq_hi, поднимает стража прямо в этой последовательности и по кадру смерти (185/177/178) ставит alive = 1. У нас уровень лежит в EMM-странице только на чтение, поэтому в W2 добавлена живая копия — 6 байт на комнату (tile/dir/x/skill/seq_lo/seq_hi): - pop_guard_leave() в начале enter_room запоминает уходящего стража; - pop_guard_enter поднимает труп сохранённой последовательностью И сохранённой X (pos_guards пересчитывает её из колонки только при загрузке уровня, дальше ею владеет leave_guard — иначе тело прыгает в центр тайла). ГРАБЛИ: guards_seq_lo/hi в ФАЙЛЕ уровня не используются, там 0xFF во всех комнатах (оригинал чистит их в reset_level_unused_fields). Прочитав их как есть, я скормил интерпретатору curr_seq = 0xFFFF, и приложение зависало — бордюр оставался синим, цикл не доходил до vsync. Живая копия стартует нулями: 0 = «поднимать стандартной стойкой». Меч Киду больше не выдаётся автоматически: DEBUG_SWORD_ROOM убран, вместо него чит S (выдать меч). Штатный путь — подобрать с пола. Проверено в MAME: чит K убивает стража, уход из комнаты 3 и возврат — тело на месте, страж не воскресает. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,20 @@ void pop_guard_load_frame(void)
|
||||
pop_kid_data_frame(KID_BIN_GFRAMES_OFF, (uint8_t)idx, (uint8_t *)&pop_gframe);
|
||||
}
|
||||
|
||||
/* leave_guard (seg002:02F5): вызывать ПЕРЕД сменой комнаты, пока Guard ещё
|
||||
* держит стража уходящей комнаты. Мёртвого запоминаем вместе с
|
||||
* последовательностью — тогда при возврате он лежит там же, а не встаёт
|
||||
* живым. */
|
||||
void pop_guard_leave(void)
|
||||
{
|
||||
if (Guard.charid == 0) return; /* стража не было */
|
||||
pop_guard_state_save(Guard.room,
|
||||
(uint8_t)(Guard.curr_row * 10), /* get_tilepos(0,row) */
|
||||
Guard.direction, Guard.x, guard_skill,
|
||||
Guard.curr_seq,
|
||||
(uint8_t)(Guard.alive >= 0));
|
||||
}
|
||||
|
||||
void pop_guard_enter(uint8_t room)
|
||||
{
|
||||
uint8_t tile, color, skill;
|
||||
@@ -126,11 +140,25 @@ void pop_guard_enter(uint8_t room)
|
||||
* Уровень 1 -> 3; extrastrength ненулевой только у skill 4. */
|
||||
guardhp_max = guardhp_curr = (uint8_t)(3 + (guard_skill == 4 ? 1 : 0));
|
||||
guardhp_delta = 0;
|
||||
/* enter_guard (seg002:0208): seqtbl_offset_char(seq_77_guard_stand_inactive).
|
||||
* Последовательность крутит общий интерпретатор (pop_guard_play), а
|
||||
* таблицу кадров он выбирает по Char.charid — у стража своя. */
|
||||
pop_loadshad();
|
||||
pop_char_set_seq(SEQ_77_GUARD_STAND_INACTIVE);
|
||||
pop_saveshad();
|
||||
pop_guard_play(); /* до первого кадра */
|
||||
/* enter_guard (seg002:0208). Живого поднимаем стандартной стойкой; у
|
||||
* ЗАПОМНЕННОГО МЁРТВОГО берём сохранённую последовательность — он лежит
|
||||
* ровно там, где лёг (порт ветки guards_seq_hi != 0). */
|
||||
{
|
||||
uint16_t saved_seq = pop_guard_state_seq(room);
|
||||
/* У ЗАПОМНЕННОГО трупа X берём сохранённую: pos_guards (seg003:620)
|
||||
* пересчитывает её из колонки только при загрузке уровня, а дальше
|
||||
* ею владеет leave_guard. Иначе тело при возврате прыгает в центр
|
||||
* тайла. */
|
||||
if (saved_seq) Guard.x = pop_guard_state_x(room);
|
||||
pop_loadshad();
|
||||
if (saved_seq) Char.curr_seq = saved_seq;
|
||||
else pop_char_set_seq(SEQ_77_GUARD_STAND_INACTIVE);
|
||||
pop_saveshad();
|
||||
pop_guard_play(); /* до первого кадра */
|
||||
/* enter_guard (seg002:218): кадр смерти -> страж поднят ТРУПОМ. */
|
||||
if (Guard.frame == 185 || Guard.frame == 177 || Guard.frame == 178) {
|
||||
Guard.alive = 1;
|
||||
guardhp_curr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user