BUG-DOOR-CLIP: обрезка силуэта правым косяком двери уровня
Симптом (нашёл пользователь сразу после L1-EXIT): при подъёме по лестнице за
дверью уровня силуэт Кида вылезал ПРАВЕЕ правого косяка проёма; по высоте
обрезка была корректна.
Причина — недопортированная половина clip_char (seg006:1231). Для кадров
двери оригинал ставит ДВА клипа, у нас был только первый:
obj_clip_top = leveldoor_ybottom + 1; // было
obj_clip_right = leveldoor_right; // не было
Отдельная ловушка: комментарий в SDLPoP говорит «frames 217..228», а КОД
проверяет >= frame_224_exit_stairs_8, то есть 224..228 — портировано по коду.
Fore-слоем это не лечится: створка и косяк уходят в оригинале целиком в
backtable (draw_leveldoor, все add_backtable), рисуются ПОД персонажем и
перекрыть его не могут. Единственный способ — срезать сам спрайт.
libbgi: gfx_blit_cols_part_w(..., uint8_t maxw) — обрезка СПРАВА у
колоночного блита. Для column-major это ровно уменьшение числа колонок, то
есть внутри ядра механизм уже был (так же клипается край экрана,
w = _bgi_maxx + 1 - x), наружу не выводился. Тело блита переехало туда,
gfx_blit_cols_part стал тонкой обёрткой (maxw=0) — тем же приёмом, каким
gfx_blit_cols уже обёрнут вокруг gfx_blit_cols_part. Работает и при flip:
первые maxw нарисованных колонок всегда ложатся в левую часть футпринта.
make size-check: роста нет.
PoP: pop_leveldoor_right / pop_leveldoor_ybottom (порт одноимённых глобалов)
пишет draw_leveldoor в pop_state — их читает clip_char из другого банка;
pop_clip_char_right() отдаёт границу, kid_draw превращает её в maxw и уводит
эти кадры с noclip-пути на общий. Прямоугольник heal (kid_lw) сужается тоже
— стираем ровно нарисованное.
Проверено в MAME: pop_leveldoor_right = 176, что есть ровно (draw_xh<<3)+48
для двери комнаты 9; pop_leveldoor_ybottom = 112 у закрытой створки и 69 у
поднятой — сходится с формулой оригинала. Отрисовку подтвердил пользователь
на живом подъёме.
Заодно: ROOMNAV остаётся включённым осознанно — это наш чит, которого в
оригинале не было, как и S/K/I; позже сведём в общий блок читов.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,10 +6,8 @@
|
|||||||
а то, **что берём в работу сейчас и в каком порядке**. Каждая запись: что
|
а то, **что берём в работу сейчас и в каком порядке**. Каждая запись: что
|
||||||
сделать, почему именно сейчас, чем подтверждать результат.
|
сделать, почему именно сейчас, чем подтверждать результат.
|
||||||
|
|
||||||
**Открытые баги** ([`bug_list.md`](bug_list.md)):
|
**Открытых багов нет** ([`bug_list.md`](bug_list.md)): остались незакрытые
|
||||||
[BUG-DOOR-CLIP](bug_list.md#bug-door-clip) (Кид не обрезается правым косяком
|
оптимизации [T-1](bug_list.md#t-1), [T-2](bug_list.md#t-2) и незаконченная
|
||||||
двери уровня) + незакрытые оптимизации [T-1](bug_list.md#t-1),
|
|
||||||
[T-2](bug_list.md#t-2) и незаконченная
|
|
||||||
[таблица обхода 24 комнат](bug_list.md#обход-всех-24-комнат-уровня-1).
|
[таблица обхода 24 комнат](bug_list.md#обход-всех-24-комнат-уровня-1).
|
||||||
|
|
||||||
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
|
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
|
||||||
@@ -373,9 +371,9 @@
|
|||||||
захлопывает решётку за спиной. Проверено: старт даёт room 1, col 0, падение
|
захлопывает решётку за спиной. Проверено: старт даёт room 1, col 0, падение
|
||||||
на row 1 — как по данным уровня.
|
на row 1 — как по данным уровня.
|
||||||
|
|
||||||
⏳ **Остаток:** `#define ROOMNAV` пока ВКЛЮЧЁН — он нужен, чтобы попадать в
|
`#define ROOMNAV` оставлен ВКЛЮЧЁННЫМ осознанно: это наш чит, которого в
|
||||||
комнату 9 для работы над [BUG-DOOR-CLIP](bug_list.md#bug-door-clip).
|
оригинале не было, — как и `S` (выдать меч), `K`, `I`. Все они со временем
|
||||||
Выключить перед L1-PASS.
|
съедутся в общий блок читов, разрешаемый в настройках (решение 2026-08-01).
|
||||||
|
|
||||||
### L1-EXIT. Выход с уровня (дверь уровня) — **СДЕЛАНО 2026-08-01**
|
### L1-EXIT. Выход с уровня (дверь уровня) — **СДЕЛАНО 2026-08-01**
|
||||||
Портированы: ветка двери уровня из `up_pressed` + `go_up_leveldoor`
|
Портированы: ветка двери уровня из `up_pressed` + `go_up_leveldoor`
|
||||||
@@ -397,8 +395,10 @@
|
|||||||
Вывод на будущее: геометрию персонажа в этом порте меняет `pop_map` (пишет в
|
Вывод на будущее: геометрию персонажа в этом порте меняет `pop_map` (пишет в
|
||||||
`Kid`), а не диспетчер.
|
`Kid`), а не диспетчер.
|
||||||
|
|
||||||
⏳ Косметика: [BUG-DOOR-CLIP](bug_list.md#bug-door-clip) — нет обрезки силуэта
|
✅ Косметика тоже закрыта: BUG-DOOR-CLIP (обрезка силуэта правым косяком
|
||||||
правым косяком проёма.
|
проёма) — недоставало второй половины `clip_char` (`obj_clip_right`) и
|
||||||
|
обрезки СПРАВА у колоночного блита. В libbgi добавлен
|
||||||
|
`gfx_blit_cols_part_w(..., maxw)`; разбор — в [`bug_closed.md`](bug_closed.md).
|
||||||
|
|
||||||
### L1-TRIAGE. Ревизия багов — **ЧАСТЬ 1 СДЕЛАНА 2026-08-01**
|
### L1-TRIAGE. Ревизия багов — **ЧАСТЬ 1 СДЕЛАНА 2026-08-01**
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,53 @@ blueline, грани пик/loose соседа, кадр loose) **всегда**
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## BUG-DOOR-CLIP. Подъём по лестнице двери уровня: нет обрезки по правому косяку — **ИСПРАВЛЕН 2026-08-01**
|
||||||
|
|
||||||
|
**Был симптом (найден пользователем сразу после L1-EXIT):** при подъёме по
|
||||||
|
лестнице за дверью уровня (кадры 224..228) силуэт Кида вылезал ПРАВЕЕ правого
|
||||||
|
косяка проёма. По высоте обрезка была корректна.
|
||||||
|
|
||||||
|
**Причина — недопортированная половина `clip_char`** (seg006:1231). Для
|
||||||
|
кадров двери оригинал ставит ДВА клипа, у нас был только первый:
|
||||||
|
|
||||||
|
```c
|
||||||
|
if (frame >= frame_224_exit_stairs_8 && frame < 229) {
|
||||||
|
obj_clip_top = leveldoor_ybottom + 1;
|
||||||
|
obj_clip_right = leveldoor_right;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Отдельная ловушка: комментарий в SDLPoP говорит «frames 217..228», а КОД
|
||||||
|
проверяет `>= frame_224_exit_stairs_8`, то есть **224..228**. Портировано по
|
||||||
|
коду.
|
||||||
|
|
||||||
|
**Почему именно обрезка, а не fore-слой:** створка и косяк уходят в оригинале
|
||||||
|
ЦЕЛИКОМ в backtable (`draw_leveldoor`, все `add_backtable`), то есть рисуются
|
||||||
|
ПОД персонажем и перекрыть его не могут. Единственный способ спрятать
|
||||||
|
поднимающегося — срезать сам спрайт.
|
||||||
|
|
||||||
|
**Фикс:**
|
||||||
|
- libbgi: `gfx_blit_cols_part_w(..., uint8_t maxw)` — обрезка СПРАВА у
|
||||||
|
колоночного блита. Для column-major это ровно уменьшение числа колонок,
|
||||||
|
то есть внутри ядра механизм уже был (так же клипается край экрана:
|
||||||
|
`w = _bgi_maxx + 1 - x`), наружу не было выведено. Тело блита переехало
|
||||||
|
туда, `gfx_blit_cols_part` стал тонкой обёрткой (maxw=0) — тем же приёмом,
|
||||||
|
каким `gfx_blit_cols` уже обёрнут вокруг `gfx_blit_cols_part`. Работает и
|
||||||
|
при flip: первые `maxw` нарисованных колонок всегда ложатся в ЛЕВУЮ часть
|
||||||
|
футпринта.
|
||||||
|
- PoP: `pop_leveldoor_right` / `pop_leveldoor_ybottom` (порт одноимённых
|
||||||
|
глобалов) пишет `draw_leveldoor` в `pop_state` — их читает `clip_char` из
|
||||||
|
другого банка; `pop_clip_char_right()` отдаёт границу, `kid_draw`
|
||||||
|
превращает её в `maxw` и уводит эти кадры с noclip-пути на общий.
|
||||||
|
`kid_lw` (прямоугольник heal) тоже сужается — стираем ровно нарисованное.
|
||||||
|
|
||||||
|
**Проверено в MAME:** `pop_leveldoor_right = 176` — ровно
|
||||||
|
`(draw_xh<<3) + 48` для двери комнаты 9; `pop_leveldoor_ybottom` = 112 у
|
||||||
|
закрытой створки и 69 у поднятой (сходится с формулой оригинала).
|
||||||
|
Отрисовка подтверждена пользователем на живом подъёме.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## BUG-SEAM-PINGPONG (#4). Пинг-понг drawn_room у шва с закрытыми воротами — **РЕШЁН 2026-07-22**
|
## BUG-SEAM-PINGPONG (#4). Пинг-понг drawn_room у шва с закрытыми воротами — **РЕШЁН 2026-07-22**
|
||||||
|
|
||||||
Настоящий корень найден потиковой трассой ЖИВОГО SDLPoP 1.23
|
Настоящий корень найден потиковой трассой ЖИВОГО SDLPoP 1.23
|
||||||
|
|||||||
@@ -14,63 +14,17 @@ odd-pixel арифметика `char_x`, подстановка тайла на
|
|||||||
починена кодом ещё в июле, список просто отстал — чем именно закрыта каждая,
|
починена кодом ещё в июле, список просто отстал — чем именно закрыта каждая,
|
||||||
расписано в [`bug_closed.md`](bug_closed.md)).
|
расписано в [`bug_closed.md`](bug_closed.md)).
|
||||||
|
|
||||||
|
**Открытых багов нет.** Осталось только незакрытое по оптимизации и
|
||||||
|
незаконченная ревизия комнат:
|
||||||
|
|
||||||
| ID | что | тип |
|
| ID | что | тип |
|
||||||
|----|-----|-----|
|
|----|-----|-----|
|
||||||
| [BUG-DOOR-CLIP](#bug-door-clip) | Кид не обрезается правым косяком двери уровня | Medium |
|
|
||||||
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация |
|
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация |
|
||||||
| [T-2](#t-2) | Кид перерисовывается в покое | оптимизация |
|
| [T-2](#t-2) | Кид перерисовывается в покое | оптимизация |
|
||||||
| [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия |
|
| [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<a id="bug-door-clip"></a>
|
|
||||||
## BUG-DOOR-CLIP. Подъём по лестнице двери уровня: нет обрезки по правому косяку — **Medium**
|
|
||||||
|
|
||||||
**Симптом (2026-08-01, найден пользователем сразу после L1-EXIT):** при
|
|
||||||
подъёме по лестнице за дверью уровня (seq_70, кадры 224..228) силуэт Кида
|
|
||||||
вылезает ПРАВЕЕ правого косяка проёма — должен уходить за него. По ВЫСОТЕ
|
|
||||||
обрезка корректна.
|
|
||||||
|
|
||||||
**Причина — недопортированная половина `clip_char`** (seg006:1231). Для
|
|
||||||
кадров двери уровня оригинал ставит ДВА клипа, а мы портировали только
|
|
||||||
первый:
|
|
||||||
|
|
||||||
```c
|
|
||||||
if (frame >= frame_224_exit_stairs_8 && frame < 229) {
|
|
||||||
obj_clip_top = leveldoor_ybottom + 1; // ЕСТЬ (pop_clip_char_top)
|
|
||||||
obj_clip_right = leveldoor_right; // НЕТ
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`leveldoor_right = (draw_xh << 3) + 48` считается в `draw_leveldoor`
|
|
||||||
(seg008:1D29) от колонки ЛЕВОЙ половины двери; для palace-уровней (+8,
|
|
||||||
`tbl_level_type`) — на уровне 1 не применяется.
|
|
||||||
|
|
||||||
Обратить внимание: комментарий в SDLPoP говорит «frames 217..228», а КОД
|
|
||||||
проверяет `>= frame_224_exit_stairs_8`, то есть **224..228**. Портировать по
|
|
||||||
коду.
|
|
||||||
|
|
||||||
**Чего не хватает в движке:** обрезки СПРАВА у колоночного блита.
|
|
||||||
`gfx_blit_cols_part` (libbgi) умеет только обрезку сверху (`skip`/`rows`) плюс
|
|
||||||
клип по краю ЭКРАНА (`w = _bgi_maxx + 1 - x`). Для column-major спрайта
|
|
||||||
обрезка справа — это ровно уменьшение `w`, то есть внутри ядра она уже есть,
|
|
||||||
не хватает способа задать её снаружи.
|
|
||||||
|
|
||||||
**План (не начат):**
|
|
||||||
1. libbgi: тело `gfx_blit_cols_part` вынести в вариант с ограничением
|
|
||||||
ширины (напр. `gfx_blit_cols_part_w(..., uint8_t maxw)`), а нынешнюю
|
|
||||||
функцию оставить тонкой обёрткой — тем же приёмом, каким `gfx_blit_cols`
|
|
||||||
уже обёрнут вокруг `gfx_blit_cols_part`. Флип тут не мешает: в кадрах
|
|
||||||
двери направление всегда dir_FF_left, то есть flip=0.
|
|
||||||
2. PoP: `pop_clip_char_top` дополнить правым клипом (или завести
|
|
||||||
`pop_clip_char_right`), считать `leveldoor_right` от колонки двери.
|
|
||||||
3. `make size-check` (трогаем libbgi) + проверка стоп-кадром в MAME:
|
|
||||||
поймать кадр 224..228 клавишей `1` и сверить кромку.
|
|
||||||
|
|
||||||
**Пока не починено, дефект косметический** — вход на следующий уровень
|
|
||||||
работает.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Оптимизация отрисовки (записано 2026-07-29)
|
# Оптимизация отрисовки (записано 2026-07-29)
|
||||||
|
|
||||||
@@ -181,6 +135,7 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ
|
|||||||
- Читы (`pop_cheat.h`): **K** — убить стража, **I** — бессмертие (toggle),
|
- Читы (`pop_cheat.h`): **K** — убить стража, **I** — бессмертие (toggle),
|
||||||
**S** — выдать меч.
|
**S** — выдать меч.
|
||||||
- Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`); ставит
|
- Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`); ставит
|
||||||
Kid в `START_ROOM`.
|
Kid в стартовую позицию УРОВНЯ (`pop_start_level`, порт do_startpos).
|
||||||
- `START_ROOM=1 / COL 3 / ROW 0` — жёстко зашитый тестовый старт; перевести
|
- **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и
|
||||||
на `pop_level_start_*` (задача L1-START в [`TASKS.md`](TASKS.md)).
|
`S`. Все они со временем съедутся в общий блок читов, разрешаемый в
|
||||||
|
настройках; пока просто включены (`pop_cheats = 1` в `roomtest.c`).
|
||||||
|
|||||||
@@ -585,6 +585,11 @@ static void draw_leveldoor(uint8_t modif, int xh, int dmy)
|
|||||||
int ybottom = dmy - 13;
|
int ybottom = dmy - 13;
|
||||||
int ylo = ybottom - (int)(modif & 3) - 48; /* leveldoor_ybottom */
|
int ylo = ybottom - (int)(modif & 3) - 48; /* leveldoor_ybottom */
|
||||||
int y = ybottom - (int)modif;
|
int y = ybottom - (int)modif;
|
||||||
|
/* leveldoor_right (seg008:1D29): правая кромка проёма — от xh ТЕКУЩЕГО
|
||||||
|
* тайла, то есть ПРАВОЙ половины двери (сюда мы попадаем из
|
||||||
|
* draw_tile_anim_right, разбирая левого соседа). +8 у palace-уровней
|
||||||
|
* (tbl_level_type) — на уровне 1 не применяется. */
|
||||||
|
pop_leveldoor_right = xh * 8 + 48;
|
||||||
env_b(99, x, ybottom);
|
env_b(99, x, ybottom);
|
||||||
if (modif)
|
if (modif)
|
||||||
env_b(144, x, ybottom - 4); /* лестница (комната ≠ стартовой) */
|
env_b(144, x, ybottom - 4); /* лестница (комната ≠ стартовой) */
|
||||||
@@ -593,6 +598,9 @@ static void draw_leveldoor(uint8_t modif, int xh, int dmy)
|
|||||||
if (y > ylo) ylo += 4;
|
if (y > ylo) ylo += 4;
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
|
/* ИМЕННО после цикла: в оригинале clip_char читает уже доведённое
|
||||||
|
* значение (низ поднятой створки), а не стартовое. */
|
||||||
|
pop_leveldoor_ybottom = ylo;
|
||||||
env_b(34, x, dmy - 64);
|
env_b(34, x, dmy - 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -679,7 +679,8 @@ void kid_draw(void)
|
|||||||
uint8_t flip = (Kid.direction >= 0); /* dir_0_right -> зеркало */
|
uint8_t flip = (Kid.direction >= 0); /* dir_0_right -> зеркало */
|
||||||
int bx = obj_x + KID_XOFF;
|
int bx = obj_x + KID_XOFF;
|
||||||
int skip = 0; /* строк спрайта срезано сверху */
|
int skip = 0; /* строк спрайта срезано сверху */
|
||||||
int ct;
|
int ct, cr;
|
||||||
|
uint16_t vis_w = w; /* видимая ширина (клип справа) */
|
||||||
if (flip)
|
if (flip)
|
||||||
bx -= (int)w; /* seg008: xpos -= image->w */
|
bx -= (int)w; /* seg008: xpos -= image->w */
|
||||||
top = obj_y - (int)h + 1 + KID_YOFF;
|
top = obj_y - (int)h + 1 + KID_YOFF;
|
||||||
@@ -692,14 +693,27 @@ void kid_draw(void)
|
|||||||
skip = ct - top;
|
skip = ct - top;
|
||||||
if (skip >= (int)h) skip = (int)h; /* обрезан целиком */
|
if (skip >= (int)h) skip = (int)h; /* обрезан целиком */
|
||||||
}
|
}
|
||||||
|
/* clip_char, вторая половина: правый косяк двери уровня. Дверь
|
||||||
|
* рисуется ПОД персонажем (вся в backtable), поэтому спрятать его
|
||||||
|
* можно только срезав спрайт — см. pop_clip_char_right. */
|
||||||
|
cr = pop_clip_char_right();
|
||||||
|
if (cr) {
|
||||||
|
int avail = cr - bx;
|
||||||
|
if (avail <= 0) vis_w = 0; /* весь спрайт за косяком */
|
||||||
|
else if (avail < (int)w) vis_w = (uint16_t)avail;
|
||||||
|
}
|
||||||
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, без теневой копии */
|
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, без теневой копии */
|
||||||
if (skip < (int)h) { /* +YOFF: центрирование */
|
if (skip < (int)h && vis_w) { /* +YOFF: центрирование */
|
||||||
if (pop_onscreen_cols(bx, top + skip + POP_YOFF, w,
|
/* noclip-ядро ширину не ограничивает, поэтому при обрезке справа
|
||||||
(uint16_t)((int)h - skip)))
|
* идём общим путём: это редкие кадры (подъём в двери уровня раз
|
||||||
|
* за уровень), цена подготовки тут не видна. */
|
||||||
|
if (vis_w == w && pop_onscreen_cols(bx, top + skip + POP_YOFF, w,
|
||||||
|
(uint16_t)((int)h - skip)))
|
||||||
gfx_blit_cols_part_noclip(bx, top + skip + POP_YOFF, img, flip,
|
gfx_blit_cols_part_noclip(bx, top + skip + POP_YOFF, img, flip,
|
||||||
(uint8_t)skip, 0);
|
(uint8_t)skip, 0);
|
||||||
else
|
else
|
||||||
gfx_blit_cols_part(bx, top + skip + POP_YOFF, img, flip, skip, 0);
|
gfx_blit_cols_part_w(bx, top + skip + POP_YOFF, img, flip,
|
||||||
|
skip, 0, (uint8_t)(vis_w == w ? 0 : vis_w));
|
||||||
}
|
}
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
/* клип СВОЕГО выхода за поле (падение ниже поля / верхний борт) —
|
/* клип СВОЕГО выхода за поле (падение ниже поля / верхний борт) —
|
||||||
@@ -708,8 +722,8 @@ void kid_draw(void)
|
|||||||
/* heal чистит ТОЛЬКО нарисованное: иначе стирается кромка пола над
|
/* heal чистит ТОЛЬКО нарисованное: иначе стирается кромка пола над
|
||||||
* срезом (мусор/дыра в кладке на второй странице дабл-буфера). */
|
* срезом (мусор/дыра в кладке на второй странице дабл-буфера). */
|
||||||
kid_lx[dp] = bx; kid_ly[dp] = top + skip;
|
kid_lx[dp] = bx; kid_ly[dp] = top + skip;
|
||||||
kid_lw[dp] = w; kid_lh[dp] = (uint16_t)((int)h - skip);
|
kid_lw[dp] = vis_w; kid_lh[dp] = (uint16_t)((int)h - skip);
|
||||||
kid_lvalid[dp] = (skip < (int)h);
|
kid_lvalid[dp] = (skip < (int)h && vis_w != 0);
|
||||||
kid_fp_w = w; kid_fp_h = h; /* размер кадра для футпринта fore */
|
kid_fp_w = w; kid_fp_h = h; /* размер кадра для футпринта fore */
|
||||||
}
|
}
|
||||||
/* add_sword_to_objtable (seg006:1798): клинок отдельным спрайтом поверх
|
/* add_sword_to_objtable (seg006:1798): клинок отдельным спрайтом поверх
|
||||||
|
|||||||
@@ -1384,6 +1384,21 @@ void pop_loose_tick(void) __banked
|
|||||||
* check_spike_below колонки берутся get_tile_div_mod БЕЗ −7.
|
* check_spike_below колонки берутся get_tile_div_mod БЕЗ −7.
|
||||||
*
|
*
|
||||||
* Возврат: КОМНАТНЫЙ y верхней границы (0 = клипа нет). */
|
* Возврат: КОМНАТНЫЙ y верхней границы (0 = клипа нет). */
|
||||||
|
/* Вторая половина ветки двери уровня из clip_char (seg006:1231):
|
||||||
|
* obj_clip_right = leveldoor_right — ЭКРАННЫЙ X, правее которого спрайт не
|
||||||
|
* рисуется. 0 = обрезки нет.
|
||||||
|
*
|
||||||
|
* Почему обрезка, а не fore-слой: створка и косяк уходят в оригинале
|
||||||
|
* ЦЕЛИКОМ в backtable (draw_leveldoor, все add_backtable), то есть рисуются
|
||||||
|
* ПОД персонажем и перекрыть его не могут. Единственный способ спрятать
|
||||||
|
* поднимающегося по лестнице — срезать сам спрайт. */
|
||||||
|
int pop_clip_char_right(void) __banked
|
||||||
|
{
|
||||||
|
if (Kid.frame >= 224 && Kid.frame < 229)
|
||||||
|
return pop_leveldoor_right;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked
|
int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked
|
||||||
{
|
{
|
||||||
static const int16_t y_clip[5] = { -60, 3, 66, 129, 192 };
|
static const int16_t y_clip[5] = { -60, 3, 66, 129, 192 };
|
||||||
@@ -1393,6 +1408,14 @@ int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked
|
|||||||
int8_t cL, cR, trow, clip_row;
|
int8_t cL, cR, trow, clip_row;
|
||||||
uint8_t t;
|
uint8_t t;
|
||||||
|
|
||||||
|
/* Кадры подъёма по лестнице двери уровня (clip_char, seg006:1231):
|
||||||
|
* персонаж режется по низу поднятой створки. ВНИМАНИЕ: комментарий в
|
||||||
|
* SDLPoP говорит «frames 217..228», а КОД проверяет
|
||||||
|
* `>= frame_224_exit_stairs_8`, то есть 224..228 — портируем по коду.
|
||||||
|
* Правую кромку отдаёт pop_clip_char_right (парная половина ветки). */
|
||||||
|
if (Kid.frame >= 224 && Kid.frame < 229)
|
||||||
|
return pop_leveldoor_ybottom + 1;
|
||||||
|
|
||||||
if (Kid.direction >= 0) xl -= wh; /* лицом вправо: левый край внутрь */
|
if (Kid.direction >= 0) xl -= wh; /* лицом вправо: левый край внутрь */
|
||||||
xr = xl + wh; /* char_x_right */
|
xr = xl + wh; /* char_x_right */
|
||||||
top_y = obj_y - (int)h + 1; /* char_top_y */
|
top_y = obj_y - (int)h + 1; /* char_top_y */
|
||||||
|
|||||||
@@ -122,6 +122,10 @@ void pop_phys_tick(void) __banked;
|
|||||||
* Возврат: КОМНАТНЫЙ y границы, 0 = клипа нет. Зовёт kid_draw. */
|
* Возврат: КОМНАТНЫЙ y границы, 0 = клипа нет. Зовёт kid_draw. */
|
||||||
int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked;
|
int pop_clip_char_top(int obj_x, int obj_y, uint16_t w, uint16_t h) __banked;
|
||||||
|
|
||||||
|
/* Парная половина clip_char: ЭКРАННЫЙ X, правее которого спрайт не рисуется
|
||||||
|
* (правый косяк двери уровня в кадрах подъёма 224..228). 0 = обрезки нет. */
|
||||||
|
int pop_clip_char_right(void) __banked;
|
||||||
|
|
||||||
/* Тип препятствия/края ВПЕРЕДИ по направлению (pop_edge_type после
|
/* Тип препятствия/края ВПЕРЕДИ по направлению (pop_edge_type после
|
||||||
* pop_edge_distance): для control (бежать / осторожный шаг / стоп у стены). */
|
* pop_edge_distance): для control (бежать / осторожный шаг / стоп у стены). */
|
||||||
#define EDGE_CLOSER 0 /* край уступа над ямой (дистанция = до кромки) */
|
#define EDGE_CLOSER 0 /* край уступа над ямой (дистанция = до кромки) */
|
||||||
|
|||||||
@@ -17,3 +17,9 @@ uint8_t pop_loose_landed;
|
|||||||
/* Чит «бессмертие» (клавиша I). Отладочный: Кид получает анимацию удара,
|
/* Чит «бессмертие» (клавиша I). Отладочный: Кид получает анимацию удара,
|
||||||
* но HP не теряет — иначе проверять боёвку приходится с трёх попаданий. */
|
* но HP не теряет — иначе проверять боёвку приходится с трёх попаданий. */
|
||||||
uint8_t pop_immortal;
|
uint8_t pop_immortal;
|
||||||
|
|
||||||
|
/* Габариты последней нарисованной двери уровня (порт leveldoor_right /
|
||||||
|
* leveldoor_ybottom): пишет draw_leveldoor из банка pop_bg, читает
|
||||||
|
* clip_char из pop_map. См. pop_state.h. */
|
||||||
|
int pop_leveldoor_right;
|
||||||
|
int pop_leveldoor_ybottom;
|
||||||
|
|||||||
@@ -18,4 +18,15 @@ extern uint8_t pop_loose_landed;
|
|||||||
* (боёвка в банке, пики в pop_map), переключает главный цикл. */
|
* (боёвка в банке, пики в pop_map), переключает главный цикл. */
|
||||||
extern uint8_t pop_immortal;
|
extern uint8_t pop_immortal;
|
||||||
|
|
||||||
|
/* Габариты ПОСЛЕДНЕЙ нарисованной двери уровня — порт глобалов
|
||||||
|
* leveldoor_right / leveldoor_ybottom (seg008 draw_leveldoor). Их ставит
|
||||||
|
* отрисовка (банк pop_bg), а читает clip_char (pop_map, W1/W2): кадры
|
||||||
|
* подъёма по лестнице (224..228) обрезаются по правому косяку проёма и по
|
||||||
|
* низу поднятой створки. Дверь рисуется ПОД персонажем, поэтому перекрыть
|
||||||
|
* его не может — оригинал именно режет спрайт.
|
||||||
|
* right — экранный X правой кромки; ybottom — room-space Y (без POP_YOFF).
|
||||||
|
* 0 = двери в отрисованной комнате нет. */
|
||||||
|
extern int pop_leveldoor_right;
|
||||||
|
extern int pop_leveldoor_ybottom;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
* сверху для column-major бесплатна: колонка = непрерывный кусок ОЗУ, старт
|
* сверху для column-major бесплатна: колонка = непрерывный кусок ОЗУ, старт
|
||||||
* просто сдвигается на skip байт, длина accel-блока уменьшается.
|
* просто сдвигается на skip байт, длина accel-блока уменьшается.
|
||||||
*
|
*
|
||||||
* Здесь же живёт полное тело блита колонками — gfx_blit_cols() тонкая
|
* Тело (вместе с клип-логикой) — в gfx_blit_cols_part_w.c: этот вызов =
|
||||||
* обёртка (skip=0, rows=0), чтобы не дублировать клип-логику.
|
* «без ограничения ширины». Подробности зеркала/клипа — см. шапку
|
||||||
* Подробности зеркала/клипа — см. шапку gfx_blit_cols.c.
|
* gfx_blit_cols.c.
|
||||||
*/
|
*/
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../_bgi.h"
|
#include "../_bgi.h"
|
||||||
@@ -19,44 +19,5 @@
|
|||||||
void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
|
void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
|
||||||
int skip, int rows)
|
int skip, int rows)
|
||||||
{
|
{
|
||||||
const uint8_t *p = (const uint8_t *)img;
|
gfx_blit_cols_part_w(x, y, img, flip, skip, rows, 0);
|
||||||
int full_w = p[0] | (p[1] << 8);
|
|
||||||
int full_h = p[2] | (p[3] << 8);
|
|
||||||
const uint8_t *px = p + 4;
|
|
||||||
int w = full_w, h;
|
|
||||||
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
|
|
||||||
int sy; /* пропуск строк сверху колонки */
|
|
||||||
int cx = 0; /* пропуск колонок слева экрана */
|
|
||||||
const uint8_t *src;
|
|
||||||
uint8_t *dst;
|
|
||||||
|
|
||||||
if (full_w <= 0 || full_h <= 0)
|
|
||||||
return;
|
|
||||||
if (skip < 0) skip = 0;
|
|
||||||
if (skip >= full_h)
|
|
||||||
return; /* обрезано целиком */
|
|
||||||
sy = skip;
|
|
||||||
h = full_h - skip;
|
|
||||||
if (rows > 0 && rows < h)
|
|
||||||
h = rows;
|
|
||||||
|
|
||||||
/* Клип по экрану — только если хоть один край вылезает. */
|
|
||||||
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
|
|
||||||
if (x < 0) { cx = -x; w += x; x = 0; }
|
|
||||||
if (y < 0) { sy -= y; h += y; y = 0; }
|
|
||||||
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
|
|
||||||
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
|
|
||||||
if (w <= 0 || h <= 0) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка. */
|
|
||||||
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
|
|
||||||
if (cx)
|
|
||||||
src += cx * sstride;
|
|
||||||
src += sy;
|
|
||||||
|
|
||||||
_bgi_begin();
|
|
||||||
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
|
|
||||||
_bgi_blit_cols_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y);
|
|
||||||
_bgi_end();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* gfx_blit_cols_part_w — блит спрайта column-major с обрезкой СВЕРХУ
|
||||||
|
* (skip/rows) И ОГРАНИЧЕНИЕМ ШИРИНЫ (maxw): рисуются только первые maxw
|
||||||
|
* колонок футпринта, считая от x. maxw = 0 — «вся ширина кадра».
|
||||||
|
*
|
||||||
|
* Здесь живёт ПОЛНОЕ тело блита колонками; gfx_blit_cols_part (maxw=0) и
|
||||||
|
* gfx_blit_cols (ещё и skip=rows=0) — тонкие обёртки, чтобы клип-логика не
|
||||||
|
* дублировалась. Подробности зеркала/клипа — см. шапку gfx_blit_cols.c.
|
||||||
|
*
|
||||||
|
* Зачем ограничение ширины. Обрезка СПРАВА для column-major — это ровно
|
||||||
|
* уменьшение числа колонок, то есть внутри уже есть (так же клипается край
|
||||||
|
* экрана: `w = _bgi_maxx + 1 - x`), наружу просто не было выведено.
|
||||||
|
* Понадобилось для порта clip_char в PoP: персонаж, поднимающийся по
|
||||||
|
* лестнице за дверью уровня, обязан уходить за правый косяк проёма
|
||||||
|
* (obj_clip_right), а дверь рисуется ПОД ним и перекрыть его не может.
|
||||||
|
*
|
||||||
|
* Важно: ограничение работает и при flip. При зеркале источник читается с
|
||||||
|
* последней колонки к первой (sstride < 0), но первые w нарисованных
|
||||||
|
* колонок в любом случае ложатся в ЛЕВУЮ часть футпринта [x, x+w) — то
|
||||||
|
* есть уменьшение w всегда срезает справа, как и требуется.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void gfx_blit_cols_part_w(int x, int y, const void *img, uint8_t flip,
|
||||||
|
int skip, int rows, uint8_t maxw)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)img;
|
||||||
|
int full_w = p[0] | (p[1] << 8);
|
||||||
|
int full_h = p[2] | (p[3] << 8);
|
||||||
|
const uint8_t *px = p + 4;
|
||||||
|
int w = full_w, h;
|
||||||
|
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
|
||||||
|
int sy; /* пропуск строк сверху колонки */
|
||||||
|
int cx = 0; /* пропуск колонок слева экрана */
|
||||||
|
const uint8_t *src;
|
||||||
|
uint8_t *dst;
|
||||||
|
|
||||||
|
if (full_w <= 0 || full_h <= 0)
|
||||||
|
return;
|
||||||
|
if (maxw && (int)maxw < w)
|
||||||
|
w = (int)maxw; /* обрезка справа (см. шапку) */
|
||||||
|
if (skip < 0) skip = 0;
|
||||||
|
if (skip >= full_h)
|
||||||
|
return; /* обрезано целиком */
|
||||||
|
sy = skip;
|
||||||
|
h = full_h - skip;
|
||||||
|
if (rows > 0 && rows < h)
|
||||||
|
h = rows;
|
||||||
|
|
||||||
|
/* Клип по экрану — только если хоть один край вылезает. */
|
||||||
|
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
|
||||||
|
if (x < 0) { cx = -x; w += x; x = 0; }
|
||||||
|
if (y < 0) { sy -= y; h += y; y = 0; }
|
||||||
|
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
|
||||||
|
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка. */
|
||||||
|
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
|
||||||
|
if (cx)
|
||||||
|
src += cx * sstride;
|
||||||
|
src += sy;
|
||||||
|
|
||||||
|
_bgi_begin();
|
||||||
|
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
|
||||||
|
_bgi_blit_cols_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y);
|
||||||
|
_bgi_end();
|
||||||
|
}
|
||||||
@@ -128,6 +128,20 @@ void gfx_blit_cols(int x, int y, const void *img, uint8_t flip);
|
|||||||
void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
|
void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
|
||||||
int skip, int rows);
|
int skip, int rows);
|
||||||
|
|
||||||
|
/* То же + ОГРАНИЧЕНИЕ ШИРИНЫ: рисуются только первые maxw колонок футпринта,
|
||||||
|
* считая от x (maxw = 0 — вся ширина кадра). То есть обрезка СПРАВА по
|
||||||
|
* произвольной линии, а не только по краю экрана.
|
||||||
|
*
|
||||||
|
* Для column-major это столь же дёшево, как обрезка сверху: колонок просто
|
||||||
|
* меньше. Работает и при flip — зеркало происходит внутри футпринта, а
|
||||||
|
* первые maxw нарисованных колонок всегда ложатся в его ЛЕВУЮ часть.
|
||||||
|
*
|
||||||
|
* Мотивация — clip_char в PoP (obj_clip_right): персонаж, поднимающийся по
|
||||||
|
* лестнице за дверью уровня, обязан уходить за правый косяк проёма, а сама
|
||||||
|
* дверь рисуется ПОД ним и перекрыть его не может. */
|
||||||
|
void gfx_blit_cols_part_w(int x, int y, const void *img, uint8_t flip,
|
||||||
|
int skip, int rows, uint8_t maxw);
|
||||||
|
|
||||||
/* То же БЕЗ клипа по экрану — пара к gfx_blit_cols_part так же, как
|
/* То же БЕЗ клипа по экрану — пара к gfx_blit_cols_part так же, как
|
||||||
* gfx_blit_part_noclip к gfx_blit_part. Вызывающий гарантирует: спрайт
|
* gfx_blit_part_noclip к gfx_blit_part. Вызывающий гарантирует: спрайт
|
||||||
* целиком на экране, ширина/высота кадра и y <= 255, skip < высоты.
|
* целиком на экране, ширина/высота кадра и y <= 255, skip < высоты.
|
||||||
|
|||||||
Reference in New Issue
Block a user