Зелье медленного падения (перо) + цвет пузырьков по типу зелья

L7-FEATHER: тип 3 (уровень 7, комната 1) больше не пустой TODO.

- pop_map.c: pop_feather — счётчик кадров эффекта (POP_FEATHER_FRAMES = 225,
  ванильный порог do_timers seg003:0517; привязку к звуку взять неоткуда).
  fall_accel — ускорение 1 / потолок 4 (seg006:057C) вместо 3 / 33.
  proc_get_object case 3: взвести эффект + зелёная вспышка на 3 кадра.
- pop_kid.c: опкод JMP_IF_FEATHER (0xF7) больше не пропускает адрес
  безусловно — под пером прыгает по нему, то есть seqtbl уводит падение и
  удар в ветки stepfloat/bumpfloat (плавные кадры, без урона).
- roomtest.c: pop_flash_red -> pop_flash_color (жёлтый/красный/ЗЕЛЁНЫЙ);
  сброс pop_feather в pop_start_level (seg003:189).
- Цвет пузырька по ТИПУ зелья (seg008:652), чего у нас не было вовсе:
  3/4 зелёный, 5/6 СИНИЙ, остальные красный.  Mono-блиттера с параметром
  цвета в libbgi нет, поэтому цвет запекается при упаковке: pop_pack_bg.py
  кладёт те же 7 кадров ещё дважды (id 30..36 зелёные, 40..46 синие),
  pop_potion_draw выбирает набор.  Атлас 23 -> 37 спрайтов (+423 Б).
- Синее зелье «−HP» приведено к оригиналу (seg006:1892): своей вспышки не
  ставит (красный кадр даёт общий flash_if_hurt — иначе экран красился
  дважды), а на уровне зелий забирает ПОЛОВИНУ запаса HP.

Такое зелье стоит уже на пройденном уровне 2 (комната 13, тайл (1,3)),
а также ур.8 комн.2 и весь ур.15 — до сих пор оно было красным.

Тесты: phys_feather_fall_is_slow_and_harmless (обычное падение с двух рядов
разгоняется и стоит HP, под пером скорость <= 4 и HP целое); tests-host
5/5 (1727 в [phys]), size-check OK.

Расхождения с ванилью — в docs/impl_diff.md (перо ловит только Кида;
синее зелье без своей вспышки).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 12:13:29 +03:00
parent b5951c9b5c
commit d7d18aef77
13 changed files with 267 additions and 24 deletions
+51 -11
View File
@@ -118,6 +118,10 @@
#define SCREENSPACE_X 58
#define FALL_ACCEL 3
#define FALL_MAX 33
/* Перо (seg006:057C, types.h:1435): ускорение и потолок скорости падения
* под зельем медленного падения. */
#define FALL_ACCEL_FEATHER 1
#define FALL_MAX_FEATHER 4
#define TILE_MIDX 7
#define FRAME_THIN 0x20
@@ -158,6 +162,11 @@ static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ
static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */
static uint8_t g_obj_xl; /* позиция внутри тайла (get_tile_div_mod) */
static uint8_t grab_timer; /* после зацепа (12): блокирует climb-up */
/* is_feather_fall (seg000:15F8): кадров, пока действует зелье медленного
* падения. У оригинала это флаг + счётчик тиков со сроком по звуку; у нас
* просто обратный отсчёт кадров (POP_FEATHER_FRAMES, pop_tune.h). Публичный:
* его читают play_seq (опкод JMP_IF_FEATHER, pop_kid.c) и приложение. */
uint8_t pop_feather;
/* Состояние edge-триггера бампа — массивы флагов перекрытия по колонкам
* (порт prev_coll_flags/curr_row_coll_flags, seg004); объявлены ниже, рядом
* с check_collisions. Сброс при смене комнаты — coll_prev_row. */
@@ -823,8 +832,19 @@ static void do_fall(void)
static void fall_accel(void)
{
if (Char.action == ACT_FREEFALL) {
int fy = (uint8_t)Char.fall_y + FALL_ACCEL;
if (fy > FALL_MAX) fy = FALL_MAX;
int fy;
/* Перо (seg006:057C). Оригинал ловит эффектом ЛЮБОГО персонажа в
* Char — зелье пьёт только Кид, но флаг глобальный, и страж, упавший
* в ту же минуту, тоже парит; SDLPoP чинит это опцией
* fix_feather_fall_affects_guards. Берём поведение С ФИКСОМ (только
* Кид): расхождение осознанное, запись в ../docs/impl_diff.md. */
if (pop_feather && Char.charid == CHARID_0_KID) {
fy = (uint8_t)Char.fall_y + FALL_ACCEL_FEATHER;
if (fy > FALL_MAX_FEATHER) fy = FALL_MAX_FEATHER;
} else {
fy = (uint8_t)Char.fall_y + FALL_ACCEL;
if (fy > FALL_MAX) fy = FALL_MAX;
}
Char.fall_y = (int8_t)fy;
}
}
@@ -1157,7 +1177,7 @@ uint8_t pop_item_taken; /* 0 = нет; иначе tilepos+1 подн
uint8_t pop_flash_time; /* кадров вспышки фона (flash_time) */
/* Цвет вспышки: 0 = ярко-жёлтая (меч), 1 = красная (зелье). В оригинале это
* flash_color; нам хватает двух значений, которые реально встречаются. */
uint8_t pop_flash_red;
uint8_t pop_flash_color;
static int8_t pickup_obj_type; /* -1 = меч (порт pickup_obj_type) */
/* do_pickup (seg006:1671): стереть предмет с карты (тайл → пол) и запомнить
@@ -1222,22 +1242,38 @@ void pop_proc_get_object(void) __banked
case 1: /* здоровье: +1 HP, красная вспышка */
if (hitp_curr != hitp_max) {
hitp_delta = 1;
pop_flash_red = 1;
pop_flash_color = POP_FLASH_RED;
pop_flash_time = 2;
}
break;
case 2: /* большая склянка: +1 к ПОТОЛКУ HP */
pop_kid_add_life();
pop_flash_red = 1;
pop_flash_color = POP_FLASH_RED;
pop_flash_time = 4; /* seg006:1878 — вспышка длиннее */
break;
case 5: /* «злое» зелье: −1 HP */
hitp_delta = -1;
pop_flash_red = 1;
pop_flash_time = 2;
case 3: /* ПЕРО: медленное падение */
/* feather_fall (seg000:15F8): взвести эффект + ЗЕЛЁНАЯ вспышка
* на 3 кадра (flash_color = 2). Оригинал здесь же глушит звуки
* и играет sound_39_low_weight — по нему же меряет срок; у нас
* срок задан кадрами (POP_FEATHER_FRAMES). */
pop_feather = POP_FEATHER_FRAMES;
pop_flash_color = POP_FLASH_GREEN;
pop_flash_time = 3;
break;
default: /* 3 перо / 4 переворот / 6 открыть —
* TODO вместе с их уровнями (7/9) */
case 5: /* «злое» зелье (синее): −1 HP */
/* seg006:1892. Своей вспышки у этой ветки НЕТ (в отличие от
* лечебных): красный кадр даёт общий механизм «Кид ранен»
* (flash_if_hurt, seg003:0AFC) — у нас pop_kid_hurt. Раньше мы
* ставили вспышку руками, то есть красили экран дважды.
* Спецсобытие уровня зелий: там синие склянки забирают ПОЛОВИНУ
* запаса, а не одну единицу. */
if (pop_current_level == POP_POTIONS_LEVEL)
hitp_delta = (int8_t)(-((hitp_max + 1) >> 1));
else
hitp_delta = -1;
break;
default: /* 4 переворот / 6 открыть — TODO
* вместе с их уровнями (9/13) */
break;
}
}
@@ -2556,6 +2592,10 @@ static void check_chomped_guard(void)
static void kid_phys(void)
{
if (grab_timer) grab_timer--;
/* do_timers (seg003:0517): срок действия пера тикает раз в игровой кадр.
* Место — у Кида, а не у стража: эффект в оригинале снимается общим
* таймером главного цикла, а у нас единственный кадровый тик Кида. */
if (pop_feather) pop_feather--;
fall_accel();
fall_speed();
determine_col();