Напиши профессиональный мод для Geode под Geometry Dash 2.20...
Prompt
Напиши профессиональный мод для Geode под Geometry Dash 2.2081 (Geode v5.11.2, C++23). Платформы: Windows, Android32, Android64. Весь код одним файлом src/main.cpp. Интерфейс только на русском (проверь, какие символы доступны в шрифтах GD; если кириллица не отображается, укажи это в допущениях). ПРАВИЛА - Не придумывай методы и поля. Если не уверен в сигнатуре, помечай // VERIFY и в конце ответа перечисли все такие места. - В конце дай список допущений и рискованных мест. - Если я пришлю лог сборки, исправляй только указанное, остальное не переписывай. - Не переопределяй деструкторы в $modify, не используй Mod::get() в статической инициализации, объявляй namespace и переменные раньше их использования. ПРОВЕРЕННЫЕ СИГНАТУРЫ (bindings 2.2081) GJBaseGameLayer: void handleButton(bool down, int button, bool isPlayer1); void queueButton(int button, bool push, bool isPlayer2, double timestamp); void processQueuedButtons(float dt, bool clearInputQueue); void processCommands(float dt, bool isHalfTick, bool isLastTick); int checkCollisions(PlayerObject*, float dt, bool ignoreDamage); double getModifiedDelta(float dt); bool shouldUseSubstepForButton(float dt); void removeReleasedButtons(); void recordAction(int button, bool down, bool player2); поле m_queuedButtons (gd::vector<PlayerButtonCommand>) В GJBaseGameLayer НЕТ pushButton/releaseButton (они есть только в PlayerObject). PlayLayer: bool init(GJGameLevel*, bool useReplay, bool dontCreateObjects); CheckpointObject* createCheckpoint(); void loadFromCheckpoint(CheckpointObject*); CheckpointObject* markCheckpoint(); void storeCheckpoint(CheckpointObject*); void removeCheckpoint(bool first); void levelComplete(); void onQuit(); void pauseGame(bool); void resumeAndRestart(bool fromStart); void resetLevelFromStart(); virtual void resetLevel(); virtual void destroyPlayer(PlayerObject*, GameObject*); void takeStateSnapshot(); void compareStateSnapshot(); поля m_isPaused, m_currentCheckpoint, m_checkpointArray. Ранее с успехом компилировались обращения к: m_player1, m_player2, m_playerDied, m_clickBetweenSteps, m_clickOnSteps, m_gameState.m_isDualMode, m_isOnGround, m_yVelocity, m_isUpsideDown, m_lastPosition, m_isDart. ПРОВЕРЕННЫЕ API GEODE 5 - Приоритет хуков: в классе $modify добавь static void onModify(auto& self) { if (!self.setHookPriorityPre("GJBaseGameLayer::queueButton", Priority::First)) geode::log::warn("..."); } Для кода «после всех» используй setHookPriorityPost с Priority::Last и вызывай оригинал в начале хука. Имя функции пиши вместе с классом. Хуки перегруженных функций приоритетом не задаются. - Асинхронщина: file::pick(mode, options) возвращает arc::Future<PickResult>. Подключи <Geode/utils/async.hpp>. Запуск: async::spawn(file::pick(...), callback) либо async::TaskHolder (его поле хранится в классе, чтобы задача отменялась при уничтожении). Колбэк вызывается в главном потоке. Точный тип PickResult и способ проверки отмены (путь или std::nullopt) пометь // VERIFY. - В Geode есть geode::utils::getInputTimestamp (шкала времени входных событий, VERIFY: единицы и связь с timestamp в queueButton). 1. СВОЙ CBF - Клик внутри физического шага делит шаг на подшаги, между ними вызывается handleButton. Своя реализация, независимая от мода Click Between Frames (id syzzi.click_between_frames) и ванильных Click On/Between Steps. - В режимах записи и воспроизведения перехватывай queueButton с приоритетом Priority::First (Pre) и не вызывай оригинал: так хуки других модов (включая CBF) для этой функции не выполнятся. Когда бот выключен, вызывай оригинал как обычно. - Если мод CBF загружен, покажи предупреждение (Loader::get()->isModLoaded). - Формат клика в макросе: номер шага (uint32) + доля шага (float или double 0..1). Обоснуй выбор в допущениях. Как сопоставить timestamp из queueButton с границами шагов, реши сам и пометь VERIFY, если не уверен. 2. ФИКСИРОВАННЫЙ TPS И УСТОЙЧИВОСТЬ К ЛАГАМ - Физика идёт строго фиксированными шагами (по умолчанию 240 TPS), независимо от FPS и просадок. - Хукни GJBaseGameLayer::getModifiedDelta: вызови оригинал (он меняет внутреннее состояние слоя), затем верни фиксированное значение. Приоритет хука: Last (Post), чтобы другие TPS bypass и спидхак-моды, включая CBF (он тоже хукает эту функцию), не могли изменить результат, пока бот включён. Когда бот выключен, хук ничего не меняет. Проверку работоспособности на 2.2081 пометь VERIFY. - Запись и воспроизведение используют один и тот же алгоритм и одни и те же шаги, привязка только к номеру шага и доле шага, никаких wall-clock часов. - Несколько шагов за кадр обрабатываются по порядку с применением всех кликов своего шага. - Макрос хранит TPS и проверяет совпадение при загрузке. 3. ПРАКТИКА И ЧЕКПОИНТЫ - Запись работает в практике и в обычном режиме. - При загрузке чекпоинта запись не прерывается: макрос обрезается до шага чекпоинта (удаляются действия с step >= шага чекпоинта), состояние зажатых кнопок на момент чекпоинта восстанавливается, запись продолжается без дефектов. - Смерть без чекпоинта при записи: запись сохраняется в буфер, запись выключается, игрок выбирает: сохранить макрос из буфера или начать новую запись. - Поддерживаются все режимы: прыжок, платформер (влево и вправо), Dual, Player 2. 4. ВОСПРОИЗВЕДЕНИЕ - Ручной ввод по умолчанию не блокируется. Переключатель «Блокировать ввод». - Если бот удерживает кнопку и игрок ставит игру на паузу, зажатая кнопка не отпускается. После возобновления состояние кнопок остаётся прежним. - По завершении макроса воспроизведение выключается, макрос остаётся в буфере для сохранения или удаления. - Отправку рекордов и прогресса не меняй. 5. СКОРОСТЬ ЗАПИСИ - Поле числа от 0.01 до 2, применяется сразу. - Макрос от скорости записи не зависит и всегда воспроизводится на 1.0x. - Музыка замедляется корректно (высота тона через FMOD, проверь поля FMODAudioEngine, VERIFY). При смерти, паузе и выходе скорость и тон возвращаются к норме. Если возможно, учитывай и звуковые эффекты. 6. ПОКАДРОВЫЙ РЕЖИМ (экспериментальный) - Настройка «размер шага» в физических шагах. При включении появляются две кнопки у левого края: «вправо» (продвинуть на выбранный шаг, применяя записанные клики) и «влево» (вернуть назад на выбранный шаг). - Шаг назад: хранить снимки состояния через равные интервалы шагов (через createCheckpoint/loadFromCheckpoint), при шаге назад загружать ближайший снимок раньше целевого шага и пересимулировать вперёд по макросу. Известный риск: не всё состояние восстанавливается (RNG, триггеры, анимации). В PlayLayer есть takeStateSnapshot/compareStateSnapshot, VERIFY их семантику и можно ли ими проверять совпадение состояния после пересимуляции. - Если шаг назад невозможно сделать надёжно, реализуй только шаг вперёд, кнопку «влево» сделай неактивной с пояснением по-русски и опиши причину в допущениях. Режим выключен по умолчанию и не должен влиять на запись и воспроизведение, когда он выключен. 7. ИНТЕРФЕЙС - Кнопка у левого края экрана, только в меню паузы. Её можно зажать мышью или пальцем и перетащить. Последнюю позицию запоминай (сохранённое значение мода). Нажатие плавно открывает панель. - Одна вкладка «Бот». Аккуратно под Windows и телефон: относительные размеры, учёт разрешения и выреза экрана (в Geode есть safe area API для Android и iOS), тач и мышь. - В панели: переключатель записи, «Выключить бота» (выключает и запись и воспроизведение), «Запустить макрос», скорость записи, блокировка ввода, покадровый режим и размер шага, имя макроса и «Сохранить», папка макросов, список макросов с загрузкой, воспроизведением и удалением. - Используй элементы Geode (ScrollLayer, TextInput, Notification и т.д.). 8. ФАЙЛЫ МАКРОСОВ - Бинарный формат: заголовок (magic, версия формата, TPS, ID уровня), затем записи (step, доля шага, кнопка, down, player). При загрузке проверяй magic, версию и размеры, защищайся от повреждённых файлов. Расширение .gdbot. - Папку выбирает пользователь через file::pick (режим OpenFolder, async, см. выше). Внутри создай подпапку (например «GDBotMacros»), выбранный путь запоминай. - ОБЯЗАТЕЛЬНО после выбора проверь запись (создание подпапки и тестового файла). Если запись не удалась (на Android доступ к папкам ограничен системой), покажи понятное сообщение по-русски и предложи выбрать другую папку или использовать папку мода (Mod::get()->getSaveDir(), она доступна для записи всегда) как запасную. Скажи об этом честно: гарантировать доступность выбранной папки на всех устройствах нельзя. - Имя файла очищай от недопустимых символов, при совпадении спрашивай или добавляй суффикс. ФОРМАТ ОТВЕТА Один полный файл src/main.cpp. После кода: список допущений, список мест с VERIFY, краткие инструкции, что проверить после сборки.
Response not available