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:
@@ -21,6 +21,7 @@
|
|||||||
#include "pop_map.h"
|
#include "pop_map.h"
|
||||||
#include "pop_geom.h"
|
#include "pop_geom.h"
|
||||||
#include "pop_trob.h"
|
#include "pop_trob.h"
|
||||||
|
#include "pop_state.h" /* pop_immortal — чит бессмертия */
|
||||||
|
|
||||||
#define TILE_MIDX 7 /* середина тайла в координатах персонажа */
|
#define TILE_MIDX 7 /* середина тайла в координатах персонажа */
|
||||||
#define SCREENSPACE_X 58 /* = pop_x_bump[FIRST_ONSCREEN_COLUMN] */
|
#define SCREENSPACE_X 58 /* = pop_x_bump[FIRST_ONSCREEN_COLUMN] */
|
||||||
@@ -342,9 +343,142 @@ static void autocontrol_opponent(void)
|
|||||||
else autocontrol_guard_active();
|
else autocontrol_guard_active();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* play_guard (seg006): ИИ + общий диспетчер. */
|
/* ---- Урон мечом (seg002:0CD4..0E1F) -------------------------------- */
|
||||||
|
#define ACTION_99_HURT 99
|
||||||
|
#define ACTION_1_RUN 1
|
||||||
|
#define FRAME_154_POKING 154
|
||||||
|
#define SEQ_69_ATTACK_WAS_PARRIED 69
|
||||||
|
#define SEQ_74_HIT_BY_SWORD 74
|
||||||
|
#define SEQ_85_STABBED_TO_DEATH 85
|
||||||
|
|
||||||
|
/* refractimer (seg002:36) — «отдышка» стража после того, как его ранили. */
|
||||||
|
static const uint8_t REFRACTIMER[NUM_GUARD_SKILLS] =
|
||||||
|
{ 16, 16, 16, 16, 8, 8, 8, 8, 0, 8, 0, 0};
|
||||||
|
|
||||||
|
/* take_hp (seg006): отнять count HP у АКТИВНОГО персонажа через дельту.
|
||||||
|
* Возврат 1 — HP кончились. Чит бессмертия ловим здесь: анимацию удара
|
||||||
|
* Кид получает, а HP не теряет. */
|
||||||
|
static uint8_t take_hp(uint8_t count)
|
||||||
|
{
|
||||||
|
if (Char.charid == CHARID_0_KID) {
|
||||||
|
if (pop_immortal) return 0;
|
||||||
|
if (count >= hitp_curr) { hitp_delta = (int8_t)-hitp_curr; return 1; }
|
||||||
|
hitp_delta = (int8_t)-(int8_t)count;
|
||||||
|
} else {
|
||||||
|
if (count >= guardhp_curr) { guardhp_delta = (int8_t)-guardhp_curr; return 1; }
|
||||||
|
guardhp_delta = (int8_t)-(int8_t)count;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hurt_by_sword (seg002): применить попадание к АКТИВНОМУ персонажу.
|
||||||
|
* Без меча в руке любое попадание смертельно; с мечом — минус 1 HP и кадр
|
||||||
|
* «получил удар».
|
||||||
|
* УПРОЩЕНИЕ: ветку «сбит с уступа» (seq_81, когда сзади пусто и до кромки
|
||||||
|
* меньше 4) не портируем — ей нужны тайловые запросы ОТ Char, а pop_map
|
||||||
|
* пока умеет только от Kid. На ровном полу (тайл сзади не пустой) оригинал
|
||||||
|
* идёт ровно нашей веткой. */
|
||||||
|
static void hurt_by_sword(void)
|
||||||
|
{
|
||||||
|
if (Char.alive >= 0) return;
|
||||||
|
/* Чит бессмертия перекрывает и «безоружного закалывают насмерть»:
|
||||||
|
* без этого Кид гибнет с одного удара мимо стойки, и чит бесполезен
|
||||||
|
* ровно там, где нужен. Анимацию удара оставляем. */
|
||||||
|
if (Char.charid == CHARID_0_KID && pop_immortal) {
|
||||||
|
pop_char_set_seq(SEQ_74_HIT_BY_SWORD);
|
||||||
|
Char.y = (uint8_t)pop_y_land[Char.curr_row + 1];
|
||||||
|
Char.fall_y = 0;
|
||||||
|
play_seq();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Char.sword != SWORD_2_DRAWN) {
|
||||||
|
take_hp(100);
|
||||||
|
pop_char_set_seq(SEQ_85_STABBED_TO_DEATH);
|
||||||
|
} else if (Char.charid != CHARID_4_SKELETON && take_hp(1)) {
|
||||||
|
pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); /* HP кончились */
|
||||||
|
} else {
|
||||||
|
pop_char_set_seq(SEQ_74_HIT_BY_SWORD);
|
||||||
|
}
|
||||||
|
Char.y = (uint8_t)pop_y_land[Char.curr_row + 1];
|
||||||
|
Char.fall_y = 0;
|
||||||
|
play_seq(); /* TODO: звук 12/13 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check_hurting (seg002:0D56): достал ли АКТИВНЫЙ персонаж соперника.
|
||||||
|
* Бьют кадры 153/154; если соперник в этот момент парирует — удар отбит. */
|
||||||
|
static void check_hurting(void)
|
||||||
|
{
|
||||||
|
int16_t distance;
|
||||||
|
uint8_t cf, of, min_range;
|
||||||
|
|
||||||
|
if (Char.sword != SWORD_2_DRAWN) return;
|
||||||
|
if (Char.curr_row != Opp.curr_row) return;
|
||||||
|
cf = Char.frame;
|
||||||
|
if (cf != FRAME_153_STRIKE_3 && cf != FRAME_154_POKING) return;
|
||||||
|
|
||||||
|
distance = pop_char_opp_dist();
|
||||||
|
of = Opp.frame;
|
||||||
|
if (distance < 0 || distance >= 29 ||
|
||||||
|
(of != FRAME_161_PARRY && of != FRAME_150_PARRY)) {
|
||||||
|
/* Соперник НЕ парирует. */
|
||||||
|
if (cf == FRAME_154_POKING) {
|
||||||
|
min_range = (uint8_t)(Opp.sword < SWORD_2_DRAWN ? 8 : 12);
|
||||||
|
distance = pop_char_opp_dist();
|
||||||
|
if (distance >= (int16_t)min_range && distance < 29)
|
||||||
|
Opp.action = ACTION_99_HURT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Парировано. */
|
||||||
|
Opp.frame = FRAME_161_PARRY;
|
||||||
|
if (Char.charid != CHARID_0_KID) justblocked = 4;
|
||||||
|
pop_char_set_seq(SEQ_69_ATTACK_WAS_PARRIED);
|
||||||
|
play_seq();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check_sword_hurting (seg002:0D1A): прогнать проверку с ОБЕИХ сторон. */
|
||||||
|
void pop_check_sword_hurting(void) __banked
|
||||||
|
{
|
||||||
|
uint8_t kf = Kid.frame;
|
||||||
|
if (kf == 0 || (kf >= 219 && kf < 229)) return; /* подъём по лестнице */
|
||||||
|
pop_loadshad_and_opp();
|
||||||
|
check_hurting();
|
||||||
|
pop_saveshad_and_opp();
|
||||||
|
pop_loadkid_and_opp();
|
||||||
|
check_hurting();
|
||||||
|
pop_savekid_and_opp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check_sword_hurt (seg002:0CD4): применить помеченные попадания. Если
|
||||||
|
* ранен страж — Киду в этом кадре урон уже не проходит (в оригинале так же:
|
||||||
|
* его action сбрасывается ДО проверки). */
|
||||||
|
void pop_check_sword_hurt(void) __banked
|
||||||
|
{
|
||||||
|
if (Guard.action == ACTION_99_HURT) {
|
||||||
|
if (Kid.action == ACTION_99_HURT) Kid.action = ACTION_1_RUN;
|
||||||
|
pop_loadshad();
|
||||||
|
hurt_by_sword();
|
||||||
|
pop_saveshad();
|
||||||
|
guard_refrac = (int8_t)REFRACTIMER[guard_skill];
|
||||||
|
} else if (Kid.action == ACTION_99_HURT) {
|
||||||
|
pop_loadkid();
|
||||||
|
hurt_by_sword();
|
||||||
|
pop_savekid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* play_guard (seg006): ИИ + общий диспетчер. Отдельная ветка «HP кончились»:
|
||||||
|
* пока страж жив (alive < 0) и HP есть — обычный ход; как только HP дошли до
|
||||||
|
* нуля, он переводится в «умирает» (alive = 0), и дальше тот же control()
|
||||||
|
* ставит ему seq_71_dying. Уже умирающий сюда попадает с alive >= 0 и просто
|
||||||
|
* доигрывает последовательность. */
|
||||||
static void play_guard(void)
|
static void play_guard(void)
|
||||||
{
|
{
|
||||||
|
if (Char.alive < 0 && guardhp_curr == 0) {
|
||||||
|
Char.alive = 0;
|
||||||
|
/* on_guard_killed (seg006:1927) — у нас пока только звук победы,
|
||||||
|
* которого нет; для уровня 1 других эффектов там нет. */
|
||||||
|
}
|
||||||
autocontrol_opponent();
|
autocontrol_opponent();
|
||||||
pop_control();
|
pop_control();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,5 +17,6 @@
|
|||||||
extern uint8_t pop_cheats; /* 0 = выключены, иначе включены */
|
extern uint8_t pop_cheats; /* 0 = выключены, иначе включены */
|
||||||
|
|
||||||
#define KBD_CHEAT_KILL 0x42 /* K (PS/2 set 2) — kill guard */
|
#define KBD_CHEAT_KILL 0x42 /* K (PS/2 set 2) — kill guard */
|
||||||
|
#define KBD_CHEAT_IMMO 0x43 /* I (PS/2 set 2) — бессмертие Кида (toggle) */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ static void run_jump(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void safe_step(void); /* fwd */
|
static void safe_step(void); /* fwd */
|
||||||
|
static void draw_sword(void); /* fwd: нужен в control_standing */
|
||||||
|
|
||||||
static void forward_pressed(void)
|
static void forward_pressed(void)
|
||||||
{
|
{
|
||||||
@@ -233,7 +234,32 @@ static void control_standing(void)
|
|||||||
if (control_shift2 == CONTROL_HELD && control_shift == CONTROL_HELD &&
|
if (control_shift2 == CONTROL_HELD && control_shift == CONTROL_HELD &&
|
||||||
get_item_action())
|
get_item_action())
|
||||||
return;
|
return;
|
||||||
/* Kid charid_0, без меча: остаётся нижняя часть control_standing. */
|
/* Ветку «charid != kid && вниз+вперёд -> draw_sword» не портируем: у нас
|
||||||
|
* соперник до control_standing не доходит, его перехватывает
|
||||||
|
* control_guard_inactive в самом диспетчере (порядок как в seg005). */
|
||||||
|
|
||||||
|
/* seg005:352 — Кид с мечом ВИДИТ активного стража: достаёт клинок сам.
|
||||||
|
* Без этого игрок стоит с мечом в ножнах, пока его рубят. */
|
||||||
|
if (pop_have_sword) {
|
||||||
|
if (offguard != 0 && control_shift >= CONTROL_RELEASED) {
|
||||||
|
/* Меч убран намеренно (down в бою) — не доставать, пока не
|
||||||
|
* отпустят Shift; в оригинале это переход на loc_6213. */
|
||||||
|
} else if (can_guard_see_kid >= 2) {
|
||||||
|
int16_t d = pop_char_opp_dist();
|
||||||
|
if (d >= -10 && d < 90) {
|
||||||
|
holding_sword = 1;
|
||||||
|
/* (word)d < (word)-6 — беззнаковое: истинно для ВСЕГО, кроме
|
||||||
|
* −6..−1. То есть «страж вплотную за спиной» -> отступить,
|
||||||
|
* иначе достать меч. */
|
||||||
|
if (d >= -6 && d < 0) { back_pressed(); return; }
|
||||||
|
draw_sword();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offguard = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (control_shift == CONTROL_HELD) {
|
if (control_shift == CONTROL_HELD) {
|
||||||
if (control_backward == CONTROL_HELD) back_pressed();
|
if (control_backward == CONTROL_HELD) back_pressed();
|
||||||
else if (control_up == CONTROL_HELD) up_pressed();
|
else if (control_up == CONTROL_HELD) up_pressed();
|
||||||
@@ -333,9 +359,11 @@ static void draw_sword(void)
|
|||||||
{
|
{
|
||||||
uint8_t seq_id = SEQ_55_DRAW_SWORD;
|
uint8_t seq_id = SEQ_55_DRAW_SWORD;
|
||||||
control_forward = control_shift2 = release_arrows();
|
control_forward = control_shift2 = release_arrows();
|
||||||
if (Char.charid != CHARID_0_KID && Char.charid != CHARID_1_SHADOW)
|
if (Char.charid == CHARID_0_KID) {
|
||||||
seq_id = SEQ_90_EN_GARDE;
|
offguard = 0; /* TODO: play_sound(19) — звука пока нет */
|
||||||
/* TODO: у Кида здесь play_sound(19) + offguard = 0 — звука пока нет. */
|
} else if (Char.charid != CHARID_1_SHADOW) {
|
||||||
|
seq_id = SEQ_90_EN_GARDE; /* соперник встаёт сразу в стойку */
|
||||||
|
}
|
||||||
Char.sword = SWORD_2_DRAWN;
|
Char.sword = SWORD_2_DRAWN;
|
||||||
seqtbl_offset_char(seq_id);
|
seqtbl_offset_char(seq_id);
|
||||||
}
|
}
|
||||||
@@ -455,8 +483,9 @@ static void swordfight(void)
|
|||||||
control_down = CONTROL_IGNORE;
|
control_down = CONTROL_IGNORE;
|
||||||
Char.sword = SWORD_0_SHEATHED;
|
Char.sword = SWORD_0_SHEATHED;
|
||||||
if (Char.charid == CHARID_0_KID) {
|
if (Char.charid == CHARID_0_KID) {
|
||||||
/* TODO: offguard/holding_sword — вместе с боёвкой Кида. */
|
offguard = 1; /* убрал намеренно — сразу не доставать */
|
||||||
guard_refrac = 9;
|
guard_refrac = 9; /* и страж на это время придержан */
|
||||||
|
holding_sword = 0;
|
||||||
seq_id = SEQ_93_PUT_SWORD_AWAY_FAST;
|
seq_id = SEQ_93_PUT_SWORD_AWAY_FAST;
|
||||||
} else if (Char.charid == CHARID_1_SHADOW) {
|
} else if (Char.charid == CHARID_1_SHADOW) {
|
||||||
seq_id = SEQ_92_PUT_SWORD_AWAY;
|
seq_id = SEQ_92_PUT_SWORD_AWAY;
|
||||||
@@ -505,6 +534,16 @@ static void control_with_sword(void)
|
|||||||
void pop_control(void)
|
void pop_control(void)
|
||||||
{
|
{
|
||||||
uint8_t f = Char.frame;
|
uint8_t f = Char.frame;
|
||||||
|
/* seg005 control(), самая первая ветка: персонаж УМИРАЕТ (alive >= 0).
|
||||||
|
* Из любой стойки он валится в seq_71_dying; остальное управление в
|
||||||
|
* этом состоянии не обрабатывается. Через это же место проходит чит K
|
||||||
|
* (pop_guard_kill ставит Guard.alive = 0). */
|
||||||
|
if (Char.alive >= 0) {
|
||||||
|
if (f == FRAME_15_STAND || f == FRAME_166_STAND_INACTIVE ||
|
||||||
|
f == FRAME_158_STAND_WITH_SWORD || f == FRAME_171_STAND_WITH_SWORD)
|
||||||
|
seqtbl_offset_char(SEQ_71_DYING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* seg005 control(): в отскоке (bumped) и в свободном падении управление
|
/* seg005 control(): в отскоке (bumped) и в свободном падении управление
|
||||||
* НЕ обрабатывается вовсе — только гасятся стрелки. Без этого игрок
|
* НЕ обрабатывается вовсе — только гасятся стрелки. Без этого игрок
|
||||||
* прерывает присед после удара (medland/плита по голове) нажатием ↑, и
|
* прерывает присед после удара (medland/плита по голове) нажатием ↑, и
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "pop_guard.h"
|
#include "pop_guard.h"
|
||||||
#include "pop_kid.h" /* pop_sword_draw — клинок общий с Кидом */
|
#include "pop_kid.h" /* pop_sword_draw — клинок общий с Кидом */
|
||||||
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */
|
#include "pop_bg.h" /* POP_YOFF, pop_fore_over_char */
|
||||||
|
#include "pop_map.h" /* hitp_curr/hitp_max — HP Кида */
|
||||||
#include "kid_data.h"
|
#include "kid_data.h"
|
||||||
|
|
||||||
/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */
|
/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */
|
||||||
@@ -144,3 +145,79 @@ void pop_guard_draw(void) __banked
|
|||||||
if (w && h)
|
if (w && h)
|
||||||
pop_fore_over_char(fp_x, obj_y, w, h, Guard.direction);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "pop_geom.h"
|
#include "pop_geom.h"
|
||||||
#include "pop_level.h"
|
#include "pop_level.h"
|
||||||
#include "pop_kid.h" /* Char/play_seq — интерпретатор общий */
|
#include "pop_kid.h" /* Char/play_seq — интерпретатор общий */
|
||||||
|
#include "pop_map.h" /* hitp_* — HP Кида */
|
||||||
#include "kid_data.h"
|
#include "kid_data.h"
|
||||||
|
|
||||||
pop_char_t Guard;
|
pop_char_t Guard;
|
||||||
@@ -19,6 +20,7 @@ int8_t can_guard_see_kid;
|
|||||||
int8_t is_guard_notice;
|
int8_t is_guard_notice;
|
||||||
uint8_t guard_skill;
|
uint8_t guard_skill;
|
||||||
int8_t guard_refrac, justblocked, kid_sword_strike;
|
int8_t guard_refrac, justblocked, kid_sword_strike;
|
||||||
|
int8_t offguard, holding_sword;
|
||||||
|
|
||||||
/* Сид бросков боёвки. В оригинале генератор один на всё, у нас
|
/* Сид бросков боёвки. В оригинале генератор один на всё, у нас
|
||||||
* последовательности разведены по доменам (кладка / анимация тайлов / бой),
|
* последовательности разведены по доменам (кладка / анимация тайлов / бой),
|
||||||
@@ -44,6 +46,7 @@ void pop_guard_reset(void)
|
|||||||
can_guard_see_kid = 0;
|
can_guard_see_kid = 0;
|
||||||
is_guard_notice = 0;
|
is_guard_notice = 0;
|
||||||
guard_refrac = justblocked = kid_sword_strike = 0;
|
guard_refrac = justblocked = kid_sword_strike = 0;
|
||||||
|
offguard = holding_sword = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* kill guard cheat (seg000:786):
|
/* kill guard cheat (seg000:786):
|
||||||
@@ -56,11 +59,35 @@ void pop_guard_kill(void)
|
|||||||
{
|
{
|
||||||
if (Guard.charid == CHARID_4_SKELETON) return;
|
if (Guard.charid == CHARID_4_SKELETON) return;
|
||||||
if (!pop_guard_present()) return;
|
if (!pop_guard_present()) return;
|
||||||
|
/* Как в оригинале: HP НЕ обнуляем здесь — ставим дельту, её применит
|
||||||
|
* pop_do_delta_hp. alive = 0 переводит стража в «умирает», а дальше
|
||||||
|
* общий control() ставит ему seq_71_dying. */
|
||||||
guardhp_delta = (int8_t)-guardhp_curr;
|
guardhp_delta = (int8_t)-guardhp_curr;
|
||||||
guardhp_curr = 0;
|
|
||||||
Guard.alive = 0;
|
Guard.alive = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* do_delta_hp (seg003): применить накопленную за кадр дельту HP и обнулить
|
||||||
|
* её. Звать раз в кадр ПОСЛЕ тиков персонажей, как в play_frame. */
|
||||||
|
void pop_do_delta_hp(void)
|
||||||
|
{
|
||||||
|
int16_t hp;
|
||||||
|
if (hitp_delta) {
|
||||||
|
hp = (int16_t)hitp_curr + hitp_delta;
|
||||||
|
if (hp < 0) hp = 0;
|
||||||
|
if (hp > (int16_t)hitp_max) hp = (int16_t)hitp_max;
|
||||||
|
hitp_curr = (uint8_t)hp;
|
||||||
|
pop_kid_hurt = 1; /* -> «брызги» поверх Кида */
|
||||||
|
hitp_delta = 0;
|
||||||
|
}
|
||||||
|
if (guardhp_delta) {
|
||||||
|
hp = (int16_t)guardhp_curr + guardhp_delta;
|
||||||
|
if (hp < 0) hp = 0;
|
||||||
|
if (hp > (int16_t)guardhp_max) hp = (int16_t)guardhp_max;
|
||||||
|
guardhp_curr = (uint8_t)hp;
|
||||||
|
guardhp_delta = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* load_frame для стража (seg006:0293 ветка charid_2_guard): таблица у
|
/* load_frame для стража (seg006:0293 ветка charid_2_guard): таблица у
|
||||||
* стражей СВОЯ (frame_tbl_guard), индекс = frame + add_frame − 149, где
|
* стражей СВОЯ (frame_tbl_guard), индекс = frame + add_frame − 149, где
|
||||||
* add_frame = 70 для кадров 102..106 (seg006:535). */
|
* add_frame = 70 для кадров 102..106 (seg006:535). */
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#define SEQ_61_PARRY_AFTER_STRIKE 61
|
#define SEQ_61_PARRY_AFTER_STRIKE 61
|
||||||
#define SEQ_62_PARRY 62
|
#define SEQ_62_PARRY 62
|
||||||
#define SEQ_66_STRIKE_AFTER_PARRY 66
|
#define SEQ_66_STRIKE_AFTER_PARRY 66
|
||||||
|
#define SEQ_71_DYING 71
|
||||||
#define SEQ_75_STRIKE 75 /* удар (Кид) */
|
#define SEQ_75_STRIKE 75 /* удар (Кид) */
|
||||||
#define SEQ_77_GUARD_STAND_INACTIVE 77
|
#define SEQ_77_GUARD_STAND_INACTIVE 77
|
||||||
#define SEQ_80_STAND_FLIPPED 80
|
#define SEQ_80_STAND_FLIPPED 80
|
||||||
@@ -95,6 +96,12 @@ extern int8_t is_guard_notice;
|
|||||||
extern uint8_t guard_skill;
|
extern uint8_t guard_skill;
|
||||||
extern int8_t guard_refrac, justblocked, kid_sword_strike;
|
extern int8_t guard_refrac, justblocked, kid_sword_strike;
|
||||||
|
|
||||||
|
/* Состояние меча КИДА (seg005/seg006):
|
||||||
|
* offguard — меч убран НАМЕРЕННО (вниз в бою): пока стоит, Кид не
|
||||||
|
* достаёт клинок автоматически, даже видя стража;
|
||||||
|
* holding_sword — клинок в руке (для индикатора HP стража). */
|
||||||
|
extern int8_t offguard, holding_sword;
|
||||||
|
|
||||||
/* Сид бросков боёвки (свой домен последовательности, см. pop_geom.h). */
|
/* Сид бросков боёвки (свой домен последовательности, см. pop_geom.h). */
|
||||||
extern pop_rnd_t pop_fight_seed;
|
extern pop_rnd_t pop_fight_seed;
|
||||||
|
|
||||||
@@ -102,6 +109,11 @@ extern pop_rnd_t pop_fight_seed;
|
|||||||
* check_can_guard_see_kid стоит в play_frame перед play_kid_frame). */
|
* check_can_guard_see_kid стоит в play_frame перед play_kid_frame). */
|
||||||
void pop_check_can_guard_see_kid(void) __banked;
|
void pop_check_can_guard_see_kid(void) __banked;
|
||||||
|
|
||||||
|
/* Боёвка: пометить попадания (обе стороны) и применить их. Порядок в
|
||||||
|
* кадре как в play_frame: hurting -> hurt -> do_delta_hp. */
|
||||||
|
void pop_check_sword_hurting(void) __banked;
|
||||||
|
void pop_check_sword_hurt(void) __banked;
|
||||||
|
|
||||||
/* Текущий кадр стража (порт cur_frame для Char): заполняет логика в W1/W2,
|
/* Текущий кадр стража (порт cur_frame для Char): заполняет логика в W1/W2,
|
||||||
* читает отрисовка в резиденте W3. image == 255 — рисовать нечего. */
|
* читает отрисовка в резиденте W3. image == 255 — рисовать нечего. */
|
||||||
extern kframe pop_gframe;
|
extern kframe pop_gframe;
|
||||||
@@ -122,6 +134,12 @@ void pop_guard_enter(uint8_t room);
|
|||||||
/* Нарисовать текущий кадр стража на back-странице (банк GFX_BANK_SPRITE,
|
/* Нарисовать текущий кадр стража на back-странице (банк GFX_BANK_SPRITE,
|
||||||
* как kid_draw) и стереть прошлый (heal). Реализация — pop_gdraw.c
|
* как kid_draw) и стереть прошлый (heal). Реализация — pop_gdraw.c
|
||||||
* (резидент W3), поэтому звать ТОЛЬКО из главного цикла, не из банка. */
|
* (резидент W3), поэтому звать ТОЛЬКО из главного цикла, не из банка. */
|
||||||
|
/* Полоса HP (Кид слева, страж справа) в статус-полосе. Рисует главный цикл
|
||||||
|
* ПОСЛЕ персонажей — она ничего не перекрывает. */
|
||||||
|
void pop_hp_draw(void) __banked;
|
||||||
|
/* Полоса стёрта перерисовкой фона (вход в комнату) — обновить обе страницы. */
|
||||||
|
void pop_hp_invalidate(void) __banked;
|
||||||
|
|
||||||
void pop_guard_heal(void) __banked;
|
void pop_guard_heal(void) __banked;
|
||||||
void pop_guard_draw(void) __banked;
|
void pop_guard_draw(void) __banked;
|
||||||
|
|
||||||
@@ -132,6 +150,10 @@ void pop_guard_reset(void);
|
|||||||
* при включённых читах — проверку делает вызывающий. */
|
* при включённых читах — проверку делает вызывающий. */
|
||||||
void pop_guard_kill(void);
|
void pop_guard_kill(void);
|
||||||
|
|
||||||
|
/* do_delta_hp (seg003): применить дельту HP, накопленную за кадр. Звать
|
||||||
|
* раз в кадр ПОСЛЕ тиков персонажей (в оригинале — в play_frame). */
|
||||||
|
void pop_do_delta_hp(void);
|
||||||
|
|
||||||
/* Тик логики стража — В БАНКЕ (guards.c). Состояние берёт отсюда. */
|
/* Тик логики стража — В БАНКЕ (guards.c). Состояние берёт отсюда. */
|
||||||
void pop_guard_tick(void) __banked;
|
void pop_guard_tick(void) __banked;
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,27 @@ uint8_t pop_sword_draw(const pop_char_t *ch, const kframe *fr,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Блит спрайта chtab_2 (атласы Kid) по ЕГО id, левым верхним углом в (x,y).
|
||||||
|
* Нужен полосе HP: она рисует id 216/217 из тех же атласов, что и кадры
|
||||||
|
* Кида, а держать вторую копию раскладки в pop_gdraw ни к чему. */
|
||||||
|
void pop_kid_img_blit(uint8_t image, int x, int y)
|
||||||
|
{
|
||||||
|
uint8_t pg = (uint8_t)(image >> 3); /* раскладка атласов: 8 спрайтов/стр. */
|
||||||
|
uint8_t idx = (uint8_t)(image & 7);
|
||||||
|
const uint8_t *img;
|
||||||
|
uint16_t w, h;
|
||||||
|
if (pg >= kid_npages) return;
|
||||||
|
img = (const uint8_t *)atlas_image(&kidp[pg], idx);
|
||||||
|
gfx_w0_map(kidp[pg].page);
|
||||||
|
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||||||
|
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||||||
|
/* Атласы Кида хранятся COLUMN-MAJOR (transpose_cols в pop_pack_kid —
|
||||||
|
* ради бесплатного горизонтального флипа), поэтому блит обязан быть
|
||||||
|
* колоночным. Row-major gfx_blit_noclip выдал бы марку транспонированной. */
|
||||||
|
if (w && h) gfx_blit_cols_part_noclip(x, y, img, 0, 0, 0);
|
||||||
|
gfx_w0_unmap();
|
||||||
|
}
|
||||||
|
|
||||||
int pop_kid_load(uint8_t npages)
|
int pop_kid_load(uint8_t npages)
|
||||||
{
|
{
|
||||||
static const char *const names[KID_MAXPAGES] = {
|
static const char *const names[KID_MAXPAGES] = {
|
||||||
@@ -184,13 +205,16 @@ pop_char_t Char;
|
|||||||
pop_char_t Opp;
|
pop_char_t Opp;
|
||||||
|
|
||||||
void pop_loadkid(void) { Char = Kid; }
|
void pop_loadkid(void) { Char = Kid; }
|
||||||
void pop_savekid(void) { Kid = Char; kid_frame = cur_frame; }
|
void pop_savekid(void) { Kid = Char; }
|
||||||
/* Выгрузка БЕЗ кадра: control() кадр не трогает, а cur_frame в этот
|
/* Выгрузка БЕЗ кадра: control() кадр не трогает, а cur_frame в этот
|
||||||
* момент принадлежит тому, кто последним крутил play_seq. */
|
* момент принадлежит тому, кто последним крутил play_seq. */
|
||||||
void pop_savekid_state(void) { Kid = Char; }
|
void pop_savekid_state(void) { Kid = Char; }
|
||||||
void pop_loadshad(void) { Char = Guard; }
|
void pop_loadshad(void) { Char = Guard; }
|
||||||
void pop_saveshad(void) { Guard = Char; pop_gframe = cur_frame; }
|
void pop_saveshad(void) { Guard = Char; }
|
||||||
void pop_loadshad_and_opp(void) { Char = Guard; Opp = Kid; }
|
void pop_loadshad_and_opp(void) { Char = Guard; Opp = Kid; }
|
||||||
|
void pop_saveshad_and_opp(void) { Guard = Char; Kid = Opp; }
|
||||||
|
void pop_loadkid_and_opp(void) { Char = Kid; Opp = Guard; }
|
||||||
|
void pop_savekid_and_opp(void) { Kid = Char; Guard = Opp; }
|
||||||
|
|
||||||
/* char_opp_dist (seg006:2135): >0 — соперник ПЕРЕД Char, <0 — за спиной.
|
/* char_opp_dist (seg006:2135): >0 — соперник ПЕРЕД Char, <0 — за спиной.
|
||||||
* +13 при встречных направлениях: у стоящих лицом друг к другу «ноль»
|
* +13 при встречных направлениях: у стоящих лицом друг к другу «ноль»
|
||||||
@@ -282,6 +306,15 @@ static void load_frame(void)
|
|||||||
cur_frame.dy = (int8_t)f[2];
|
cur_frame.dy = (int8_t)f[2];
|
||||||
cur_frame.flags = f[3];
|
cur_frame.flags = f[3];
|
||||||
cur_frame.sword = f[4];
|
cur_frame.sword = f[4];
|
||||||
|
/* Кэш кадра для ОТРИСОВКИ раскладываем ЗДЕСЬ, по charid активного
|
||||||
|
* персонажа, а не в save*-функциях. Раньше кэш писали pop_savekid/
|
||||||
|
* pop_saveshad, и любое окно Char БЕЗ play_seq (окна боёвки
|
||||||
|
* check_sword_hurting / check_sword_hurt) записывало Киду кадр, который
|
||||||
|
* на самом деле принадлежал СТРАЖУ: kid_draw брал его image из атласа
|
||||||
|
* Кида и рисовал произвольную позу. Теперь владелец один — тот, для
|
||||||
|
* кого кадр реально загружен. */
|
||||||
|
if (Char.charid == CHARID_0_KID) kid_frame = cur_frame;
|
||||||
|
else pop_gframe = cur_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t char_dx_forward(int8_t dx)
|
static uint8_t char_dx_forward(int8_t dx)
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ void pop_savekid_state(void); /* Kid = Char, кадр не трогаем */
|
|||||||
void pop_loadshad(void);
|
void pop_loadshad(void);
|
||||||
void pop_saveshad(void);
|
void pop_saveshad(void);
|
||||||
void pop_loadshad_and_opp(void); /* Char = Guard, Opp = Kid (seg006:841) */
|
void pop_loadshad_and_opp(void); /* Char = Guard, Opp = Kid (seg006:841) */
|
||||||
|
void pop_saveshad_and_opp(void); /* Guard = Char, Kid = Opp */
|
||||||
|
void pop_loadkid_and_opp(void); /* Char = Kid, Opp = Guard */
|
||||||
|
void pop_savekid_and_opp(void); /* Kid = Char, Guard = Opp */
|
||||||
|
|
||||||
/* Дистанция до соперника в системе Char: >0 впереди, <0 за спиной,
|
/* Дистанция до соперника в системе Char: >0 впереди, <0 за спиной,
|
||||||
* 999 — разные комнаты (порт char_opp_dist, seg006:2135). */
|
* 999 — разные комнаты (порт char_opp_dist, seg006:2135). */
|
||||||
@@ -114,6 +117,10 @@ void kid_draw(void);
|
|||||||
* звать СРАЗУ после kid_draw в кадре, когда HP убавилось (pop_kid_hurt). */
|
* звать СРАЗУ после kid_draw в кадре, когда HP убавилось (pop_kid_hurt). */
|
||||||
void kid_draw_splash(void);
|
void kid_draw_splash(void);
|
||||||
|
|
||||||
|
/* Блит спрайта chtab_2 по id, левым верхним углом в (x,y) — для полосы HP
|
||||||
|
* (id 216 полное деление / 217 пустое). */
|
||||||
|
void pop_kid_img_blit(uint8_t image, int x, int y);
|
||||||
|
|
||||||
/* Восстановить фон (из теневой копии) на месте ПРЕДЫДУЩЕГО кадра Kid —
|
/* Восстановить фон (из теневой копии) на месте ПРЕДЫДУЩЕГО кадра Kid —
|
||||||
* вместо перерисовки всего экрана. Звать ПЕРЕД kid_tick/kid_draw. */
|
* вместо перерисовки всего экрана. Звать ПЕРЕД kid_tick/kid_draw. */
|
||||||
void kid_heal(void);
|
void kid_heal(void);
|
||||||
|
|||||||
@@ -114,6 +114,12 @@ uint8_t pop_loose_fell; /* 0=нет; иначе tilepos+1 упавше
|
|||||||
uint8_t pop_kid_dead; /* 1 = Kid мёртв (напоролся на пики и т.п.) */
|
uint8_t pop_kid_dead; /* 1 = Kid мёртв (напоролся на пики и т.п.) */
|
||||||
uint8_t pop_kid_hurt; /* 1 = в этом кадре убавилось HP → «брызги» */
|
uint8_t pop_kid_hurt; /* 1 = в этом кадре убавилось HP → «брызги» */
|
||||||
uint8_t hitp_curr; /* текущее HP (0 = мёртв); pop_kid_hp_reset ставит старт */
|
uint8_t hitp_curr; /* текущее HP (0 = мёртв); pop_kid_hp_reset ставит старт */
|
||||||
|
uint8_t hitp_max; /* стартовое HP Кида (для индикатора) */
|
||||||
|
/* Дельта HP Кида за кадр (порт hitp_delta). Её ставит СВОРД-путь боёвки
|
||||||
|
* (guards.c), а применяет pop_do_delta_hp раз в кадр — как в оригинале.
|
||||||
|
* Путь пик/падения (take_hp ниже) правит hitp_curr сразу: он был написан и
|
||||||
|
* проверен до боёвки, и переводить его на дельту без нужды рискованно. */
|
||||||
|
int8_t hitp_delta;
|
||||||
static uint8_t g_room; /* номер ТЕКУЩЕЙ комнаты (для room_modif пик/ворот) */
|
static uint8_t g_room; /* номер ТЕКУЩЕЙ комнаты (для room_modif пик/ворот) */
|
||||||
static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ — loose→empty) */
|
static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ — loose→empty) */
|
||||||
static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */
|
static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */
|
||||||
@@ -156,7 +162,7 @@ void pop_map_set(uint8_t *fg) __banked { g_fg = fg; }
|
|||||||
|
|
||||||
void pop_map_set_room(uint8_t room) __banked { g_room = room; }
|
void pop_map_set_room(uint8_t room) __banked { g_room = room; }
|
||||||
|
|
||||||
void pop_kid_hp_reset(void) __banked { hitp_curr = 3; pop_kid_dead = 0; }
|
void pop_kid_hp_reset(void) __banked { hitp_curr = hitp_max = 3; hitp_delta = 0; pop_kid_dead = 0; }
|
||||||
|
|
||||||
/* take_hp (seg006:06BD, только Kid): count>=hitp → смерть (return 1). */
|
/* take_hp (seg006:06BD, только Kid): count>=hitp → смерть (return 1). */
|
||||||
static uint8_t take_hp(uint8_t count)
|
static uint8_t take_hp(uint8_t count)
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ uint8_t pop_tile_at(int8_t col, int8_t row) __banked;
|
|||||||
* респавн). */
|
* респавн). */
|
||||||
extern uint8_t pop_kid_dead;
|
extern uint8_t pop_kid_dead;
|
||||||
extern uint8_t hitp_curr;
|
extern uint8_t hitp_curr;
|
||||||
|
extern uint8_t hitp_max; /* стартовое HP (для индикатора) */
|
||||||
|
extern int8_t hitp_delta; /* урон за кадр от меча; применяет pop_do_delta_hp */
|
||||||
void pop_kid_hp_reset(void) __banked;
|
void pop_kid_hp_reset(void) __banked;
|
||||||
|
|
||||||
/* Предметы (seg005 check_get_item/get_item, seg006 do_pickup/proc_get_object).
|
/* Предметы (seg005 check_get_item/get_item, seg006 do_pickup/proc_get_object).
|
||||||
|
|||||||
@@ -13,3 +13,7 @@
|
|||||||
#include "pop_state.h"
|
#include "pop_state.h"
|
||||||
|
|
||||||
uint8_t pop_loose_landed;
|
uint8_t pop_loose_landed;
|
||||||
|
|
||||||
|
/* Чит «бессмертие» (клавиша I). Отладочный: Кид получает анимацию удара,
|
||||||
|
* но HP не теряет — иначе проверять боёвку приходится с трёх попаданий. */
|
||||||
|
uint8_t pop_immortal;
|
||||||
|
|||||||
@@ -14,4 +14,8 @@
|
|||||||
* разбирает логика loose-полов (pop_map, W1/W2). */
|
* разбирает логика loose-полов (pop_map, W1/W2). */
|
||||||
extern uint8_t pop_loose_landed;
|
extern uint8_t pop_loose_landed;
|
||||||
|
|
||||||
|
/* Чит «бессмертие» (I): Кида бьют, но HP не убавляется. Читают пути урона
|
||||||
|
* (боёвка в банке, пики в pop_map), переключает главный цикл. */
|
||||||
|
extern uint8_t pop_immortal;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ static void enter_room(uint8_t room)
|
|||||||
pop_trob_room_changed(); /* отложенные перерисовки пик — не из старой комнаты */
|
pop_trob_room_changed(); /* отложенные перерисовки пик — не из старой комнаты */
|
||||||
pop_trob_anim_room(room); /* факелы/зелья: стартовая фаза + trob */
|
pop_trob_anim_room(room); /* факелы/зелья: стартовая фаза + trob */
|
||||||
pop_guard_enter(room); /* страж комнаты (порт enter_guard) */
|
pop_guard_enter(room); /* страж комнаты (порт enter_guard) */
|
||||||
|
pop_hp_invalidate(); /* фон перерисован — полоса HP тоже */
|
||||||
/* Модификаторы тайлов (openness ворот, состояние пик) — ПЕРСИСТЕНТНЫЕ
|
/* Модификаторы тайлов (openness ворот, состояние пик) — ПЕРСИСТЕНТНЫЕ
|
||||||
* per-room (room_modif живёт между входами); рисуем и коллидируем по ним,
|
* per-room (room_modif живёт между входами); рисуем и коллидируем по ним,
|
||||||
* а не по свежему backtable. Фаза P0. */
|
* а не по свежему backtable. Фаза P0. */
|
||||||
@@ -246,7 +247,7 @@ int main(void)
|
|||||||
#ifdef ROOMNAV
|
#ifdef ROOMNAV
|
||||||
uint8_t nav_prev = 0;
|
uint8_t nav_prev = 0;
|
||||||
#endif
|
#endif
|
||||||
uint8_t cheat_prev = 0;
|
uint8_t cheat_prev = 0, immo_prev = 0;
|
||||||
|
|
||||||
if (pop_bg_load() != 0) { /* атласы — ДО initgraph (снимает DSS-страницу W0) */
|
if (pop_bg_load() != 0) { /* атласы — ДО initgraph (снимает DSS-страницу W0) */
|
||||||
puts("pop_bg_load failed");
|
puts("pop_bg_load failed");
|
||||||
@@ -323,11 +324,17 @@ int main(void)
|
|||||||
* стоит, можно спокойно разбирать позу Kid и окклюзию. */
|
* стоит, можно спокойно разбирать позу Kid и окклюзию. */
|
||||||
if (kbd_raw_down(0x16)) frozen = 1;
|
if (kbd_raw_down(0x16)) frozen = 1;
|
||||||
if (kbd_raw_down(0x1E)) frozen = 0;
|
if (kbd_raw_down(0x1E)) frozen = 0;
|
||||||
{ /* Читы (seg000: switch под if (cheats_enabled)). K — убить
|
{ /* Читы (seg000: switch под if (cheats_enabled)). Все — по
|
||||||
* стража; по фронту нажатия, чтобы не срабатывало каждый кадр. */
|
* ФРОНТУ нажатия, иначе срабатывали бы каждый кадр.
|
||||||
|
* K — убить стража;
|
||||||
|
* I — бессмертие Кида (наш отладочный, в оригинале его нет:
|
||||||
|
* иначе боёвку проверять получается ровно три раза). */
|
||||||
uint8_t k = kbd_raw_down(KBD_CHEAT_KILL);
|
uint8_t k = kbd_raw_down(KBD_CHEAT_KILL);
|
||||||
if (pop_cheats && k && !cheat_prev) pop_guard_kill();
|
uint8_t i = kbd_raw_down(KBD_CHEAT_IMMO);
|
||||||
|
if (pop_cheats && k && !cheat_prev) pop_guard_kill();
|
||||||
|
if (pop_cheats && i && !immo_prev) pop_immortal = !pop_immortal;
|
||||||
cheat_prev = k;
|
cheat_prev = k;
|
||||||
|
immo_prev = i;
|
||||||
}
|
}
|
||||||
if (frozen) {
|
if (frozen) {
|
||||||
PROF(0); /* чёрный: ждём vsync */
|
PROF(0); /* чёрный: ждём vsync */
|
||||||
@@ -379,6 +386,11 @@ int main(void)
|
|||||||
kid_tick(); /* play_seq: следующий кадр */
|
kid_tick(); /* play_seq: следующий кадр */
|
||||||
pop_phys_tick(); /* K3: падение/приземление/стена */
|
pop_phys_tick(); /* K3: падение/приземление/стена */
|
||||||
pop_guard_tick(); /* логика стража — В БАНКЕ (guards.c) */
|
pop_guard_tick(); /* логика стража — В БАНКЕ (guards.c) */
|
||||||
|
/* Боёвка, порядок как в play_frame (seg000:872): сначала пометить
|
||||||
|
* попадания с обеих сторон, потом применить, потом свести HP. */
|
||||||
|
pop_check_sword_hurting();
|
||||||
|
pop_check_sword_hurt();
|
||||||
|
pop_do_delta_hp();
|
||||||
if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */
|
if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */
|
||||||
uint8_t down = pop_room_link(cur_room, LINK_DOWN);
|
uint8_t down = pop_room_link(cur_room, LINK_DOWN);
|
||||||
pop_fell_out = 0;
|
pop_fell_out = 0;
|
||||||
@@ -495,6 +507,7 @@ int main(void)
|
|||||||
kid_draw_splash();
|
kid_draw_splash();
|
||||||
pop_kid_hurt = 0;
|
pop_kid_hurt = 0;
|
||||||
}
|
}
|
||||||
|
pop_hp_draw(); /* полоса HP — только при изменении */
|
||||||
PROF(6); /* fore поверх Kid + борта */
|
PROF(6); /* fore поверх Kid + борта */
|
||||||
pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(),
|
pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(),
|
||||||
kid_fp_width(), kid_fp_height(),
|
kid_fp_width(), kid_fp_height(),
|
||||||
|
|||||||
Reference in New Issue
Block a user