feat: Enhance minigame effects and UI interactions
- Implemented LeaveMinigameQueueEffect to handle player removal from queues and runtime. - Added OpenMinigameQueueUIEffect to manage the opening of the minigame queue UI. - Introduced StartPlayerTimerEffect and StopPlayerTimerEffect for managing player timers and scoring. - Enhanced PlaceBlocksEffect with options for filling volumes and hollow placements. - Updated SpawnItemEffect to fix codec handling for item IDs. - Added MountPlayerEffect and DismountPlayerEffect for player interactions with NPCs. - Improved StartQueuedMinigameEffect to check player counts before starting games. - Added localization entries for new effects and UI elements. - Updated manifest to include HyUI as a dependency.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# King of the Hill — Overview
|
||||
|
||||
In this tutorial you will build a **King of the Hill** minigame with three rounds. Each round has a dedicated capture point in a different part of the arena. Players earn one point per second while standing on the active hill. The player with the most points after all three rounds wins.
|
||||
|
||||
## What You Will Build
|
||||
|
||||
| Volume / File | Purpose |
|
||||
|---|---|
|
||||
| `King_Of_The_Hill.json` | Minigame definition — rounds, timing, HUD, PvP rules |
|
||||
| Queue pad | Players step on it to join the waiting session |
|
||||
| Start button | Game master trigger to launch the game and set phase to ACTIVE |
|
||||
| Arena boundary | Map discovery tag — tells the runtime which map it is running on |
|
||||
| Spawn zone | Where players enter to activate and start playing |
|
||||
| Capture Zone A | Active during Round 1 — scores +1/second while standing inside |
|
||||
| Capture Zone B | Active during Round 2 — scores +1/second while standing inside |
|
||||
| Capture Zone C | Active during Round 3 — scores +1/second while standing inside |
|
||||
| Round 2 / Round 3 signal volumes | Announce the new round when it starts |
|
||||
| End-game timer volumes | Detect when Round 3 finishes and end the game |
|
||||
|
||||
## Game Flow
|
||||
|
||||
```
|
||||
1. Players step on the queue pad.
|
||||
2. Game master triggers the start button (or add auto-start logic).
|
||||
3. Runtime starts → phase is set to ACTIVE.
|
||||
4. Players enter the spawn zone → they become ACTIVE (HUD shown, items given).
|
||||
5. Round 1 runs for 120 seconds. Capture Zone A scores.
|
||||
6. Round 1 ends → Round 2 begins. Capture Zone B scores.
|
||||
7. Round 2 ends → Round 3 begins. Capture Zone C scores.
|
||||
8. Round 3 ends → game_end timer fires → EndMinigame.
|
||||
9. Scores are tallied. Highest score wins. Arena resets.
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Hytale server with core-minigames installed.
|
||||
- A world with three distinct hilltop areas for your capture zones (A, B, C).
|
||||
- A lobby or staging area where players queue before the game begins.
|
||||
- Basic familiarity with placing trigger volumes and adding conditions/effects in the editor.
|
||||
|
||||
## Files in This Tutorial
|
||||
|
||||
| File | Contents |
|
||||
|---|---|
|
||||
| `02-minigame-definition.md` | JSON definition with all fields explained |
|
||||
| `03-join-and-start.md` | Queue pad, start button, map boundary |
|
||||
| `04-spawn-and-activation.md` | Spawn zone + player activation |
|
||||
| `05-capture-zones.md` | The three scoring capture zones |
|
||||
| `06-round-signals.md` | Round announcements and end-game timer |
|
||||
| `07-end-and-reset.md` | Ending the game and resetting the arena |
|
||||
| `08-full-example-files.md` | All complete configurations in one place |
|
||||
@@ -0,0 +1,60 @@
|
||||
# King of the Hill — Minigame Definition
|
||||
|
||||
Create the following file in your server's assets:
|
||||
|
||||
```
|
||||
src/main/resources/Server/Minigames/King_Of_The_Hill.json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Id": "King_Of_The_Hill",
|
||||
"Name": "King of the Hill",
|
||||
"Enabled": true,
|
||||
"MinPlayers": 2,
|
||||
"MaxPlayers": 16,
|
||||
"TeamMode": "SOLO",
|
||||
"WinCondition": "HIGHEST_SCORE",
|
||||
"RoundLengthSeconds": 120,
|
||||
"TotalRounds": 3,
|
||||
"CountdownSeconds": 10,
|
||||
"AllowPvp": true,
|
||||
"RequiredGameMode": "Adventure",
|
||||
"ShowHud": true,
|
||||
"ShowScore": true,
|
||||
"ShowTime": true,
|
||||
"ShowRound": true
|
||||
}
|
||||
```
|
||||
|
||||
## Field Breakdown
|
||||
|
||||
| Field | Value | Why |
|
||||
|---|---|---|
|
||||
| `Id` | `King_Of_The_Hill` | Must match the filename exactly and all `MinigameId` fields on every trigger volume in the world. |
|
||||
| `MinPlayers` | `2` | Queue will not start a countdown until at least 2 players have joined. Set to `1` for solo testing. |
|
||||
| `TeamMode` | `SOLO` | Each player competes individually with their own score. |
|
||||
| `WinCondition` | `HIGHEST_SCORE` | The player with the most points when the game ends wins. |
|
||||
| `RoundLengthSeconds` | `120` | Each hill is active for 2 minutes before the next round begins. |
|
||||
| `TotalRounds` | `3` | Three rounds run in sequence. The runtime advances rounds automatically when each timer expires. |
|
||||
| `CountdownSeconds` | `10` | After MinPlayers has joined the queue, a 10-second countdown begins before the game starts. |
|
||||
| `AllowPvp` | `true` | Players can knock each other off the hill. Set to `false` for a passive score-race version. |
|
||||
| `RequiredGameMode` | `Adventure` | Applied to each player when they activate. Prevents block breaking and placing. |
|
||||
| `ShowHud` | `true` | Enables the in-game HUD. Without this, no HUD appears even if the other Show* fields are set. |
|
||||
| `ShowScore` | `true` | Shows the player's current score on the HUD. |
|
||||
| `ShowTime` | `true` | Shows time remaining in the current round on the HUD. Only renders when `RoundLengthSeconds > 0`. |
|
||||
| `ShowRound` | `true` | Shows current round out of total rounds (e.g. `2 / 3`). Only renders when `TotalRounds > 1`. |
|
||||
|
||||
## How Rounds Work
|
||||
|
||||
You do not need to manually advance rounds. The runtime handles this automatically:
|
||||
|
||||
1. When the game starts, the `$round` timer begins counting down `RoundLengthSeconds` for round 1.
|
||||
2. When the timer expires, round 2 starts and the timer resets.
|
||||
3. When round 3's timer expires, `on_game_rounds_complete` is dispatched.
|
||||
|
||||
Your trigger volumes use the `CurrentRound` condition to check which round is active, so each capture zone automatically becomes active or inactive as rounds change.
|
||||
|
||||
## Testing With One Player
|
||||
|
||||
Change `MinPlayers` to `1` while setting up and testing your world. Change it back before going live.
|
||||
@@ -0,0 +1,86 @@
|
||||
# King of the Hill — Join Pad & Auto-Start
|
||||
|
||||
This step sets up the volumes that let players join the queue and auto-start the game once enough players are ready.
|
||||
|
||||
---
|
||||
|
||||
## 1. Arena Boundary Volume
|
||||
|
||||
This volume is not interactive — it exists purely so the runtime knows which map it is running on. Place it as a large box that encompasses the entire arena.
|
||||
|
||||
**Volume type:** Any (Enter trigger is fine, no effects needed)
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` *(replace with your map name)* |
|
||||
| `MapRole` | `MainArena` |
|
||||
|
||||
No conditions. No effects.
|
||||
|
||||
> The `MapId` value is arbitrary — pick a name that describes your arena (e.g. `Hillside`, `Volcano`, `Castle`). Use the same value on every other volume in this world that belongs to this map.
|
||||
|
||||
---
|
||||
|
||||
## 2. Queue Pad
|
||||
|
||||
Place this volume at a lobby pad where players gather before the game. When stepped on, it adds the player to the queue and attempts to start the game. The start attempt is a no-op until `MinPlayers` (2) have joined, at which point the runtime is created automatically.
|
||||
|
||||
**Volume type:** Enter trigger
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `JoinMinigameQueue` | `MinigameId = King_Of_The_Hill` |
|
||||
| `StartQueuedMinigame` | `MinigameId = King_Of_The_Hill` |
|
||||
|
||||
> **How the auto-start works:** `StartQueuedMinigame` checks that no runtime is already running and that the queue has at least `MinPlayers` players before doing anything. With `MinPlayers = 2`: Player 1 steps on the pad — `StartQueuedMinigame` sees only 1 queued player and skips. Player 2 steps on the pad — queue now has 2 players, `StartQueuedMinigame` creates the runtime (phase = `WAITING_FOR_PLAYERS`). The signal volume below immediately advances it to `ACTIVE`.
|
||||
|
||||
Players who step on this pad after the game has already started are queued for the next game — `StartQueuedMinigame` detects the active runtime and skips.
|
||||
|
||||
---
|
||||
|
||||
## 3. STARTING Phase Volume
|
||||
|
||||
The system runs the `CountdownSeconds` countdown automatically during `WAITING_FOR_PLAYERS`. Once the countdown finishes, the runtime advances to `STARTING` and fires `SignalPhaseStart=STARTING`. Your volume responds by setting the phase to `ACTIVE` to begin the game.
|
||||
|
||||
**Volume type:** Any (Tag Added trigger — fired by the runtime, not physically entered)
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
| `SignalPhaseStart` | `STARTING` |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `SetMinigamePhase` | `MinigameId = King_Of_The_Hill`, `Phase = ACTIVE` |
|
||||
| `SetRound` | `Operation = Set`, `Amount = 1` |
|
||||
(anything else you want to happen? Effects, sounds, Starting cinematic? Do before setting to active and starting the round)
|
||||
|
||||
> **Countdown**: with `CountdownSeconds = 10` in the definition, a "Starting in N..." message is sent to all players each second. If a player steps off the queue pad (`LeaveMinigameQueue`) or disconnects during the countdown and the player count drops below `MinPlayers`, the countdown cancels and players are returned to the queue automatically.
|
||||
>
|
||||
> **Skip the countdown**: set `CountdownSeconds = 0` in the definition to go from `STARTING` to `ACTIVE` immediately.
|
||||
|
||||
---
|
||||
|
||||
## Leave Queue (Optional)
|
||||
|
||||
If players want to leave before the game starts, add a separate pad with:
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `LeaveMinigameQueue` | `MinigameId = King_Of_The_Hill` |
|
||||
@@ -0,0 +1,67 @@
|
||||
# King of the Hill — Spawn Zone & Player Activation
|
||||
|
||||
Players are not automatically active when the game starts. They must enter the spawn zone, which triggers the activation lifecycle: inventory save, item giving, game mode change, and HUD display.
|
||||
|
||||
---
|
||||
|
||||
## What Activation Does
|
||||
|
||||
When `SetPlayerStatus ACTIVE` fires for a player, the runtime:
|
||||
|
||||
1. Saves their current inventory (if `SavePlayerInventory = true`, the default).
|
||||
2. Gives them their start items or assigned loadout (if any defined in the definition).
|
||||
3. Applies `RequiredGameMode` (`Adventure` in this tutorial).
|
||||
4. Shows the HUD (since `ShowHud = true`).
|
||||
|
||||
This only happens once per player per game. If the same player re-enters the spawn zone it is harmless — the runtime ignores duplicate ACTIVE requests.
|
||||
|
||||
---
|
||||
|
||||
## Spawn Zone Volume
|
||||
|
||||
Place this volume at the area where players begin the round — typically a platform or small room at the edge of the arena that all players pass through when the game starts.
|
||||
|
||||
**Volume type:** Enter trigger
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
|
||||
**Conditions:**
|
||||
|
||||
| Condition | Fields | Purpose |
|
||||
|---|---|---|
|
||||
| `PlayerInMinigame` | `MinigameId = King_Of_The_Hill` | Only fires for players who are actually in the runtime (not random passersby). |
|
||||
| `MinigamePhase` | `MinigameId = King_Of_The_Hill`, `Phase = ACTIVE` | Prevents accidental activation while players are still in the lobby countdown. |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `SetPlayerStatus` | `MinigameId = King_Of_The_Hill`, `Status = ACTIVE` |
|
||||
| `SetSpawnPoint` | `MinigameId = King_Of_The_Hill`, `DestinationId = 100,64,200` *(replace with your spawn coordinates)* |
|
||||
|
||||
> The `SetSpawnPoint` stores a respawn destination for each player. When a player dies during the game, the runtime automatically respawns them at their stored checkpoint. Replace `100,64,200` with the actual world coordinates of your spawn area center. You can also use the format `world_name,X,Y,Z` if the spawn is in a different world.
|
||||
|
||||
---
|
||||
|
||||
## How Players Get to the Spawn Zone
|
||||
|
||||
In a typical KOTH setup, players are somewhere in the arena or lobby when the game starts. You have two options:
|
||||
|
||||
**Option A — Players walk there.** The spawn zone is accessible from the lobby area. When the game starts and phase becomes `ACTIVE`, players walk through the spawn zone on their way into the arena. This is the simplest approach and works well when the lobby and arena are adjacent.
|
||||
|
||||
**Option B — Teleport on game start.** Add a `SignalPhaseStart=ACTIVE` signal volume (covered in `06-round-signals.md`) with a `RespawnPlayer` effect. This teleports all active players to their spawn on phase change.
|
||||
|
||||
For this tutorial, Option A is assumed. If using Option B, `RespawnPlayer` resolves the destination using the player's stored checkpoint from `SetSpawnPoint`, so the activation volume still needs to fire first. Consider placing the spawn zone between the lobby exit and the arena entrance so it is always traversed.
|
||||
|
||||
---
|
||||
|
||||
## Respawn on Death
|
||||
|
||||
Because `SetSpawnPoint` is called at activation, player deaths during the game automatically respawn at those coordinates. No additional setup is required for basic respawning.
|
||||
|
||||
If you want players to respawn with a delay or only when a specific condition is met, add a `RespawnPlayer` effect to a separate signal or death-event volume.
|
||||
@@ -0,0 +1,147 @@
|
||||
# King of the Hill : Capture Zones
|
||||
|
||||
Each round has one active capture zone. Players standing inside the correct zone for the current round earn +1 point per second. Zones outside the active round do nothing.
|
||||
|
||||
You will create three separate trigger volumes : one per hilltop.
|
||||
|
||||
---
|
||||
|
||||
## How Scoring Works
|
||||
|
||||
Each capture zone uses:
|
||||
|
||||
- A **TICK** volume type so it fires continuously while players are standing inside it.
|
||||
- A `CurrentRound` condition that passes only when the correct round number is active.
|
||||
- A `ModifyPlayerScore` effect that adds 1 point per trigger fire.
|
||||
|
||||
The tick rate on the volume controls how often points are awarded. Set it to **1 second** in the editor so players earn 1 point per second. If the tick fires every 0.5 seconds, players would earn 2 points per second : adjust the amount or tick rate to your preference.
|
||||
leaving it at zero would be everytick its fired, Giving 30 points
|
||||
---
|
||||
|
||||
## Capture Zone A : Round 1
|
||||
|
||||
Place this volume on Hilltop A, the first round's scoring area. Size it so that players must be clearly on top of the hill.
|
||||
|
||||
**Volume type:** TICK trigger (tick interval: 1 second)
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
|
||||
**Conditions:**
|
||||
|
||||
| Condition | Fields | Purpose |
|
||||
|---|---|---|
|
||||
| `PlayerInMinigame` | `MinigameId = King_Of_The_Hill` | Only scores players in the runtime. |
|
||||
| `CurrentRound` | `MinigameId = King_Of_The_Hill`, `Compare = EQUAL`, `Round = 1` | Only active during Round 1. |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `ModifyPlayerScore` | `MinigameId = King_Of_The_Hill`, `Operation = ADD`, `Amount = 1` |
|
||||
|
||||
---
|
||||
|
||||
## Capture Zone B : Round 2
|
||||
|
||||
Place this volume on Hilltop B. Same setup as Zone A with only the round number changed.
|
||||
|
||||
**Volume type:** TICK trigger (tick interval: 1 second)
|
||||
|
||||
**Tags:** Same as Zone A (`MinigameId`, `MapId`)
|
||||
|
||||
**Conditions:**
|
||||
|
||||
| Condition | Fields |
|
||||
|---|---|
|
||||
| `PlayerInMinigame` | `MinigameId = King_Of_The_Hill` |
|
||||
| `CurrentRound` | `MinigameId = King_Of_The_Hill`, `Compare = EQUAL`, `Round = 2` |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `ModifyPlayerScore` | `MinigameId = King_Of_The_Hill`, `Operation = ADD`, `Amount = 1` |
|
||||
|
||||
---
|
||||
|
||||
## Capture Zone C : Round 3
|
||||
|
||||
Place this volume on Hilltop C.
|
||||
|
||||
**Volume type:** TICK trigger (tick interval: 1 second)
|
||||
|
||||
**Tags:** Same as above
|
||||
|
||||
**Conditions:**
|
||||
|
||||
| Condition | Fields |
|
||||
|---|---|
|
||||
| `PlayerInMinigame` | `MinigameId = King_Of_The_Hill` |
|
||||
| `CurrentRound` | `MinigameId = King_Of_The_Hill`, `Compare = EQUAL`, `Round = 3` |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `ModifyPlayerScore` | `MinigameId = King_Of_The_Hill`, `Operation = ADD`, `Amount = 1` |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
**You may want to add further effects the zones** :
|
||||
On Capture Zone 1 using `SignalRoundStart=1` volume tag (covered in `06-round-signals.md` and `tags.md`)
|
||||
you can add the following to play a sound at the start of the round and have a VFX with a duration of the round set
|
||||
|
||||
On Tag Added trigger
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `PlaySound` | `Your choice of sound`|
|
||||
| `Play VFX` | `Your choice of VFX` `Duration = round time`|
|
||||
|
||||
|
||||
|
||||
**Multiple players on the hill at once** : each player in the volume gets the `ModifyPlayerScore` effect independently. If two players stand on Hilltop A during Round 1, both earn +1/second. This is intentional for KOTH : fighting over the hill is part of the game.
|
||||
|
||||
**Zone size** : make your capture zones intentionally tight so there is real competition for the spot. A zone that is too large removes the "king of the hill" tension.
|
||||
|
||||
|
||||
**Dymanic Zone Size**
|
||||
Lets say you have rounds last 60 secounds. You want the Capture Zone for round 2 to get smaller half way.
|
||||
Create two Capture Zones one the full size and one the half size
|
||||
|
||||
Add on the large Capture Zone 2 using `SignalRoundStart=2` volume tag (covered in `06-round-signals.md` and `tags.md`)
|
||||
And another tag to each to later identify them
|
||||
`Round2Capture=Large`
|
||||
`Round2Capture=Small`
|
||||
|
||||
The larger Capture Zone
|
||||
On Tag Added trigger
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `DisableVolume` | `MatchTagKey=Round2Capture` `MatchTagValue=Small`|
|
||||
| `PlaySound` | `Your choice of sound`|
|
||||
| `Play VFX` | `Your choice of VFX` `Duration = 30`|
|
||||
| `EnableVolume` | `EffectDelay=30` `MatchTagKey=Round2Capture` `MatchTagValue=Small`|
|
||||
| `ModifyTag` | `Operation=Set` `TagKey=SmallRound2Capture` `TagValue=Enabled` `MatchTagKey=Round2Capture` `MatchTagValue=Small`|
|
||||
| `DisableVolume` | `MatchTagKey=Round2Capture` `MatchTagValue=Large`|
|
||||
|
||||
The Smaller Capture Zone
|
||||
On Tag Added trigger
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `PlaySound` | `Your choice of sound`|
|
||||
| `Play VFX` | `Your choice of VFX` `Duration = 30`|
|
||||
| `EnableVolume` | `EffectDelay=30` `MatchTagKey=Round2Capture` `MatchTagValue=Large`|
|
||||
| `ModifyTag` | `Operation=Remove` `TagKey=SmallRound2Capture` `TagValue=Enabled` `MatchTagKey=Round2Capture` `MatchTagValue=Small`|
|
||||
| `DisableVolume` | `MatchTagKey=Round2Capture` `MatchTagValue=Large`|
|
||||
|
||||
|
||||
There are a couple of ways to do this, but this is just one I felt like typing.
|
||||
|
||||
**Score display** : the HUD shows the current score in real time. Players can see how their score compares while standing on the hill. Round and time remaining are also shown because `ShowRound = true` and `ShowTime = true` are set in the definition.
|
||||
@@ -0,0 +1,112 @@
|
||||
# King of the Hill — Round Signals & Game End
|
||||
|
||||
This step wires up announcements for each new round and explains how the game ends automatically when the final round completes.
|
||||
|
||||
---
|
||||
|
||||
## How Signal Volumes Work
|
||||
|
||||
Signal volumes have special tags (`SignalRoundStart`, `SignalPhaseTick`, `SignalPhaseStart`, etc.) that the runtime writes to at the right moment. When the tag value changes, Hytale fires the volume's `TAG_ADDED` trigger, which runs the attached effects.
|
||||
|
||||
Every signal volume must also have `MinigameId` set so the runtime knows which game's signals to respond to.
|
||||
|
||||
---
|
||||
|
||||
## Round 2 Announcement
|
||||
|
||||
When Round 2 begins, fire a message to all players announcing the new hill location.
|
||||
|
||||
**Volume type:** Any (Tag Added trigger is conventional for signal volumes — the volume is never physically entered, it is fired by the runtime)
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
| `SignalRoundStart` | `2` |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `SendMinigameMessage` | `MinigameId = King_Of_The_Hill`, `MessageKey = round_2_start`, `Target = ALL` |
|
||||
|
||||
> `MessageKey` references a translation key in your server's language files. If you do not have a dedicated translations setup, you can skip `SendMinigameMessage` for now — the HUD already shows the updated round number automatically.
|
||||
|
||||
---
|
||||
|
||||
## Round 3 Announcement
|
||||
|
||||
Same structure, fires when Round 3 begins.
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
| `SignalRoundStart` | `3` |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `SendMinigameMessage` | `MinigameId = King_Of_The_Hill`, `MessageKey = round_3_start`, `Target = ALL` |
|
||||
|
||||
---
|
||||
|
||||
## How the Game Ends Automatically
|
||||
|
||||
When the Round 3 timer expires, the runtime calls `end()` automatically (because `TotalRounds = 3` and neither `OvertimeEnabled` nor `SuddenDeathEnabled` are set). No extra timer volume is needed.
|
||||
|
||||
The end sequence fires in this order:
|
||||
|
||||
1. `SignalPhaseStart=ENDING` fires — use this for a scoreboard display, player freeze, or end announcement.
|
||||
2. HUDs are removed, inventories restored, rewards distributed.
|
||||
3. `on_game_end` event fires (with `reason = "rounds_complete"`).
|
||||
4. Runtime is removed from the active list.
|
||||
5. `SignalPhaseStart=RESETTING` fires — use this to restore blocks, re-enable the queue pad, etc.
|
||||
6. `on_arena_reset` event fires.
|
||||
|
||||
You do not need a `StartTimer`/`SignalOnTimerExpire` pair to end the game.
|
||||
|
||||
---
|
||||
|
||||
## ENDING Phase Volume (Optional)
|
||||
|
||||
To show a final message or freeze players briefly at game end, respond to `SignalPhaseStart=ENDING`:
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
| `SignalPhaseStart` | `ENDING` |
|
||||
|
||||
**Example effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `SendMinigameMessage` | `MinigameId = King_Of_The_Hill`, `MessageKey = game_over`, `Target = ALL` |
|
||||
|
||||
---
|
||||
|
||||
## Round 1 Announcement (Optional)
|
||||
|
||||
Round 1 begins immediately when the game goes ACTIVE (there is no `SignalRoundStart=1` in the current round implementation for the initial round). If you want a Round 1 announcement, fire it from the `SignalPhaseStart=ACTIVE` volume set up in step 03:
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
| `SignalPhaseStart` | `ACTIVE` |
|
||||
|
||||
**Effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `SendMinigameMessage` | `MinigameId = King_Of_The_Hill`, `MessageKey = round_1_start`, `Target = ALL` |
|
||||
@@ -0,0 +1,87 @@
|
||||
# King of the Hill — Ending & Resetting
|
||||
|
||||
This step covers what happens when the game ends, what signals fire and when, and how to use arena reset to prepare for the next game.
|
||||
|
||||
---
|
||||
|
||||
## What Happens When the Game Ends
|
||||
|
||||
When the final round timer expires (and no overtime/sudden death is configured), the runtime calls `end("rounds_complete")` automatically. The full sequence:
|
||||
|
||||
1. **HUDs removed** for all players in the runtime.
|
||||
2. **Phase set to `ENDING`** → `SignalPhaseStart=ENDING` fires.
|
||||
3. **Active signal timers cancelled.**
|
||||
4. **Temporary entities cleaned up** (spawned NPCs/items tracked by the runtime).
|
||||
5. **Player inventories restored** (since `RestorePlayerInventory = true` is the default).
|
||||
6. **Rewards distributed** (if any `Rewards` are defined in the definition).
|
||||
7. **`on_game_end` event dispatched** with `reason = "rounds_complete"` and final scores.
|
||||
8. **Runtime state cleared** — scores, player status, timers, counters all zeroed.
|
||||
9. **Runtime removed** from the active list.
|
||||
10. **Phase set to `RESETTING`** → `SignalPhaseStart=RESETTING` fires.
|
||||
11. **`on_arena_reset` event dispatched** (since `ResetOnEnd = true` is the default).
|
||||
|
||||
You do not need to manually reset scores or clear HUDs.
|
||||
|
||||
---
|
||||
|
||||
## ENDING Phase Volume (Optional)
|
||||
|
||||
Use `SignalPhaseStart=ENDING` to show a scoreboard, announce the winner, or freeze players briefly before cleanup. This fires while the runtime still holds final scores.
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
| `SignalPhaseStart` | `ENDING` |
|
||||
|
||||
**Example effects:**
|
||||
|
||||
| Effect | Fields |
|
||||
|---|---|
|
||||
| `SendMinigameMessage` | `MinigameId = King_Of_The_Hill`, `MessageKey = game_over`, `Target = ALL` |
|
||||
|
||||
---
|
||||
|
||||
## Arena Reset Volume
|
||||
|
||||
Use `SignalPhaseStart=RESETTING` to restore any world state changed during the game. This fires after the runtime has been cleaned up, so it is safe to re-enable the queue pad without risk of double-triggering.
|
||||
|
||||
> **Scope note:** `SignalPhaseStart=RESETTING` is scoped by `MinigameId` tag match, NOT by physical containment within the `MapRole=MainArena` boundary. Any volume anywhere in the world with a matching `MinigameId` tag will respond. The arena boundary volume is only used for map *discovery*, not for event routing.
|
||||
|
||||
For this King of the Hill setup there are no block placements or NPC spawns, so the arena reset volume is optional. Add it if you want to re-enable the queue pad, reset visual markers, or restore barriers changed mid-game.
|
||||
|
||||
**Tags:**
|
||||
|
||||
| Tag | Value |
|
||||
|---|---|
|
||||
| `MinigameId` | `King_Of_The_Hill` |
|
||||
| `MapId` | `Hillside` |
|
||||
| `SignalPhaseStart` | `RESETTING` |
|
||||
|
||||
**Example effects:**
|
||||
|
||||
| Effect | Fields | Purpose |
|
||||
|---|---|---|
|
||||
| `PlaceBlocks` | *(coordinates of any changed areas, original block id)* | Restore modified terrain. |
|
||||
|
||||
---
|
||||
|
||||
## Repeating the Game
|
||||
|
||||
After the runtime ends and `SignalPhaseStart=RESETTING` fires, the minigame is ready to start again. Players can step back onto the queue pad and a new runtime will be created when `StartQueuedMinigame` fires again.
|
||||
|
||||
No state from the previous game persists — scores, player status, timers, and counters are all cleared by the end flow.
|
||||
|
||||
---
|
||||
|
||||
## Manual Reset
|
||||
|
||||
If you need to reset the game mid-match (for testing or crash recovery), use the admin command:
|
||||
|
||||
```
|
||||
/minigame reset King_Of_The_Hill
|
||||
```
|
||||
|
||||
This calls `ResetMinigame` directly, which sets phase to `RESETTING`, fires `SignalPhaseStart=RESETTING`, clears runtime state, and dispatches `on_arena_reset` — without going through the normal end flow (no rewards, no inventory restore, no `ENDING` phase).
|
||||
@@ -0,0 +1,135 @@
|
||||
# King of the Hill — Complete Reference
|
||||
|
||||
All volumes and the definition file in one place. Use this as a checklist when setting up your world.
|
||||
|
||||
---
|
||||
|
||||
## Definition File
|
||||
|
||||
**Path:** `src/main/resources/Server/Minigames/King_Of_The_Hill.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"Id": "King_Of_The_Hill",
|
||||
"Name": "King of the Hill",
|
||||
"Enabled": true,
|
||||
"MinPlayers": 2,
|
||||
"MaxPlayers": 16,
|
||||
"TeamMode": "SOLO",
|
||||
"WinCondition": "HIGHEST_SCORE",
|
||||
"RoundLengthSeconds": 120,
|
||||
"TotalRounds": 3,
|
||||
"CountdownSeconds": 10,
|
||||
"AllowPvp": true,
|
||||
"RequiredGameMode": "Adventure",
|
||||
"ShowHud": true,
|
||||
"ShowScore": true,
|
||||
"ShowTime": true,
|
||||
"ShowRound": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Volume Checklist
|
||||
|
||||
| # | Volume Name | Type | Tags | Conditions | Effects |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | Arena Boundary | Enter | `MinigameId=King_Of_The_Hill` `MapId=Hillside` `MapRole=MainArena` | — | — |
|
||||
| 2 | Queue Pad | Enter | `MinigameId=King_Of_The_Hill` | — | `JoinMinigameQueue` → `StartQueuedMinigame` |
|
||||
| 3 | Starting Signal | Tag Added | `MinigameId=King_Of_The_Hill` `MapId=Hillside` `SignalPhaseStart=STARTING` | — | `SetMinigamePhase ACTIVE` |
|
||||
| 4 | Spawn Zone | Enter | `MinigameId=King_Of_The_Hill` `MapId=Hillside` | `PlayerInMinigame` + `MinigamePhase=ACTIVE` | `SetPlayerStatus ACTIVE` → `SetSpawnPoint` |
|
||||
| 5 | Capture Zone A | TICK (1s) | `MinigameId=King_Of_The_Hill` `MapId=Hillside` | `PlayerInMinigame` + `CurrentRound=EQUAL 1` | `ModifyPlayerScore ADD 1` |
|
||||
| 6 | Capture Zone B | TICK (1s) | `MinigameId=King_Of_The_Hill` `MapId=Hillside` | `PlayerInMinigame` + `CurrentRound=EQUAL 2` | `ModifyPlayerScore ADD 1` |
|
||||
| 7 | Capture Zone C | TICK (1s) | `MinigameId=King_Of_The_Hill` `MapId=Hillside` | `PlayerInMinigame` + `CurrentRound=EQUAL 3` | `ModifyPlayerScore ADD 1` |
|
||||
| 8 | Round 2 Signal | Tag Added | `MinigameId=King_Of_The_Hill` `MapId=Hillside` `SignalRoundStart=2` | — | `SendMinigameMessage round_2_start ALL` |
|
||||
| 9 | Round 3 Signal | Tag Added | `MinigameId=King_Of_The_Hill` `MapId=Hillside` `SignalRoundStart=3` | — | `SendMinigameMessage round_3_start ALL` |
|
||||
| 10 | End Announcement | Tag Added | `MinigameId=King_Of_The_Hill` `MapId=Hillside` `SignalPhaseStart=ENDING` | — | `SendMinigameMessage game_over ALL` *(optional)* |
|
||||
| 11 | Arena Reset | Tag Added | `MinigameId=King_Of_The_Hill` `MapId=Hillside` `SignalPhaseStart=RESETTING` | — | *(block restore effects)* *(optional)* |
|
||||
|
||||
The game ends automatically when Round 3's timer expires — no end-game timer volume is needed.
|
||||
|
||||
---
|
||||
|
||||
## Volume Detail — Queue Pad (Auto-Start)
|
||||
|
||||
```
|
||||
Tags:
|
||||
MinigameId = King_Of_The_Hill
|
||||
|
||||
Effects:
|
||||
JoinMinigameQueue MinigameId=King_Of_The_Hill
|
||||
StartQueuedMinigame MinigameId=King_Of_The_Hill
|
||||
```
|
||||
|
||||
`StartQueuedMinigame` is a no-op until `MinPlayers` (2) are in the queue AND no runtime is already running.
|
||||
|
||||
---
|
||||
|
||||
## Volume Detail — Starting Signal
|
||||
|
||||
```
|
||||
Tags:
|
||||
MinigameId = King_Of_The_Hill
|
||||
MapId = Hillside
|
||||
SignalPhaseStart = STARTING
|
||||
|
||||
Effects:
|
||||
SetMinigamePhase MinigameId=King_Of_The_Hill Phase=ACTIVE
|
||||
```
|
||||
|
||||
Fires after the countdown completes. The system runs the `CountdownSeconds` countdown
|
||||
automatically during `WAITING_FOR_PLAYERS` — no timer volume required.
|
||||
|
||||
---
|
||||
|
||||
## Volume Detail — Spawn Zone
|
||||
|
||||
```
|
||||
Tags:
|
||||
MinigameId = King_Of_The_Hill
|
||||
MapId = Hillside
|
||||
|
||||
Conditions:
|
||||
PlayerInMinigame MinigameId=King_Of_The_Hill
|
||||
MinigamePhase MinigameId=King_Of_The_Hill Phase=ACTIVE
|
||||
|
||||
Effects:
|
||||
SetPlayerStatus MinigameId=King_Of_The_Hill Status=ACTIVE
|
||||
SetSpawnPoint MinigameId=King_Of_The_Hill DestinationId=<X,Y,Z>
|
||||
```
|
||||
|
||||
Replace `<X,Y,Z>` with the world coordinates of your spawn area.
|
||||
|
||||
---
|
||||
|
||||
## Volume Detail — Capture Zone A (copy pattern for B and C)
|
||||
|
||||
```
|
||||
Tags:
|
||||
MinigameId = King_Of_The_Hill
|
||||
MapId = Hillside
|
||||
|
||||
Conditions:
|
||||
PlayerInMinigame MinigameId=King_Of_The_Hill
|
||||
CurrentRound MinigameId=King_Of_The_Hill Compare=EQUAL Round=1
|
||||
|
||||
Effects:
|
||||
ModifyPlayerScore MinigameId=King_Of_The_Hill Operation=ADD Amount=1
|
||||
```
|
||||
|
||||
Change `Round=1` to `Round=2` and `Round=3` for Zones B and C.
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Symptom | Likely Cause |
|
||||
|---|---|
|
||||
| HUD never appears | `SetPlayerStatus ACTIVE` is not firing — check that the spawn zone has both `PlayerInMinigame` and `MinigamePhase=ACTIVE` conditions. |
|
||||
| Score never changes | Capture zone tick interval is not set, or the `CurrentRound` condition does not match the active round. Verify the `Round` value matches the round number (1, 2, or 3). |
|
||||
| Game never starts | Queue pad must have both `JoinMinigameQueue` AND `StartQueuedMinigame`. The starting signal volume must have `SignalPhaseStart=STARTING` with `SetMinigamePhase ACTIVE` effect. |
|
||||
| Game never ends | Rounds do not auto-end if `OvertimeEnabled` or `SuddenDeathEnabled` are set in the definition. Verify neither is enabled. |
|
||||
| Volume conditions blocked | Wrong `MinigameId` value — ensure every volume and every condition/effect field uses exactly `King_Of_The_Hill` (case-sensitive, underscores not spaces). |
|
||||
| Players not activating | Phase was never set to `ACTIVE` — verify the auto-start signal volume (`SignalPhaseStart=WAITING_FOR_PLAYERS`) is present and fires `SetMinigamePhase ACTIVE`. |
|
||||
| Game starts with 1 player | `MinPlayers` in the definition is set to `1`. Change to `2` if you need two players. |
|
||||
Reference in New Issue
Block a user