diff --git a/README.md b/README.md index 8a0b2a0..c5bd57a 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Output: `build/libs/LoParkour-1.3.0.jar` ## ✨ Features - **Infinite Generation** - Parkour generates endlessly as you play -- **Multiple Game Modes** - Default, Speedrun, Hardcore, Gravity Shift, Elytra +- **Multiple Game Modes** - Default, Speedrun, Gravity Shift, Elytra - **Ghost System** - Race against top player recordings - **Custom Schematics** - Create and use your own parkour structures - **Visual Themes** - Customize block styles and particle effects @@ -57,7 +57,6 @@ Output: `build/libs/LoParkour-1.3.0.jar` - **Default** - Classic infinite parkour - **Speedrun** - Blocks disappear after a few seconds -- **Hardcore** - Lose all rewards on fall - **Gravity Shift** - Random gravity effects (levitation, slow falling, jump boost) - **Elytra** - Fly through particle rings diff --git a/TODO.md b/TODO.md index 009697d..c18ad2f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,324 +1,109 @@ -# LoParkour — TODO (обновлено 2026-06-01) +# LoParkour Bug Tracker -> Roadmap + **честный аудит** репозитория. Старые галочки «✅» в фазах 1–4 не соответствуют коду — см. раздел **Аудит**. +## CRITICAL -## Легенда +### 1. ProfileValue.asInt/asDouble — NumberFormatException при пустой строке +- **Файл:** `Profile.java:57-58` +- **Описание:** `Integer.parseInt(value)` и `Double.parseDouble(value)` падают при пустой строке `""`, которая возвращается по умолчанию при отсутствии ключа (`new ProfileValue("")`) +- **Фикс:** Добавить дефолтное значение либо кидать понятное исключение, а не NFE -- 🔴 Критично — блокирует сборку / релиз / основной UX -- 🟡 Важно — заявленная функциональность или i18n -- 🟢 Планово — улучшение, рефакторинг -- ✅ Готово (проверено в коде) +### 2. Bukkit.createBlockData — IllegalArgumentException при невалидном materialName +- **Файл:** `GeneratorProfileManager.java:133` +- **Описание:** Если materialName из конфига невалидный (опечатка в generation.yml), `Bukkit.createBlockData()` кинет исключение +- **Фикс:** Валидировать materialName перед вызовом, либо ловить конкретно на строке 133 ---- +### 3. JumpDirector диагональный дрейф — heading никогда не выпрямляется +- **Файл:** `JumpDirector.java:87-99` +- **Описание:** `getRecommendedHeading()` всегда создает `Vector(1,0,1)` для X и Z границ. После нормализации heading становится диагональным и НИКОГДА не возвращается к осевому направлению +- **Фикс:** Использовать `Vector(1,0,0)` для X и `Vector(0,0,1)` для Z. Сбрасывать heading на исходный при отсутствии границы -## Аудит 2026-06-01 (снимок) +### 4. subList().clear() на synchronizedList(LinkedList) — повреждение node связей +- **Файл:** `GeneratorCleanup.java:50-56` +- **Описание:** `history.subList(0, removeCount).clear()` на `Collections.synchronizedList(new LinkedList<>())` — compound операция не атомарна. ConcurrentModificationException или повреждение связей +- **Фикс:** Оборачивать в `synchronized(history)` или использовать CopyOnWriteArrayList -| Область | Факт | -|--------|------| -| **Сборка** | ✅ `./gradlew compileJava` проходит | -| **Адаптивность** | 🟡 Подключено: `AdaptiveServices`, `MetricsCollector` на генераторе, профиль после fall/reset | -| **Elytra** | 🟡 `ElytraMode` + `ElytraGenerator` есть; отдельного `mode/elytra/*` из старого TODO **нет** | -| **Размер файлов** | 🔴 Цель «все < 200 строк» **не выполнена**: 18 файлов > 150, 2 > 200 | -| **i18n** | 🟡 Основные потоки через `Locales`; остаётся Phase 0 split + `MessageKeys` | -| **GUI** | 🟡 Основные меню на LoLib OK; **заглушки** `Menu`/`PagedMenu`, `DynamicMenu` вырезан | -| **TODO.md** | Ранее помечено ✅ то, что не сделано — статусы ниже исправлены | +### 5. Мультиплеер — runaway генерация блоков +- **Файл:** `PlayerInteractionHandler.java:116-118` + `LifecycleTickManager.java:89-97` +- **Описание:** Каждый игрок триггерит `generate(1)` при скоре. N игроков = N блоков/тик. maintainBlockLead добавляет еще. Положительная обратная связь +- **Фикс:** Не больше 1 блока за тик независимо от числа игроков, кэп на maintainBlockLead -**Статистика:** ~160 `.java` файлов, средний размер ~85 строк, максимум **252** (`ElytraGenerator.java`). +### 6. Система схематик полностью сломана +- **Файл:** `PlayerInteractionHandler.java:111-113` + `BlockPlacer.java:207-212` +- **Описание:** `isSchematicEndBlock` возвращает true для ЛЮБОГО schematic блока. При входе в схематик сразу удаляется + блоки засчитываются как очки +- **Фикс:** `isSchematicEndBlock` должен проверять только ПОСЛЕДНИЙ блок схематика. Исключить schematic блоки из скоринга ---- +### 7. Прыжки за границами валидации +- **Файл:** `JumpCalculator.java:101-114` +- **Описание:** JumpOffsetGenerator генерирует offset до 5 блоков, total horizontal = sqrt(4²+5²)=6.4 при лимите 4.1. Retry-цикл не гарантирует валидность за 10 попыток +- **Фикс:** Ограничить randomOffset пределами валидатора: `maxRandomOffset = sqrt(maxHorizontal² - distance²)` -## Фаза 0: Полный рефакторинг (KISS · DRY · SOLID) — приоритет +## HIGH -> Цель: предсказуемая структура, тонкие классы, без «божественных» файлов. +### 8. angleInY вращает offset относительно глобального Option.HEADING +- **Файл:** `JumpCalculator.java:105-113` +- **Описание:** rotateAroundY использует разницу между текущим heading и Option.HEADING, а не между текущим и предыдущим heading. Блоки уходят вбок +- **Фикс:** Вращать offset относительно предыдущего heading -### Правила (жёсткие) +### 9. Дублирование canJump(Location) и canJump(Vector) — рассинхронизация +- **Файл:** `JumpValidator.java:73-161` +- **Описание:** Два метода canJump. Location-версия учитывает slab-корректировку, Vector — нет +- **Фикс:** Убрать дублирование или добавить adjustment в Vector-версию -| Правило | Значение | -|--------|----------| -| **Целевой размер** | ~**150** строк на файл | -| **Жёсткий потолок** | **190–200** строк (исключения только с комментом в PR) | -| **Файлов в папке** | ≤ **6** (при росте — новая подпапка) | -| **KISS** | Один публичный сценарий = один класс / use-case | -| **DRY** | Сообщения только через `Locales`; команды — `Messages` / `admin.*` | -| **SOLID** | `generator` не импортирует `adaptive`; связь только через `GeneratorEventListener` + DI в bootstrap | +### 10. synchronizedList — итерации без синхронизации +- **Файл:** `GeneratorState.java:35` +- **Описание:** Итерации по `history` (cleanupDistantBlocks, historyContains, indexOfBlock) не обернуты в `synchronized`. Пока все на main thread, но это хрупко +- **Фикс:** Добавить `synchronized(history)` на все итерации, либо убрать synchronizedList -### Целевая структура пакетов (✅ 2026-06-01) +### 11. heading.clone() без null-проверки +- **Файл:** `JumpCalculator.java:101-103` +- **Описание:** `generator.state.heading.clone()` упадет с NPE, если heading не инициализирован до первого вызова calculateJumpOffset +- **Фикс:** Добавить null-guard: если heading == null, инициализировать из Option.HEADING -``` -dev.loki.loparkour/ -├── LoParkour.java # main plugin class -├── command/ -│ ├── LoParkourCommand.java -│ ├── player/ # PlayerCommandHandler -│ ├── admin/ # AdminCommandHandler -│ └── schematic/ # SchematicCommandHandler -├── config/ -│ ├── core/ # Config, ConfigLoader, ConfigAccessor, ConfigUpdater -│ ├── locale/ # Locales, LocaleLoader, LocaleCache -│ └── options/ # Option + OptionSQL, OptionGeneral, … -├── generator/ -│ ├── core/ # ParkourGenerator, Profile, GeneratorState, … -│ ├── jump/, lifecycle/, profile/, effect/ -│ └── core/GeneratorEventListener -├── mode/ -│ ├── base/ # Mode, MultiMode, Modes, ModeMessages -│ ├── impl/ # DefaultMode, SpeedrunMode, … -│ └── elytra/ # ElytraGenerator + ring/physics/hud -├── menu/ -│ ├── core/ # LPMenu, MainMenu, Menus, ParkourOption -│ └── play/, settings/, community/, lobby/ -├── player/ -│ ├── core/ # ParkourUser, ParkourPlayer -│ ├── service/ # UserRegistry, hotbar, scoreboard, … -│ ├── spectator/ -│ └── data/ -├── util/ -│ ├── text/, item/, gui/, world/, particle/, misc/ -├── storage/ -│ ├── Storage.java # фасад -│ ├── sql/ # StorageSQL, SQL*, миграции -│ └── disk/ # StorageDisk (JSON) -├── session/ -│ ├── core/Session.java -│ └── manager/ # State, Player, User managers -├── listener/ -│ ├── player/, gameplay/, schematic/ -├── leaderboard/ -│ ├── model/Score, core/, persistence/ -├── ghost/ -│ ├── model/, core/ -├── hook/ -│ ├── papi/, holo/, floodgate/, vault/ -├── schematic/ -│ ├── registry/Schematics.java -│ └── lpschem/ -├── api/core/ + api/event/ -├── reward/core/, style/core/, world/core/ -├── adaptive/bootstrap/AdaptiveServices -├── bootstrap/PluginBootstrap -└── … -``` +## MEDIUM -Скрипт переноса: `scripts/refactor-packages.ps1` (+ `fix-imports-after-refactor.ps1`). +### 12. BlockSelector.selectBlockData рекурсия при пустом списке стилей +- **Файл:** `BlockSelector.java:32-33` +- **Описание:** Если style == null, код сбрасывает стиль на первый. `orElseThrow()` упадет, если список пуст +- **Фикс:** Проверка `if (styles.isEmpty()) return Material.STONE.createBlockData()` -### Очередь разбиения (файлы > 150 строк) +### 13. checkPlayerFall — break вместо continue +- **Файл:** `LifecycleTickManager.java:74-78` +- **Описание:** При падении двух игроков в одном тике, второй пропускает обработку до следующего тика +- **Фикс:** Заменить break на continue -| Приоритет | Файл | Строк | Действие | -|-----------|------|-------|----------| -| ✅ 1 | `ElytraGenerator.java` | 101 | → `elytra/ElytraRingLoop`, `ElytraScoring`, `ElytraHud`, `ElytraPlayerKit`, `ElytraFireworkListener` | -| ✅ 2 | `ParkourPlayer.java` | 166 | → `ParkourHotbar` | -| 🟡 3 | `PAPIHook.java` | 194 | → placeholders по группам | -| 🟡 4 | `LoParkour.java` | 192 | → `bootstrap/PluginBootstrap.java` | -| 🟡 5 | `MetricsCollector.java` | 187 | → session hook + jump hook + flush | -| 🟡 6 | `StorageSQL.java` | 179 | → queries / mapper / connection (уже частично) | -| 🟡 7 | `BlockPlacer.java` | 176 | | | -| 🟡 8 | `JumpValidator.java` | 172 | | | -| 🟡 9 | `LoParkourCommand.java` | 170 | → делегаты только маршрутизация | -| 🟡 10 | `CoopMode.java` | 165 | → `CoopGenerator` отдельный файл | -| 🟢 | остальные 151–159 | 8 файлов | после топ-10 | +### 14. removeTrailBlocks сдвигает индексы истории +- **Файл:** `GeneratorCleanup.java:79-93` +- **Описание:** `history.subList(startIndex, endIndex).clear()` сдвигает индексы при параллельном вызове historyContains +- **Фикс:** Защититься копированием или проверять синхронизацию -### Чек-лист фазы 0 +### 15. Дублирование isSlabMaterial +- **Файл:** `JumpValidator.java:67-71` +- **Описание:** JumpValidator и BlockSelector имеют дублирующуюся логику определения slab. Рассинхронизация при изменениях +- **Фикс:** Вынести в общий утилитарный метод -- [ ] Нет файлов > 200 строк (кроме явно согласованных) -- [x] Пакеты разнесены (config/mode/command/generator/menu/player/util) -- [ ] Нет циклических зависимостей `generator` ↔ `adaptive` -- [ ] Единый `MessageService` / `Locales` для player + console + action bar + title -- [ ] Удалить или подключить мёртвый код: `Menu`, `PagedMenu`, `DynamicMenu`, `MenuStub` -- [ ] `Option.java` — только фасад, без роста (делегаты в `options/*`) -- [x] `./gradlew build` зелёный -- [ ] README/TODO не врут про «всё готово» +### 16. DifficultyScore thresholds для льда (1.0/2.0) могут быть недостижимы +- **Файл:** `GeneratorProfileManager.java:57-69` +- **Описание:** packed_ice добавляется при difficulty >= 1.0, blue_ice при >= 2.0. Многие конфиги не достигают этих значений. Лед не появляется +- **Фикс:** Нормализовать difficulty до предсказуемой шкалы или изменить thresholds ---- +### 17. schematicBlocks — ArrayList без thread safety +- **Файл:** `GeneratorState.java:41` +- **Описание:** schematicBlocks это plain ArrayList, без синхронизации, в отличие от history +- **Фикс:** Добавить синхронизацию или документировать main-thread-only -## Баги и долг (найдено при аудите) +## LOW -### 🔴 Сборка +### 18. scoredBlocks HashSet не очищается при fall +- **Файл:** `ParkourPlayer.java:125-127` +- **Описание:** HashSet растет бесконечно. При возрождении на тех же координатах не засчитает блоки +- **Фикс:** scoredBlocks.clear() при fall/reset -| # | Проблема | Статус | -|---|----------|--------| -| B1 | `SQLConnectionManager` visibility | ✅ | -| B2 | `AdaptiveConfig` + `Config.CONFIG` | ✅ | +### 19. getFormattedTime() — Date(elapsed) хак +- **Файл:** `GeneratorStatistics.java:90-91` +- **Описание:** `new Date(elapsed)` — Date ожидает timestamp от 1970, а передается длительность. Сломается при >24ч +- **Фикс:** Использовать Duration API для форматирования -### 🟡 Локализация (i18n) — большинство закрыто - -| # | Проблема | Статус | -|---|----------|--------| -| I1 | `LPMenu` back/next | ✅ `other.back`, `other.next` | -| I2 | `other.no_menu_items` | ✅ | -| I3 | joining disabled | ✅ `ModeMessages` + `other.joining_disabled` | -| I4 | Elytra messages | ✅ `modes.elytra.*` | -| I5–I11 | Race, coop, admin, schematic, commands, bungee, GUI stubs | ✅ (см. `locales/*.yml`) | -| I12 | `ghost.saved` в en | ✅ | -| I13 | `%[a-z]` vs `%s` | 🟢 документировать / унифицировать позже | -| I14 | reload NPE с консоли | ✅ `getString(sender, …)` | -| I15 | недостающие ключи | ✅ основной набор добавлен | - -### 🟡 Логика / архитектура - -| # | Проблема | Где | -|---|----------|-----| -| A1 | `MetricsCollector` на генераторе | ✅ `SessionStateManager` + `AdaptiveServices.attachToGenerator` | -| A2 | Adaptive → профиль генерации | ✅ `GeneratorProfileManager` + `AdaptiveDifficulty` | -| A3 | `adaptive.*` в config.yml | ✅ | -| A4 | SQL таблица stats | ✅ `StorageSQL` + `SQLMigrationManager.createAdaptiveStatsTable` | -| A5 | `DynamicMenu` мёртвый | 🔴 | -| A6 | STONE debug-spam | ✅ только при отсутствии `.material` | -| A7 | Несогласованные пути locale: `play.single.*` vs `modes.*` для режимов | `*Mode.getItem()` | -| A8 | `sendHelp` упоминает `/LoParkour`, игроки используют `/parkour` | `PlayerCommandHandler` | - -### 🟢 Мелочи - -| # | Проблема | -|---|----------| -| M1 | `SpectatorMode`: MiniMessage `` vs legacy `§c` в других режимах | -| M2 | `HardcoreMode` / др.: смешение `ColorUtil` и сырого `sendMessage` | -| M3 | Unit/integration тесты в TODO помечены ✅ — **фактически** только 2 test-класса | - ---- - -## Фаза 1: Рефакторинг структуры (статус реальный) - -### Архитектура - -| # | Задача | Статус | -|---|--------|--------| -| 1 | Разбить `ParkourGenerator` | 🟡 Частично (composition), файл 159 строк — ок | -| 2 | `GeneratorProfileManager` | 🟡 Есть, без отдельного `ProfileCalculator` | -| 3 | statistics из generator | 🟡 `GeneratorStatistics` внутри generator, не пакет `statistics/` | -| 4 | Структура пакетов `generator/core`, `jump`, `lifecycle` | 🟡 Частично | -| 5 | `GeneratorEventListener` | ✅ Интерфейс + вызовы в `PlayerInteractionHandler` | - -### Большие файлы (цель < 200) — **не выполнено** - -| Файл | Строк | Статус | -|------|-------|--------| -| ElytraGenerator.java | 101 | ✅ | -| ParkourPlayer.java | 166 | ✅ | -| PAPIHook, LoParkour, MetricsCollector, … | 151–194 | 🟡 см. Фаза 0 | - ---- - -## Фаза 2: Адаптивная система (статус реальный) - -| # | Задача | Статус | -|---|--------|--------| -| 1 | `adaptive/model/` | 🟡 Классы есть | -| 2 | `adaptive/storage/` | ✅ compile + SQL/file repo | -| 3 | SQL таблицы | ✅ CREATE в `StorageSQL` / `SQLMigrationManager` | -| 4 | File JSON storage | ✅ | -| 5 | `MetricsCollector` | ✅ | -| 6 | Calculator / Adjuster в профиле | ✅ через `AdaptiveDifficulty` | -| 7 | `AdaptiveMode`, `StatsService` | 🔴 отдельный режим не нужен пока | -| 8 | `config.yml` → `adaptive.*` | ✅ | - ---- - -## Фаза 3: ElytraMode (статус реальный) - -| # | Задача | Статус | -|---|--------|--------| -| 1 | `ElytraMode` | 🟡 Есть | -| 2 | `ElytraGenerator` + helpers | ✅ Разбит на `mode/elytra/*` | -| 3 | `ElytraConfig`, `RingRenderer`, … | ✅ В пакете `mode/elytra/` | -| 4 | `modes/elytra.yml` | 🔴 Нет | -| 5 | i18n для elytra | ✅ | - ---- - -## Фаза 4: Режимы (статус реальный) - -| Режим | Меню | i18n join disabled | Примечание | -|-------|------|-------------------|------------| -| default, speedrun, hardcore, gravity-shift | ✅ SingleMenu | 🔴 хардкод EN | | -| elytra, race | ✅ | 🔴 | race — ещё UI хардкод | -| coop | ❌ (null item — ок) | 🔴 | title хардкод | -| spectator | ✅ | 🟡 частично Locales | | -| roguelike | — | — | **Нет в коде** (в старом TODO было ✅ — ошибка) | - ---- - -## Фаза 5: i18n — единая система сообщений - -| # | Задача | Статус | -|---|--------|--------| -| 1 | `other.back` / `other.next` | ✅ | -| 2 | Ключи из таблицы I* | ✅ | -| 3 | `MessageKeys` enum или constants class (DRY путей) | 🟢 | -| 4 | `Locales.getString(CommandSender)` — использовать везде для консоли | 🟡 Частично | -| 5 | Убрать debug-spam STONE | ✅ | -| 6 | Синхронизировать `en.yml` ↔ `ru.yml` | 🟡 периодический diff | -| 7 | Проверка CI: скрипт diff ключей locale vs `Locales.getString`/`sendTranslated` | 🟢 | - ---- - -## Технический долг (актуальный) - -| # | Задача | Приоритет | -|---|--------|-----------| -| 1 | Compile adaptive | ✅ | -| 2 | Bootstrap MetricsCollector | ✅ | -| 3 | SQL adaptive table | ✅ | -| 4 | Удалить или реализовать `Menu`/`PagedMenu`/`DynamicMenu` | 🟡 | -| 5 | Реальные тесты adaptive + modes | 🟢 | - ---- - -## Чек-лист перед релизом (честный) - -- [x] `./gradlew build` без ошибок -- [x] Нет хардкода RU в `LPMenu` / основных режимах и админке -- [ ] Полный diff ключей locale vs Java (CI) -- [ ] Адаптивность: ручной тест на сервере (`adaptive.enabled: true`) -- [ ] ElytraMode протестирован вручную -- [ ] Нет файлов > 200 строк (или исключения задокументированы) -- [ ] README / CHANGELOG соответствуют коду - ---- - -## SQL миграция (без изменений) - -```sql -CREATE TABLE IF NOT EXISTS loparkour_player_stats ( - player_uuid VARCHAR(36) PRIMARY KEY, - skill_rating DOUBLE DEFAULT 1.0, - sessions_count INT DEFAULT 0, - total_jumps INT DEFAULT 0, - total_falls INT DEFAULT 0, - longest_streak INT DEFAULT 0, - last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_skill_rating (skill_rating), - INDEX idx_last_updated (last_updated) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -``` - ---- - -## Оценка времени (пересмотр) - -| Фаза | Дней | Комментарий | -|------|------|-------------| -| **0: KISS/DRY/SOLID + лимит строк** | 5–8 | Блокирует нормальную поддержку | -| **5: i18n** | 2–3 | Можно параллельно с 0 | -| **2: Adaptive** | 3–4 | После зелёной сборки + bootstrap | -| **3: Elytra split** | 1–2 | После 0 | -| **4: Режимы / тесты** | 2–3 | | -| **Итого** | **13–20** | Старые «8–12 дней» занижены | - ---- - -## Метрики успеха (пересмотр) - -### Технические -- [ ] ≥ 90% файлов ≤ 150 строк -- [ ] 0 файлов > 200 строк -- [ ] SOLID: `adaptive` → listener/port, не прямой import generator internals -- [ ] `./gradlew test` — осмысленное покрытие, не «галочка в TODO» - -### Функциональные -- [ ] 2+ языка без хардкода в Java (кроме логов debug) -- [ ] Adaptive влияет на профиль после N сессий -- [ ] ElytraMode без монолита 250+ строк - -### Производительность -- [ ] TPS стабилен 50+ игроков (профилирование) -- [ ] SQL batch / flush adaptive < 50ms +### 20. isUnsafeParkourMaterial блокирует PUMPKIN и _BED без причины +- **Файл:** `BlockSelector.java:48-60` +- **Описание:** PUMPKIN не имеет onPlace-сканирования соседей. _BED безопасны при setBlockData с флагом false +- **Фикс:** Убрать лишние исключения diff --git a/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java b/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java index fb51d31..310190b 100644 --- a/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java +++ b/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java @@ -17,7 +17,6 @@ import dev.loki.loparkour.mode.impl.CoopMode; import dev.loki.loparkour.mode.impl.DefaultMode; import dev.loki.loparkour.mode.impl.ElytraMode; import dev.loki.loparkour.mode.impl.GravityShiftMode; -import dev.loki.loparkour.mode.impl.HardcoreMode; import dev.loki.loparkour.mode.impl.RaceMode; import dev.loki.loparkour.mode.impl.SpectatorMode; import dev.loki.loparkour.mode.impl.SpeedrunMode; @@ -76,7 +75,6 @@ public final class PluginBootstrap { Registry.register(new RaceMode()); Registry.register(new CoopMode()); Registry.register(new GravityShiftMode()); - Registry.register(new HardcoreMode()); Modes.init(); } diff --git a/src/main/java/dev/loki/loparkour/mode/base/Modes.java b/src/main/java/dev/loki/loparkour/mode/base/Modes.java index 7890482..f5494db 100644 --- a/src/main/java/dev/loki/loparkour/mode/base/Modes.java +++ b/src/main/java/dev/loki/loparkour/mode/base/Modes.java @@ -12,7 +12,6 @@ public class Modes { public static RaceMode RACE; public static CoopMode COOP; public static GravityShiftMode GRAVITY_SHIFT; - public static HardcoreMode HARDCORE; public static void init() { DEFAULT = (DefaultMode) Registry.getMode("default"); @@ -22,6 +21,5 @@ public class Modes { RACE = (RaceMode) Registry.getMode("race"); COOP = (CoopMode) Registry.getMode("coop"); GRAVITY_SHIFT = (GravityShiftMode) Registry.getMode("gravity-shift"); - HARDCORE = (HardcoreMode) Registry.getMode("hardcore"); } } diff --git a/src/main/java/dev/loki/loparkour/mode/impl/HardcoreMode.java b/src/main/java/dev/loki/loparkour/mode/impl/HardcoreMode.java deleted file mode 100644 index fcfc40f..0000000 --- a/src/main/java/dev/loki/loparkour/mode/impl/HardcoreMode.java +++ /dev/null @@ -1,85 +0,0 @@ -package dev.loki.loparkour.mode.impl; -import dev.loki.loparkour.mode.base.ModeMessages; -import dev.loki.loparkour.mode.base.Mode; -import dev.loki.loparkour.mode.base.Modes; - -import dev.loki.loparkour.config.core.Config; -import dev.loki.loparkour.config.locale.Locales; -import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator; -import dev.loki.loparkour.leaderboard.core.Leaderboard; -import dev.loki.loparkour.player.core.ParkourPlayer; -import dev.loki.loparkour.session.core.Session; -import dev.loki.loparkour.util.item.Item; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Hardcore Mode — lose all collected rewards on fall. - */ -public class HardcoreMode implements Mode { - - private final Leaderboard leaderboard = new Leaderboard(getName(), Leaderboard.Sort.SCORE); - - @Override - @NotNull - public String getName() { - return "hardcore"; - } - - @Override - @Nullable - public Item getItem(String locale) { - return Locales.getItem(locale, "play.single.hardcore"); - } - - @Override - @NotNull - public Leaderboard getLeaderboard() { - return leaderboard; - } - - @Override - public void create(Player player) { - if (!ModeMessages.checkJoiningEnabled(player)) { - return; - } - - ParkourPlayer pp = ParkourPlayer.getPlayer(player); - if (pp != null && pp.session.generator instanceof HardcoreGenerator) { - return; - } - - player.closeInventory(); - Session.create(session -> new HardcoreGenerator(session), null, null, player); - } - - // ── HardcoreGenerator ───────────────────────────────────────────────────── - - private static class HardcoreGenerator extends ParkourGenerator { - - private final boolean resetRewards; - - public HardcoreGenerator(@NotNull Session session) { - super(session); - this.resetRewards = Config.CONFIG.isPath("modes.hardcore.reset-rewards") - ? Config.CONFIG.getBoolean("modes.hardcore.reset-rewards") : true; - } - - @Override - public void fall() { - if (resetRewards) { - for (ParkourPlayer pp : getPlayers()) { - pp.collectedRewards.clear(); - } - } - - super.fall(); - } - - @Override - public Mode getMode() { - return Modes.HARDCORE; - } - } -} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index bc179ff..954f2ac 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -208,11 +208,8 @@ modes: amplifier: 1 duration: 5 - hardcore: - reset-rewards: true - elytra: - enabled: true + enabled: false ring-distance-min: 20 ring-distance-max: 40 ring-size: 5 diff --git a/src/main/resources/locales/en.yml b/src/main/resources/locales/en.yml index 9f80e23..53a4372 100644 --- a/src/main/resources/locales/en.yml +++ b/src/main/resources/locales/en.yml @@ -40,11 +40,6 @@ play: name: "�D4FF&lGravity Shift" lore: "񢩈Random potion effects||񢩈every few jumps" - hardcore: - material: "skeleton_skull" - name: "�D4FF&lHardcore" - lore: "񢩈Lose all rewards on fall||񢩈No second chances." - spectator: name: "&#FFFFFFSpectator" diff --git a/src/main/resources/locales/ru.yml b/src/main/resources/locales/ru.yml index 0b12185..db4071b 100644 --- a/src/main/resources/locales/ru.yml +++ b/src/main/resources/locales/ru.yml @@ -39,11 +39,6 @@ play: name: "�D4FF&lГравитационный сдвиг" lore: "񢩈Случайные эффекты зелий||񢩈каждые несколько прыжков" - hardcore: - material: "skeleton_skull" - name: "�D4FF&lХардкор" - lore: "񢩈Потеря всех наград при падении||񢩈Второго шанса не будет." - spectator: name: "&#FFFFFFНаблюдатель"