323 lines
17 KiB
Markdown
323 lines
17 KiB
Markdown
# SpeedHG
|
||
|
||
> **A Spigot / Paper plugin for the *SpeedHG* Hunger-Games variant by McScrims Network.**
|
||
> Built with Kotlin · Paper API 1.21 · Version `1.0.0`
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Overview](#overview)
|
||
- [Dependencies](#dependencies)
|
||
- [Game Flow](#game-flow)
|
||
- [Configuration](#configuration)
|
||
- [Kits](#kits)
|
||
- [Perks](#perks)
|
||
- [Commands](#commands)
|
||
- [Permissions](#permissions)
|
||
- [Architecture](#architecture)
|
||
- [Adding a New Kit](#adding-a-new-kit)
|
||
- [Adding a New Perk](#adding-a-new-perk)
|
||
- [Runtime Configuration via `SPEEDHG_CUSTOM_SETTINGS`](#runtime-configuration-via-speedhg_custom_settings)
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
SpeedHG is a competitive Hunger-Games game-mode where every player selects a **Kit** (with an Aggressive or Defensive playstyle) and up to **2 passive Perks** before the round starts. The world border steadily shrinks, disasters trigger, and a feast spawns mid-game to force confrontation.
|
||
|
||
Key features at a glance:
|
||
|
||
- 17 fully-implemented Kits, each with 2 Playstyles (Aggressive / Defensive)
|
||
- 14 passive Perks with persistent DB-backed selection
|
||
- Hit-charge system for active abilities (configurable hits-to-charge per kit)
|
||
- Dynamic WorldBorder shrink, Feast, and Pit events
|
||
- Anti-runner detection, soup-based healing, team support
|
||
- Lunar Client integration (waypoints, scoreboard)
|
||
- Discord webhook for game results
|
||
- MySQL-backed stats, ranking, and leaderboard
|
||
- Fully i18n-able via `en_US.yml` (MiniMessage format)
|
||
|
||
---
|
||
|
||
## Dependencies
|
||
|
||
| Dependency | Role |
|
||
|-----------------|-------------------------------------|
|
||
| `WorldEdit` | Arena construction for GladiatorKit |
|
||
| `Apollo-Bukkit` | Lunar Client integration |
|
||
| `Kup` | Internal rank/permission bridge |
|
||
|
||
All three must be present on the server; the plugin will not load without them.
|
||
|
||
---
|
||
|
||
## Game Flow
|
||
|
||
```
|
||
LOBBY → STARTING → INVINCIBILITY → INGAME → ENDING
|
||
```
|
||
|
||
| Phase | Description |
|
||
|-----------------|------------------------------------------------------------------------------------------|
|
||
| `LOBBY` | Waiting for `min-players`. Every 15 s an idle message is broadcast. |
|
||
| `STARTING` | Countdown (`lobby-time` seconds). Aborts if player count drops. |
|
||
| `INVINCIBILITY` | Players are teleported, kits & perks applied, Speed I + Haste I active. No damage dealt. |
|
||
| `INGAME` | Combat enabled, WorldBorder shrinks, timer counts up, Feast & Pit events fire. |
|
||
| `ENDING` | Winner announced, server shuts down after a short delay. |
|
||
|
||
Default timing values (overridable in `config.yml`):
|
||
|
||
| Setting | Default |
|
||
|--------------------|------------|
|
||
| Min players | 8 |
|
||
| Lobby time | 120 s |
|
||
| Invincibility time | 120 s |
|
||
| Border start | 600 blocks |
|
||
| Border end | 150 blocks |
|
||
| Border shrink time | 900 s |
|
||
|
||
---
|
||
|
||
## Configuration
|
||
|
||
### `config.yml`
|
||
|
||
```yaml
|
||
game:
|
||
beta: true
|
||
min-players: 2
|
||
lobby-time: 60
|
||
invincibility-time: 120
|
||
border-start: 600.0
|
||
border-end: 100.0
|
||
border-shrink-time: 900 # seconds
|
||
max-radius-teleport: 50.0
|
||
ranked: false
|
||
|
||
teams:
|
||
enabled: false
|
||
preset-count: 10
|
||
max-size: 2
|
||
|
||
anti-runner:
|
||
enabled: false
|
||
|
||
discord:
|
||
enabled: false
|
||
webhook-url: "YOUR_WEBHOOK_URL"
|
||
|
||
database:
|
||
host: "localhost"
|
||
port: 3306
|
||
name: "speedhg"
|
||
username: "speedhg_user"
|
||
password: "your_password"
|
||
use-ssl: false
|
||
pool:
|
||
max-size: 10
|
||
min-idle: 2
|
||
```
|
||
|
||
### `SPEEDHG_CUSTOM_SETTINGS` Environment Variable
|
||
|
||
For production overrides without touching `config.yml`, pass a JSON blob via environment variable (see [Runtime Configuration](#runtime-configuration-via-speedhg_custom_settings)).
|
||
|
||
---
|
||
|
||
## Kits
|
||
|
||
Players select a kit in the lobby and choose a **Playstyle** (Aggressive `[AGG]` / Defensive `[DEF]`).
|
||
Active abilities are triggered by right-clicking the kit item. Most abilities require a configurable number of hits to charge first (`global_hits_required`, default: 15).
|
||
|
||
| Kit | Aggressive Active | Defensive Active | Passive (AGG) | Passive (DEF) |
|
||
|------------------|----------------------------------------------------------------|---------------------------------------------------------|----------------------------------------------|-------------------------------------------------|
|
||
| **Anchor** | Summon Iron Golem anchor — no-knock + bonus damage in radius | ← same | Partial knockback resistance | Partial knockback resistance |
|
||
| **Armorer** | — | — | Strength I on kill | Protection I on armor |
|
||
| **Backup** | — | — | — | — |
|
||
| **BlackPanther** | Wakanda Leap — leap forward, hit enemies on land | ← same | — | — |
|
||
| **Blitzcrank** | Rocket Grab — pull nearest enemy to you | ← same | — | — |
|
||
| **Gladiator** | Sky arena 1v1 duel (glass cage, Wither IV after 180 s) | ← same | — | — |
|
||
| **Goblin** | Steal enemy kit for 60 s | Spawn a bunker for cover | — | — |
|
||
| **IceMage** | Ice Spike Burst — cone of freezing projectiles | Freeze Burst — 16 snowballs in circle | Speed in cold biomes, slowness chance on hit | 33 % slowness proc on being hit |
|
||
| **Ninja** | Vanish + backstab: teleport behind last-hit enemy | Shadow Step: teleport to nearest enemy | Last-hit tracking | — |
|
||
| **Puppet** | Puppet Trap — summon entity traps | ← similar | — | — |
|
||
| **Rattlesnake** | Pounce — sneak-charge then leap, applies Poison II | — | Poison II on pounce hit | 25 % reflect Poison II on being hit |
|
||
| **Spielo** | — | — | — | — |
|
||
| **Tesla** | Lightning strike AoE | ← same | — | Knock-back + ignite reflect on being hit |
|
||
| **TheWorld** | Shockwave + 3× Blink forward | Shockwave + Freeze nearby enemies (10 s, 5-hit cap) | — | Frozen enemies have a 5-hit break cap |
|
||
| **Trident** | Dive — launch up, lightning on landing (charge-based) | — | — | Parry — chance to bounce + Slowness on attacker |
|
||
| **Venom** | Venom Blast — Blindness I + Wither I + 2 hearts instant damage | Barrier — absorbs up to 15 damage, reflects projectiles | — | — |
|
||
| **Voodoo** | Curse — apply Glowing + delayed damage to nearby enemies | ← similar | 20 % Wither proc on hit | Speed + Regen while cursed enemies are nearby |
|
||
|
||
### Kit Item
|
||
|
||
Each kit provides its signature item in the player's inventory at game start. Items are tracked in `cachedItems` (UUID → `List<ItemStack>`) and cleaned up on `onRemove`.
|
||
|
||
---
|
||
|
||
## Perks
|
||
|
||
Players may equip **up to 2 Perks** before the round starts. Selections are persisted to the database per-player UUID.
|
||
|
||
| Perk | Effect |
|
||
|-------------------|-------------------------------------------------------------------------------------|
|
||
| **Adrenaline** | Dropping below 3 hearts → Speed II for 5 s (30 s cooldown) |
|
||
| **Berserker** | Below 4 hearts → deal 15 % more melee damage |
|
||
| **Bloodlust** | On kill: Speed I + Regen I for 5 s |
|
||
| **Enderblood** | Ender Pearl landings deal no fall damage |
|
||
| **Evasion** | 15 % chance to dodge incoming projectiles |
|
||
| **Featherweight** | Full immunity to fall damage |
|
||
| **Ghost** | Invisible to compass tracking and the Oracle perk |
|
||
| **Gourmet** | Consuming soup → Regen I + Speed I for 2 s |
|
||
| **Last Stand** | Taking damage below 3 hearts → Resistance II + Absorption I for 4 s (60 s cooldown) |
|
||
| **Momentum** | Sprint for 4 s without combat → Speed I |
|
||
| **Oracle** | Shows kit + distance to the nearest enemy on sneak / compass |
|
||
| **Pyromaniac** | Immune to fire, lava, magma blocks, and burn ticks |
|
||
| **Scavenger** | Every kill drops an extra Golden Apple at the corpse |
|
||
| **Vampire** | 10 % chance on melee hit to heal ½ heart |
|
||
|
||
---
|
||
|
||
## Commands
|
||
|
||
| Command | Alias | Permission | Description |
|
||
|--------------------------------------------|------------------|-------------------------|-----------------------------------|
|
||
| `/kit <name> <playstyle>` | — | — | Select a kit via command |
|
||
| `/kitinfo <id>` | `/perkinfo <id>` | — | Show name + lore of a kit or perk |
|
||
| `/perks` | — | — | Open the Perk Selector GUI |
|
||
| `/leaderboard` | — | — | View the top 10 players |
|
||
| `/timer <time>` | — | `speedhg.admin.timer` | Change the current game timer |
|
||
| `/ranking <toggle\|status\|rank [player]>` | — | `speedhg.admin.ranking` | Manage the ranking system |
|
||
| `/help` | — | — | Show the help screen |
|
||
|
||
---
|
||
|
||
## Permissions
|
||
|
||
| Node | Default | Description |
|
||
|-------------------------|---------|--------------------------------|
|
||
| `speedhg.bypass` | `false` | Join while the game is running |
|
||
| `speedhg.admin.timer` | `op` | Change the game timer |
|
||
| `speedhg.admin.ranking` | `op` | Manage ranking |
|
||
| `speedhg.admin.staff` | `false` | Staff flag for Lunar Client |
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
```
|
||
SpeedHG (main class)
|
||
├── GameManager – state machine, game loop, compass, win check
|
||
├── KitManager – kit registry, player selections, hit-charge state
|
||
├── PerkManager – perk registry, player selections, DB persistence
|
||
├── KitEventDispatcher – single Listener delegating all kit events
|
||
├── PerkEventDispatcher – single Listener delegating all perk events
|
||
├── ScoreboardManager – sidebar scoreboard
|
||
├── TablistManager – tab list (Lunar Client / Volcano rank)
|
||
├── AntiRunningManager – cave-aware anti-runner detection
|
||
├── DisasterManager – periodic in-game disasters
|
||
├── FeastManager – mid-game feast event
|
||
├── PitManager – pit event
|
||
├── RankingManager – ELO / rank logic
|
||
├── StatsManager – kill/death/win tracking (MySQL)
|
||
├── DatabaseManager – HikariCP connection pool
|
||
├── LanguageManager – MiniMessage i18n
|
||
├── LunarClientManager – Lunar Client waypoints, scoreboard
|
||
├── DiscordWebhookManager– post-game Discord messages
|
||
├── WorldManager – map archive extraction & world loading
|
||
├── DataPackManager – biome overrides via datapack
|
||
└── CustomGameManager – SPEEDHG_CUSTOM_SETTINGS JSON parsing
|
||
```
|
||
|
||
### Thread Safety
|
||
|
||
All per-player state maps use `ConcurrentHashMap`. `BukkitTask` references are canceled and removed in every `onRemove` / `onDeactivate` hook.
|
||
|
||
### Kit Charge System
|
||
|
||
When a player's kit is applied, a `PlayerChargeData` object is stored in `KitManager`. Each confirmed melee hit (≥ 90 % attack strength) increments the charge counter. When it reaches `hitsRequired`, the state transitions to `READY` and the player receives an ActionBar notification. Hitting right-click with the ability item triggers `ActiveAbility.execute()` and resets the charge.
|
||
|
||
`hitsRequired` priority: **kit-specific override** > **global_hits_required** > **hardcoded default**.
|
||
Kits that use an internal cooldown instead (e.g. TridentKit Dive) set `hardcodedHitsRequired = 0`.
|
||
|
||
---
|
||
|
||
## Adding a New Kit
|
||
|
||
1. Create `src/main/kotlin/.../kit/impl/YourKit.kt` extending `Kit()`.
|
||
2. Implement all abstract members (`id`, `displayName`, `lore`, `icon`, `cachedItems`, `giveItems`, `getActiveAbility`, `getPassiveAbility`).
|
||
3. Create `private inner class` for each ability (Active & Passive, for each Playstyle).
|
||
4. Put magic numbers in a `companion object` with `UPPER_SNAKE_CASE` constants.
|
||
5. Wire configurable values through `override().getLong("key") ?: DEFAULT_*`.
|
||
6. Add language keys under `kits.<id>.*` in `src/main/resources/languages/en_US.yml`.
|
||
7. Register in `SpeedHG.registerKits()`:
|
||
```kotlin
|
||
kitManager.registerKit( YourKit() )
|
||
```
|
||
|
||
---
|
||
|
||
## Adding a New Perk
|
||
|
||
1. Create `src/main/kotlin/.../perk/impl/YourPerk.kt` extending `Perk()`.
|
||
2. Implement `id`, `displayName`, `lore`, `icon`.
|
||
3. Override only the event hooks you need (`onActivate`, `onDeactivate`, `onHitEnemy`, etc.).
|
||
4. Clean up all state in `onDeactivate`.
|
||
5. Add language keys under `perks.<id>.*` in `en_US.yml`.
|
||
6. Register in `SpeedHG.registerPerks()`:
|
||
```kotlin
|
||
perkManager.registerPerk( YourPerk() )
|
||
```
|
||
|
||
---
|
||
|
||
## Runtime Configuration via `SPEEDHG_CUSTOM_SETTINGS`
|
||
|
||
Pass a JSON blob as an environment variable to override game and kit settings without redeploying:
|
||
|
||
```json
|
||
{
|
||
"game": {
|
||
"min_players": 4,
|
||
"lobby_time": 45,
|
||
"invincibility_time": 60,
|
||
"border_start": 500.0,
|
||
"border_end": 50.0,
|
||
"border_shrink_time": 720
|
||
},
|
||
"kits": {
|
||
"global_hits_required": 12,
|
||
"kits": {
|
||
"ninja": {
|
||
"hits_required": 10,
|
||
"extras": {
|
||
"cooldown_ms": 8000,
|
||
"backstab_damage": 6.0
|
||
}
|
||
},
|
||
"gladiator": {
|
||
"arena_radius": 11,
|
||
"arena_height": 7,
|
||
"wither_after_seconds": 180
|
||
},
|
||
"trident": {
|
||
"extras": {
|
||
"max_dive_charges": 3,
|
||
"sequence_cooldown_ms": 12000,
|
||
"lightning_radius": 4.0,
|
||
"lightning_damage": 4.0,
|
||
"parry_chance": 0.35,
|
||
"parry_slowness_ticks": 60
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Kit-level `extras` keys are free-form strings resolved at runtime via `KitOverride.getLong()`, `.getInt()`, and `.getDouble()` with the kit's own hardcoded defaults as fallback.
|
||
|
||
---
|
||
|
||
*McScrims Network · play.mcscrims.club* |