Будущие исправления и нововведения, которые остаются актуальными после сверки текущего порта с байткодом оригинала. При изменении физики и формата уровней приоритетом остаётся точное воспроизведение поведения оригинальной игры.
- Исправить переключение чекпоинтов. При активации нового чекпоинта старый должен
переходить в исходное состояние, эквивалентное записываемому оригиналом значению
128, а новый — значению136. Текущийlevel_deactivate_old_checkpoint()записывает старому ID8, поэтому фактически не воспроизводит переход из байткода. - Сохранять оригинальное состояние собранных тайлов. Для extra life и других
одноразовых объектов использовать состояние, эквивалентное
128, вместо произвольной замены на ID0; физика должна читать ID через маску, а рендер — корректно учитывать runtime-флаг. - Завершать выход из уровня в конце тика. Касание открытой двери должно выставлять
отложенный флаг/событие, а не немедленно менять
g_game.stateвнутриtestTile(). Применять переход после остальных фаз текущего тика и последнего игрового кадра, как оригинальныйmExitFlag. - Сделать снимок ввода для каждого фиксированного тика. Явно определить перенос press/release/held из render frame в один или несколько 30-мс обновлений, чтобы короткие нажатия не терялись и не дублировались при accumulator catch-up.
- Воспроизвести fixed-delay семантику игрового таймера. Оригинал запускает
Timer.schedule(..., 0, 30): задержанный тик сдвигает следующие тики и не вызывает серию обновлений для догоняющей симуляции. Не выполнять несколько физических тиков подряд после задержки; немедленный первый тик при запуске уровня сохранить.
- Разделить сборку, выпуск и резервное копирование. Обычный
makeдолжен оставлятьEBOOT.PBPиBounce.elfна месте для инкрементальной сборки. Копирование ресурсов вrelease/и создание архива исходников оформить отдельными целямиreleaseиbackup, явно указав используемые ими внешние утилиты.
- Сделать отдельный экран паузы поверх игры. Добавить
STATE_PAUSE, замораживать физику, рисовать текущую сцену с затемнением и показывать Resume/Restart/Quit. Не использоватьSTATE_MENUкак состояние паузы. - Перевести Nokia splash с кадров на реальное время. Хранить длительность в
миллисекундах, чтобы заставка длилась одинаково при variable tick 30/60 Гц и не зависела
от частоты рендера. Воспроизвести порядок оригинала
mSplashTimer > 30при периоде 30 мс, а не считать комментарий про три секунды источником истины. - Привязать
Press STARTк геометрии заставки. Вычислять вертикальную позицию от фактических координат иactual_heightизображения с именованным отступом вместо выражения136 + 70.
- Локализовать индикатор страницы инструкций. Убрать русскую строку
"Часть %d из %d"изmenu.c, добавить шаблон во всеlang/lang.*и форматировать его через основную систему локализации. - Перенести дополнительные строки в
lang/lang.*. Убрать ручной выбор языка дляSelect level,SettingsиPress STARTизlocal_extra.c, использовать единые строковые ID и после переноса удалить ставший ненужным модульlocal_extra. - Сделать загрузчик локализации отказоустойчивым. Проверять
fseek(), отрицательный результатftell(), полный результатfread()и границы offset/length до чтения строк; при ошибке оставлять безопасные fallback-строки. - Убрать общие буферы из форматирования локализации. Заменить статические
result_bufferиtemp_bufferвlocal_get_text_with_params()на буфер и размер, передаваемые вызывающей стороной, чтобы результаты вложенных и последовательных вызовов не перезаписывали друг друга. - Обрабатывать переполнение разметки инструкций. Не обрезать локализованный текст
молча при достижении
INSTR_MAX_LINES; возвращать явный статус и показывать безопасный результат либо выделять нужное число строк при открытии страницы.
- Автоматизировать переключение состояния PSP GU. Убрать из игрового и меню-кода
обязанность вручную согласовывать
graphics_begin_plain(),graphics_begin_textured()и сброс батча. Высокоуровневые операции рисования должны сами устанавливать полное необходимое состояние. - Ввести явные слои игровой сцены. Зафиксировать порядок «фон → тайлы и объекты →
игрок → передний план → HUD» и заменить специальную очередь
level_set_ring_fg_defer()общим передним слоем без изменения изображения. - Вынести камеру в отдельный модуль. Сохранить текущую dead zone, ограничения карты
и проверку видимости двери, но убрать состояние камеры и её расчёты из
game.c. - Исправить проверку границы статической VRAM. Сначала вычислять выровненный offset,
затем проверять
aligned_offset + sizeотносительно размера EDRAM и только после этого выдавать память. - Описать время жизни областей VRAM. Для будущей динамической загрузки текстур
добавить сбрасываемую арену или менеджер свободных блоков; не выдавать bump-аллокатор без
освобождения за универсальный
png_free_texture(). - Добавить batching для произвольного UV-quad. Включить повёрнутые и составные
спрайты из
png_draw_sprite_uv4()в общий батч, чтобы такие вызовы не требовали отдельного немедленного draw и принудительного flush. - Контролировать размер списка команд GU. Отслеживать значение
sceGuCheckList()относительноGU_CMD_LIST_SIZEи безопасно завершать либо разбивать кадр до выхода за пределыs_listпри будущем усложнении рендера. - Добавить swizzling текстур в VRAM. Хранить подходящие статические текстуры в
swizzled-разметке и явно отмечать формат хранения в
texture_t; привязка текстуры должна выбирать правильный режим GU. Не менять пиксельные UV, фильтрацию и содержимое изображения. - Убрать жёсткий предел батча в 128 спрайтов. Подобрать ёмкость из максимальной нагрузки сцены и бюджета display list, сохранив безопасный порционный flush при смене текстуры, режима или исчерпании выделенного пространства.
- Объединить диспетчеризацию экранов. Дать каждому
GameStateсобственные update/render-обработчики и убрать промежуточное преобразование черезmenu_get_type_from_game_state(). - Разделить перегруженное состояние
Game. Отделить игровой прогресс от состояния интерфейса и ресурсов заставок; оставить единственное объявлениеg_game. - Разделить обязанности
level.c. При плановом рефакторинге вынести данные уровня, runtime-состояние объектов и рендер в отдельные модули. Не совмещать эту работу с изменениями физики; отдельно описать исходный ID тайла, собранное/активное состояние и чисто рендерные флаги. - Вынести доступ к ресурсам из
main.c. Перенестиutil_open_file()и его контракт изmain.c/types.hв отдельный модуль файловых ресурсов, сохранив единую точку открытия файлов для уровней, текстур, звуков и сохранений. - Перенести цвета UI из
level.h. Создать отдельный модуль темы или UI-констант, чтобы код уровня не определял оформление меню и HUD. - Отделить обработчики эффектов тайлов без изменения семантики. Вынести изменение
счёта, карты и состояния игрока из большой функции
testTile()в именованные обработчики, но сохранить точный порядок и момент побочных эффектов внутри обхода коллизий оригинала. Не откладывать все эффекты до конца физического шага; отдельно отложить только выход через эквивалентmExitFlag. - Убрать управление UI из save-модуля. Возвращать из обновления рекорда результат
«новый рекорд», чтобы
game.cсам менялnew_best_score, а save-код не изменял состояние игры напрямую.
- Разделить декодирование PNG, хранение текстур и их отрисовку. Оставить декодеру
преобразование файла в пиксели, вынести размещение RAM/VRAM и swizzling в текстурный
модуль, а
png_draw_*заменить операциями нативного PSP-рендера надtexture_t. - Ввести менеджер игровых ресурсов. Обращаться к текстурам, звукам и другим ассетам
по стабильным идентификаторам; централизовать загрузку, владение и освобождение на
границах экранов и уровней. Игровая логика не должна напрямую вызывать
png_load_*. - Декодировать PNG без промежуточной копии всего сжатого файла. Передавать открытый
через общий ресурсный модуль
FILE*непосредственно в файловый интерфейс stb_image, сохранив обработку ошибок и единый способ поиска файлов. - Изолировать PSP-зависимый код. Сосредоточить вызовы
sceGu*,sceCtrl*, EDRAM, системного времени и lifecycle в нативных платформенных модулях с узкими интерфейсами; игровая логика, физика и парсер уровня не должны зависеть от заголовков PSPSDK. - Подготовить общие модули движка для
bounce_zeroиbounce_back. Совместно использовать нативные PSP-модули рендера, ввода, звука, ресурсов и времени, оставив правила игры, данные и контент в отдельных game-модулях без копирования реализации.
- Ограничить поиск места при увеличении мяча. Сохранить порядок позиций оригинала, но ввести максимальный радиус; если свободного места нет, отменять увеличение без бесконечного цикла и без изменения позиции игрока.
- Валидировать заголовок и moving objects уровня. Отклонять данные, где старт,
выход или области объектов выходят за карту,
topLeft >= bottomRightлибо размер объекта меньше допустимого. - Описать пары составных колец в метаданных. Для каждой части хранить смещение до
парной части и ID обоих неактивных состояний, чтобы
deactivate_ring_pair()не кодировал диапазоны и переходы13→17,21→25и другие вручную. - Именовать размер записи moving object. Заменить литерал
8в проверке бинарного уровня на константу формата и использовать её при валидации и чтении записи.
- Сделать запись сохранения неблокирующей. При наличии dirty-данных запускать
sceUtilitySavedataInitStart()один раз и продвигать utility state из основного цикла, продолжая обновлять и рисовать UI. При ошибке сохранять dirty-флаг для повторной записи. Не запускать savedata utility, если данные не менялись. - Создавать начальный слот сохранения на первом запуске. Перед первой записью
выставлять dirty-состояние либо использовать отдельную принудительную запись, чтобы
save_store_data()не завершался до созданияDATA.BIN. - Убрать инициализацию из
save_get_data(). Инициализировать Savedata явно при старте приложения; getter должен только вернуть готовые данные или статус ошибки и не запускать Utility/IO из render-кода.
- Убрать дублирующий
graphics_begin_plain()вrender_game(). Оставить одно переключение перед HUD после завершения текстурированного переднего слоя. - Убрать повторный flush перед отрисовкой текста.
graphics_draw_text()не должен сначала вызыватьgraphics_flush_batch(), а затем повторно делать это внутриgraphics_begin_textured(); оставить переключение состояния единственной точкой сброса. - Заменить
gravity == -30на именованную константу. ИспользоватьUWATER_LARGE_MAX_GRAVITY, не меняя численное поведение оригинала. - Убрать повторный
#include "graphics.h"изsplash.c. - Убрать ненужный
#include "level.h"изtile_table.c. - Вычислять число метаданных из массива. Возвращать из
tile_meta_count()значениеsizeof(TILE_DB) / sizeof(TILE_DB[0]), чтобы литерал55не расходился с таблицей.
- Исправить учёт release-хвоста при микшировании. Учитывать затухающий голос в нормализации, чтобы наложение звуков не давало перегруз перед clamp.
- Сравнить equal-power и текущее усреднение микса. Оставить новый вариант только при слышимом улучшении на PSP без потери характера оригинальных Nokia OTT-звуков.
- Сократить статическую память OTT-плееров. Подобрать фактический предел числа нот вместо трёх массивов по 1024 элемента и проверить все три оригинальных SFX.
- Исправить контракт
sound_init(). Использовать единый формат результата, корректно обрабатывать ошибку вmain.cи выполнятьpspAudioEnd()вместе с остальным cleanup на всех путях после частичной инициализации. - Возвращать ошибки OTT-парсера. Отличать корректный файл от неподдерживаемого или повреждённого и не запускать плеер с частично заполненной структурой.
- Не менять
g_game.stateиз HOME callback. Callback должен сигнализировать выход через PSP event flag или другой потокобезопасный канал, а основной цикл — применить его в безопасной точке. - Обрабатывать ошибки PSP callback API. Проверять создание и регистрацию callback, создание и запуск потока; при неудаче завершать инициализацию предсказуемо.
- Не сохранять старые edge-события при ошибке чтения контроллера. Помечать snapshot ввода невалидным и очищать frame edges, не выдавая прошлое состояние за новый кадр.
- Добавить опциональное управление аналоговым стиком. Включать analog sampling и преобразовывать оси с dead zone в те же цифровые команды, сохраняя крестовину и исходную дискретную механику основным способом управления.