From f8e96c0495cfd22bf563d6fee61ca797fc96f898 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Thu, 27 Aug 2026 15:34:19 +0300 Subject: [PATCH] =?UTF-8?q?SprPoP:=20=D1=87=D0=B8=D1=82=D1=8B=20=D0=B7?= =?UTF-8?q?=D0=B4=D0=BE=D1=80=D0=BE=D0=B2=D1=8C=D1=8F=20=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B0=20=E2=80=94=20=D1=84=D0=B0=D0=B7=D0=B0=20C?= =?UTF-8?q?=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0=20keys=5Fplan.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shift+S — одна единица HP (малое красное зелье), Shift+T — поднять потолок HP (большая склянка), Shift+W — медленное падение (зелье пера). Механика у всех трёх в движке уже была: читы просто ставят те же переменные, что и подобранное зелье. СВЕРКА С SDLPoP дала деталь, которую легко проглядеть: ветка ЧИТА и ветка ЗЕЛЬЯ различаются. Зелье в seg006:1871 зовёт stop_sounds, а чит в seg000:838 — нет. Повторяем чит, а не зелье. Исключение — перо: там stop_sounds сидит внутри самой feather_fall(), поэтому остаётся. Вторая деталь: Shift+T НЕ проверяет, полное ли HP, — потолок растёт всегда (упираясь в POP_MAX_HITP = 10, как max_hitp_allowed оригинала). У Shift+S условие hitp_curr != hitp_max есть и сохранено. Модификаторы разведены: Shift+S делит скан-код с Ctrl+S (звук), поэтому требует отпущенного Ctrl; Shift+T делит с голым T (таймер), но тот сам требует отпущенного Shift. Проверено в MAME по памяти, а не на глаз: Shift+T 3/3 -> 4/4, Shift+W взводит pop_feather в 148, Shift+S на подпорченном отладчиком HP 2/4 -> 3/4. R (воскрешение) НЕ входит: это не ещё один чит, а правка модели смерти — окно неуязвимости, размазанное по кадровой цепочке (счётчик в seg003:512, пропуск пик и челюстей в seg000:1245, пропуск урона мечом в seg000:876, восстановление позы в seg006:1352). Задевает гейт «мёртв», про который memory pop_level_restart_scope прямо предупреждает. Отложено по решению пользователя. Co-Authored-By: Claude Opus 5 --- applications/SprPoP/src/pop_cheat.h | 1 + applications/SprPoP/src/sprpop_cold.c | 42 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/applications/SprPoP/src/pop_cheat.h b/applications/SprPoP/src/pop_cheat.h index b18f07c..f58cc3a 100644 --- a/applications/SprPoP/src/pop_cheat.h +++ b/applications/SprPoP/src/pop_cheat.h @@ -51,6 +51,7 @@ extern uint8_t pop_cheats; /* 0 = выключены, иначе включ /* Наши сверхштатные, все под Ctrl, чтобы не занимать голые буквы. */ #define KBD_KEY_P 0x4D /* Ctrl+P — режим скорости (pop_pace.h) */ +#define KBD_KEY_W 0x1D /* Shift+W — медленное падение (feather_fall) */ #define KBD_KEY_D 0x23 /* Ctrl+D — отладочная строка вкл/выкл */ #define KBD_KEY_F 0x2B /* Ctrl+F — стоп-кадр, вкл/выкл одной клавишей */ diff --git a/applications/SprPoP/src/sprpop_cold.c b/applications/SprPoP/src/sprpop_cold.c index 285ee20..b51484d 100644 --- a/applications/SprPoP/src/sprpop_cold.c +++ b/applications/SprPoP/src/sprpop_cold.c @@ -534,6 +534,7 @@ uint8_t pop_dbg_roomnav(uint8_t dir) __banked static uint8_t cheat_prev, immo_prev, sound_prev, music_prev, nlvl_prev; static uint8_t upsd_prev, xdec_prev, xinc_prev, cfg_prev; static uint8_t time_sub_prev, time_add_prev; +static uint8_t hp1_prev, hpmax_prev, feather_prev; void pop_cheat_tick(void) __banked { @@ -590,7 +591,48 @@ void pop_cheat_tick(void) __banked /* C — записать POP.CFG сейчас (отладка MS2, до экрана Settings: * там сохранять будет Apply/выход). */ uint8_t cf = kbd_raw_down(0x21); /* C (PS/2 set 2) */ + /* ЧИТЫ ЗДОРОВЬЯ И ПЕРА (seg000:838, :845, :795). + * + * Делают то же, что соответствующие зелья, но НЕ через них: + * оригинал в этих ветках не зовёт stop_sounds, хотя ветка + * зелья зовёт (seg006:1871). Повторяем чит, а не зелье. + * Исключение — перо: там stop_sounds сидит внутри самого + * feather_fall(), поэтому остаётся. + * + * Shift+S делит скан-код с Ctrl+S (звук), поэтому Ctrl обязан + * быть отпущен; Shift+T — с голым T (таймер), но тот сам + * требует отпущенного Shift. */ + uint8_t shift = (uint8_t)(kbd_raw_down(KBD_LSHIFT) || + kbd_raw_down(KBD_RSHIFT)); + uint8_t h1 = (uint8_t)(shift && nomod && + kbd_raw_down(KBD_SOUND_TOGGLE)); + uint8_t hm = (uint8_t)(shift && kbd_raw_down(KBD_TIMER)); + uint8_t ff = (uint8_t)(shift && kbd_raw_down(KBD_KEY_W)); if (pop_cheats && k && !cheat_prev) pop_guard_kill(); + /* Shift+S — одна единица HP, как малое красное зелье. */ + if (pop_cheats && h1 && !hp1_prev && hitp_curr != hitp_max) { + pop_sfx_play(33); + hitp_delta = 1; + pop_flash_color = POP_FLASH_RED; + pop_flash_time = 2; + } + /* Shift+T — поднять ПОТОЛОК HP, как большая склянка. Оригинал + * здесь условия «не полон» не ставит: потолок растёт всегда. */ + if (pop_cheats && hm && !hpmax_prev) { + pop_sfx_play(30); + pop_add_life(); + pop_flash_color = POP_FLASH_RED; + pop_flash_time = 4; + } + /* Shift+W — медленное падение, как зелье пера. */ + if (pop_cheats && ff && !feather_prev) { + pop_feather = POP_FEATHER_FRAMES; + pop_sfx_stop(); + pop_sfx_play(39); + pop_flash_color = POP_FLASH_GREEN; + pop_flash_time = 3; + } + hp1_prev = h1; hpmax_prev = hm; feather_prev = ff; /* Бессмертие по кругу: 0 выкл -> 1 «только бой» -> 2 «бой и * мелкий урон» -> 0. Что означают уровни — pop_state.h. */ if (pop_cheats && i && !immo_prev)