Skip to content

Latest commit

 

History

History
207 lines (182 loc) · 21.7 KB

File metadata and controls

207 lines (182 loc) · 21.7 KB

TODO

Будущие исправления и нововведения, которые остаются актуальными после сверки текущего порта с байткодом оригинала. При изменении физики и формата уровней приоритетом остаётся точное воспроизведение поведения оригинальной игры.

Соответствие оригиналу

  • Исправить переключение чекпоинтов. При активации нового чекпоинта старый должен переходить в исходное состояние, эквивалентное записываемому оригиналом значению 128, а новый — значению 136. Текущий level_deactivate_old_checkpoint() записывает старому ID 8, поэтому фактически не воспроизводит переход из байткода.
  • Сохранять оригинальное состояние собранных тайлов. Для extra life и других одноразовых объектов использовать состояние, эквивалентное 128, вместо произвольной замены на ID 0; физика должна читать 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-парсера. Отличать корректный файл от неподдерживаемого или повреждённого и не запускать плеер с частично заполненной структурой.

PSP lifecycle и ввод

  • Не менять g_game.state из HOME callback. Callback должен сигнализировать выход через PSP event flag или другой потокобезопасный канал, а основной цикл — применить его в безопасной точке.
  • Обрабатывать ошибки PSP callback API. Проверять создание и регистрацию callback, создание и запуск потока; при неудаче завершать инициализацию предсказуемо.
  • Не сохранять старые edge-события при ошибке чтения контроллера. Помечать snapshot ввода невалидным и очищать frame edges, не выдавая прошлое состояние за новый кадр.
  • Добавить опциональное управление аналоговым стиком. Включать analog sampling и преобразовывать оси с dead zone в те же цифровые команды, сохраняя крестовину и исходную дискретную механику основным способом управления.