PoP roomtest: HP-учёт, индикаторы HP, чит бессмертия; фикс кэша кадра
Боёвка (порт seg002/seg006): - check_sword_hurting / check_hurting / check_sword_hurt / hurt_by_sword / take_hp через дельты; do_delta_hp сводит их раз в кадр; - парирование (justblocked), refractimer после ранения стража; - смерть по seq_71_dying — через неё же теперь работает чит K: страж действительно погибает, а не замирает на месте; - парные окна Char/Opp: loadkid_and_opp / savekid_and_opp / saveshad_and_opp. Индикаторы HP (порт draw_kid_hp / draw_guard_hp): Кид слева, страж справа. Перерисовка ТОЛЬКО при изменении числа и тогда на ОБЕИХ страницах дабл-буфера (счётчик hp_todo, иначе на второй странице осталось бы старое значение и мерцало через кадр); pop_hp_invalidate при входе в комнату, где фон перерисован целиком. Чит I — бессмертие Кида (нашего изобретения, в оригинале его нет). Перекрывает и путь «безоружного закалывают насмерть»: тот идёт мимо HP, и без этого чит бесполезен ровно там, где нужен. ДВА НАЙДЕННЫХ БАГА: 1. Полосу HP блитил row-major примитивом, а атласы Кида и стража хранятся COLUMN-major (ради бесплатного флипа) — марки выходили транспонированными. Теперь колоночный блит, стрелки как в оригинале. 2. Кэш кадра для ОТРИСОВКИ заполняли pop_savekid/pop_saveshad. Любое окно Char БЕЗ play_seq — а это оба окна боёвки — записывало Киду кадр, который принадлежал СТРАЖУ, и kid_draw искал этот image в атласе Кида, рисуя произвольную позу. Владельцем кэша стал load_frame: он один знает, чей кадр загружен (по Char.charid). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "pop_guard.h"
|
||||
#include "pop_kid.h" /* pop_sword_draw — клинок общий с Кидом */
|
||||
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */
|
||||
#include "pop_map.h" /* hitp_curr/hitp_max — HP Кида */
|
||||
#include "kid_data.h"
|
||||
|
||||
/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */
|
||||
@@ -144,3 +145,79 @@ void pop_guard_draw(void) __banked
|
||||
if (w && h)
|
||||
pop_fore_over_char(fp_x, obj_y, w, h, Guard.direction);
|
||||
}
|
||||
|
||||
/* ---- Индикаторы HP (порт draw_kid_hp / draw_guard_hp, seg000:1416) --- *
|
||||
* Оригинал рисует их в статус-полосе на y = 194 (ниже поля тайлов, внутри
|
||||
* тех же 200 строк экрана PoP): HP Кида слева направо шагом 7, HP стража
|
||||
* справа налево от x = 314. Пустые деления у Кида — свой спрайт (chtab_2
|
||||
* id 217); у стража оригинал печатает тот же спрайт ЧЁРНЫМ силуэтом, а мы
|
||||
* просто не печатаем — полоса и так на чёрном фоне.
|
||||
*
|
||||
* Спрайты Кида (216/217) лежат в его же атласах, поэтому блитим через
|
||||
* pop_kid_img_blit; спрайт стража — image 0 его chtab (наша страница 0). */
|
||||
#define HP_Y 194
|
||||
#define HP_STEP 7
|
||||
#define HP_KID_FULL 216
|
||||
#define HP_KID_EMPTY 217
|
||||
#define HP_MAXDRAW 10 /* больше в полосу всё равно не влезает */
|
||||
|
||||
/* Полоса перерисовывается ТОЛЬКО когда числа изменились — и тогда на ОБЕИХ
|
||||
* страницах дабл-буфера (иначе на второй останется старое значение и его
|
||||
* будет видно как мерцание через кадр). Отсюда счётчик страниц, а не флаг.
|
||||
*
|
||||
* Снимки прошлых значений пишем ДО сравнения: `if (n != s) s = n;` SDCC z80
|
||||
* собирает неверно — SUB затирает A, и в s уезжает разность (memory
|
||||
* sdcc_z80_cmp_store_a_bug). */
|
||||
static uint8_t hp_todo; /* сколько страниц ещё обновить */
|
||||
static uint8_t hp_kid_prev, hp_kidmax_prev, hp_gd_prev, hp_gdmax_prev;
|
||||
|
||||
/* Заставить перерисовать полосу на обеих страницах: звать при входе в
|
||||
* комнату (фон перерисован целиком и стёр её) и при старте. */
|
||||
void pop_hp_invalidate(void) __banked { hp_todo = 2; }
|
||||
|
||||
void pop_hp_draw(void) __banked
|
||||
{
|
||||
uint8_t i, n;
|
||||
uint8_t gd = (uint8_t)(Guard.charid ? guardhp_curr : 0);
|
||||
uint8_t gm = (uint8_t)(Guard.charid ? guardhp_max : 0);
|
||||
|
||||
{ /* Изменилось хоть одно число — обновить обе страницы. */
|
||||
uint8_t k = hitp_curr, km = hitp_max;
|
||||
uint8_t changed = (uint8_t)(k != hp_kid_prev || km != hp_kidmax_prev ||
|
||||
gd != hp_gd_prev || gm != hp_gdmax_prev);
|
||||
hp_kid_prev = k; hp_kidmax_prev = km;
|
||||
hp_gd_prev = gd; hp_gdmax_prev = gm;
|
||||
if (changed) hp_todo = 2;
|
||||
}
|
||||
if (hp_todo == 0) return;
|
||||
hp_todo--;
|
||||
|
||||
/* Стереть прошлую полосу: фон под ней — чёрный борт. */
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
setfillstyle(SOLID_FILL, BLACK);
|
||||
bar(0, HP_Y + POP_YOFF, 319, HP_Y + POP_YOFF + 6);
|
||||
|
||||
/* Кид: сначала пустые деления, поверх — полные (порядок оригинала). */
|
||||
n = (uint8_t)(hitp_max > HP_MAXDRAW ? HP_MAXDRAW : hitp_max);
|
||||
for (i = hitp_curr; i < n; i++)
|
||||
pop_kid_img_blit(HP_KID_EMPTY, (int)i * HP_STEP, HP_Y + POP_YOFF);
|
||||
for (i = 0; i < hitp_curr && i < HP_MAXDRAW; i++)
|
||||
pop_kid_img_blit(HP_KID_FULL, (int)i * HP_STEP, HP_Y + POP_YOFF);
|
||||
|
||||
/* Страж: только полные деления, справа налево. Скелет полосы не имеет. */
|
||||
if (g_ok && Guard.charid != 0 && Guard.charid != CHARID_4_SKELETON && gd) {
|
||||
const uint8_t *img = (const uint8_t *)atlas_image(&gp[0], 0);
|
||||
uint16_t w, h;
|
||||
gfx_w0_map(gp[0].page);
|
||||
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||||
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||||
if (w && h) {
|
||||
/* Атлас стража тоже column-major — блит колоночный. */
|
||||
n = (uint8_t)(gd > HP_MAXDRAW ? HP_MAXDRAW : gd);
|
||||
for (i = 0; i < n; i++)
|
||||
gfx_blit_cols_part_noclip(314 - (int)i * HP_STEP,
|
||||
HP_Y + POP_YOFF, img, 0, 0, 0);
|
||||
}
|
||||
gfx_w0_unmap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user