From 0ef8c4b60e2d9f3db8900ab02f9075377bdd9a2d Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Thu, 20 Aug 2026 10:08:52 +0300 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=B5=D1=81=D1=81=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D1=82=D0=B8=D0=B5:=20=D0=B4=D0=B2=D0=B0=20=D1=83=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BD=D1=8F=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20?= =?UTF-8?q?=D1=82=D1=83=D0=BC=D0=B1=D0=BB=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1 — только бой: удары мечом не отнимают HP (ветка в hurt_by_sword). 2 — плюс мелкий урон: не проходят «минус деление» от падения с двух этажей и от падающей плиты (pop_take_hp гасит count < 100). Мгновенная смерть остаётся на обоих: пики, чомпер, падение с трёх этажей и удар вне боевой стойки приходят с count = 100. В коде ровно два значения урона, 1 и 100, поэтому граница точная, а не эвристическая. Заодно ушёл костыль «снять бессмертие на время вызова take_hp(100)» в hurt_by_sword — он был нужен только потому, что прежний чит глушил и смертельный урон. Клавиша I идёт по кругу 0 -> 1 -> 2 -> 0; в отладочной метке число красных палочек = уровень. --- applications/PoP/roomtest/guards.c | 13 +++++++------ applications/PoP/roomtest/pop_cheat.h | 4 +++- applications/PoP/roomtest/pop_guard.c | 9 ++++++++- applications/PoP/roomtest/pop_state.h | 9 +++++++++ applications/PoP/roomtest/roomtest.c | 5 ++++- applications/PoP/roomtest/roomtest_cold.c | 4 ++-- 6 files changed, 33 insertions(+), 11 deletions(-) diff --git a/applications/PoP/roomtest/guards.c b/applications/PoP/roomtest/guards.c index d3a7143..02e4def 100644 --- a/applications/PoP/roomtest/guards.c +++ b/applications/PoP/roomtest/guards.c @@ -973,14 +973,15 @@ static void hurt_by_sword(void) * нет ни одной — управление зависало намертво: Кид стоял в боевой * стойке без меча и не разворачивался к сопернику (BUG-CHEAT-IMM-1). * - * Ветку глушим на время вызова, а не правим pop_take_hp: тот общий - * с физикой (падения/пики/чомперы), и там бессмертие обязано - * работать при любом положении меча. */ - uint8_t imm = pop_immortal; - pop_immortal = 0; + * Костыля «снять бессмертие на время вызова» здесь БОЛЬШЕ НЕТ: + * pop_take_hp гасит только урон меньше 100, а тут ровно 100. */ pop_take_hp(100); - pop_immortal = imm; pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); + } else if (Char.charid == CHARID_0_KID && pop_immortal) { + /* ЧИТ, уровень 1 и выше: в боевой стойке удары не отнимают HP. + * Кадр «получил удар» оставляем — иначе бой перестаёт читаться, + * да и оригинал на выживший удар ставит ровно его. */ + pop_char_set_seq(SEQ_74_HIT_BY_SWORD); } else if (Char.charid != CHARID_4_SKELETON && pop_take_hp(1)) { pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); /* HP кончились */ } else { diff --git a/applications/PoP/roomtest/pop_cheat.h b/applications/PoP/roomtest/pop_cheat.h index e5f05b5..a489a98 100644 --- a/applications/PoP/roomtest/pop_cheat.h +++ b/applications/PoP/roomtest/pop_cheat.h @@ -19,7 +19,9 @@ extern uint8_t pop_cheats; /* 0 = выключены, иначе включены */ #define KBD_CHEAT_KILL 0x42 /* K (PS/2 set 2) — kill guard */ -#define KBD_CHEAT_IMMO 0x43 /* I (PS/2 set 2) — бессмертие Кида (toggle) */ +#define KBD_CHEAT_IMMO 0x43 /* I (PS/2 set 2) — бессмертие Кида по кругу + * 0 -> 1 (только бой) -> 2 (и мелкий урон) -> 0; + * уровни расписаны в pop_state.h */ #define KBD_CHEAT_SWORD 0x1B /* S (PS/2 set 2) — выдать Киду меч */ #define KBD_CHEAT_NEXTLVL 0x4B /* Shift+L (PS/2 set 2) — следующий уровень */ #define KBD_CHEAT_XDEC 0x54 /* [ (PS/2 set 2) — сдвинуть Кида на 1 px влево */ diff --git a/applications/PoP/roomtest/pop_guard.c b/applications/PoP/roomtest/pop_guard.c index d45898a..099e852 100644 --- a/applications/PoP/roomtest/pop_guard.c +++ b/applications/PoP/roomtest/pop_guard.c @@ -85,7 +85,14 @@ void pop_guard_kill(void) uint8_t pop_take_hp(uint8_t count) { if (Char.charid == CHARID_0_KID) { - if (pop_immortal) return 0; + /* ЧИТ БЕССМЕРТИЯ, уровень 2 (pop_state.h): гасим ТОЛЬКО мелкий урон + * — падение с двух этажей, придавливание плитой. Мгновенная смерть + * (пики, чомпер, падение с трёх этажей, удар вне боевой стойки) + * приходит с count = 100 и проходит всегда: у читa нет задачи + * отменять смерть, у него задача отменить «минус одно деление». + * Уровень 1 сюда не вмешивается вовсе — он живёт в hurt_by_sword и + * гасит только удары мечом. */ + if (pop_immortal >= 2 && count < 100) return 0; if (count >= hitp_curr) { hitp_delta = (int8_t)-hitp_curr; return 1; } hitp_delta = (int8_t)-(int8_t)count; } else { diff --git a/applications/PoP/roomtest/pop_state.h b/applications/PoP/roomtest/pop_state.h index 704b0f4..ae167a6 100644 --- a/applications/PoP/roomtest/pop_state.h +++ b/applications/PoP/roomtest/pop_state.h @@ -45,6 +45,15 @@ extern uint8_t pop_nav_hold; /* Чит «бессмертие» (I): Кида бьют, но HP не убавляется. Читают пути урона * (боёвка в банке, пики в pop_map), переключает главный цикл. */ +/* ЧИТ БЕССМЕРТИЯ, три состояния (клавиша I по кругу): + * 0 — выключено; + * 1 — только БОЙ: удары мечом не отнимают HP (pop_take_hp тут ни при + * чём, ветка живёт в hurt_by_sword, guards.c); + * 2 — бой И мелкий урон: не проходят «минус одно деление» от падения с + * двух этажей и от падающей плиты (pop_take_hp гасит count < 100). + * МГНОВЕННАЯ СМЕРТЬ остаётся на любом уровне: пики, чомпер, падение с + * трёх этажей, удар мечом вне боевой стойки — все они приходят с + * count = 100. Чит отменяет потерю HP, а не смерть. */ extern uint8_t pop_immortal; /* Габариты ПОСЛЕДНЕЙ нарисованной двери уровня — порт глобалов diff --git a/applications/PoP/roomtest/roomtest.c b/applications/PoP/roomtest/roomtest.c index b506480..1221c19 100644 --- a/applications/PoP/roomtest/roomtest.c +++ b/applications/PoP/roomtest/roomtest.c @@ -312,7 +312,10 @@ int main(void) uint8_t xd = kbd_raw_down(KBD_CHEAT_XDEC); uint8_t xi = kbd_raw_down(KBD_CHEAT_XINC); if (pop_cheats && k && !cheat_prev) pop_guard_kill(); - if (pop_cheats && i && !immo_prev) pop_immortal = !pop_immortal; + /* Бессмертие по кругу: 0 выкл -> 1 «только бой» -> 2 «бой и + * мелкий урон» -> 0. Что означают уровни — pop_state.h. */ + if (pop_cheats && i && !immo_prev) + pop_immortal = (uint8_t)(pop_immortal >= 2 ? 0 : pop_immortal + 1); if (pop_cheats && s && !sword_prev) pop_have_sword = 1; if (pop_cheats && nl && !nlvl_prev && pop_current_level < POP_LAST_LEVEL) pop_next_level = (uint8_t)(pop_current_level + 1); diff --git a/applications/PoP/roomtest/roomtest_cold.c b/applications/PoP/roomtest/roomtest_cold.c index 9026f85..d3f7c08 100644 --- a/applications/PoP/roomtest/roomtest_cold.c +++ b/applications/PoP/roomtest/roomtest_cold.c @@ -302,7 +302,7 @@ uint8_t pop_dbg_roomnav(uint8_t dir) __banked * жёлтый x 10.. 85 уровень (1..15) * белый x 100..175 комната (1..24) * голубой x 190..205 скорость (1 палочка NORMAL, 2 FAST, 3 FASTEST) - * красный x 220..225 бессмертие (палочка есть = включено) + * красный x 220..230 бессмертие (1 палочка = только бой, 2 = и мелкий урон) * * ЦВЕТА — индексы ИГРОВОЙ палитры, и выбраны они не по вкусу, а по двум * ограничениям. @@ -370,7 +370,7 @@ void pop_dbg_room_label(uint8_t room) __banked if (imm) { /* бессмертие — красный */ setfillstyle(SOLID_FILL, 0x3C); - label_bars(220, 1); + label_bars(220, imm); /* палочек = уровень (1 или 2) */ } room_label_redraw--; }