First commit

This commit is contained in:
2026-06-07 14:34:30 -07:00
commit 8eb6e563c5
130 changed files with 8412 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
# Architecture
## Overview
core-minigames extends Hytale's existing asset, trigger-volume, and event systems. It does not run a separate minigame logic registry beside Hytale's trigger-volume registry.
```text
Server/Minigames/*.json
|
v
MinigameDefinition asset store
|
v
MinigameRuntimeService
|
+--> Hytale event bus: MinigameEvent
|
+--> TriggerEffect.CODEC entries
|
`--> TriggerCondition.CODEC entries
```
## Asset Store
`MinigameDefinition` is registered as a Hytale asset store in `MinigameCorePlugin.setup()`.
Important details:
- Path: `Server/Minigames/`
- Codec: `MinigameDefinition.CODEC`
- Key function: `MinigameDefinition::getId`
- Editor dropdown source: `MinigameDefinition`
The trigger-volume editor can use this asset source for fields named `MinigameId`, so minigame trigger effects and conditions can pick from loaded minigame assets instead of free-typing IDs.
## Runtime
`DefaultMinigameRuntimeService` owns active `MinigameRuntime` instances keyed by runtime ID. A single minigame ID can have multiple active runtimes when multiple discovered maps or variants are started.
Runtime state includes:
- Current `MinigamePhase`
- Player state, score, lives, team, tags, and status
- Team membership and team score
- Objective state
- Counter and variable state
- Spectator and eliminated-player sets
- Runtime ID, map ID, and variant ID
`DefaultMinigameService` handles definition-level actions and delegates live actions to the runtime service.
## Events
`MinigameEvent` implements Hytale's `IEvent<String>`.
Runtime events dispatch through:
```java
HytaleServer.get()
.getEventBus()
.dispatchFor(MinigameEvent.class, event.minigameId())
.dispatch(event);
```
This keeps minigame events available to Hytale's event system instead of routing them through a custom `EventRegistry`.
## Trigger Conditions
Minigame trigger conditions extend Hytale's `TriggerCondition` and are registered directly into `TriggerCondition.CODEC`.
Current condition type IDs:
- `MinigamePhase`
- `PlayerInMinigame`
- `PlayerStatus`
- `PlayerScore`
- `TeamScore`
- `Counter`
- `CurrentRound`
- `PlayerLives`
- `TeamCondition`
- `TimerElapsed`
- `ObjectiveStatus`
- `WinConditionMet`
## Trigger Effects
Minigame trigger effects extend Hytale's `TriggerEffect` and are registered directly into `TriggerEffect.CODEC`.
Current effect type IDs:
- `StartMinigame`
- `EndMinigame`
- `ResetMinigame`
- `SetMinigamePhase`
- `ResetScores`
- `ModifyPlayerScore`
- `ModifyTeamScore`
- `ModifyCounter`
- `ModifyPlayerLives`
- `SetPlayerStatus`
- `JoinMinigameQueue`
- `LeaveMinigameQueue`
- `VoteForMap`
- `StartQueuedMinigame`
- `SpawnItem`
- `SetRound`
- `StartTimer`
- `StopTimer`
- `SetSpawnPoint`
- `RespawnPlayer`
- `SendMinigameMessage`
- `AssignTeam`
- `SaveInventory`
- `RestoreInventory`
- `ClearInventory`
- `SetObjectiveStatus`
- `ModifyObjectiveProgress`
- `AwardKillScore`
## Volumes
There are no custom minigame volume classes after the refactor. Hytale trigger volumes provide the region shape, enter/exit behavior, tags, and trigger effect assets.
To define a minigame area, create a normal Hytale trigger volume and tag it with `MinigameId=<id>`, `MapId=<map>`, optional `VariantId=<variant>`, and optionally `MapRole=MainArena`. Then attach minigame trigger conditions and effects to that trigger volume or to reusable Hytale trigger effect assets.
## Removed Layer
The following old architecture pieces were removed because they duplicated Hytale systems:
- `BuiltinRegistries`
- `ConditionRegistry`
- `EventRegistry`
- `MinigameRegistries`
- `TemplateRegistry`
- `VolumeRegistry`
- `VolumeTypeDefinition`
Future minigame features should keep following the same pattern: use Hytale asset stores, Hytale trigger codec registration, and Hytale event dispatch unless there is a concrete engine gap that requires a local adapter.
+100
View File
@@ -0,0 +1,100 @@
# Commands Reference
All commands are subcommands of `/minigame`.
## Definition Management
### `/minigame list`
Lists registered minigames and basic runtime state.
### `/minigame create <id> <name>`
Creates a new minigame definition with default settings.
```text
/minigame create Beach_Race "Beach Race"
```
Definitions created by command start disabled. Enable them when the asset is configured.
### `/minigame info <id>`
Shows the loaded definition and runtime status for a minigame.
### `/minigame edit <id> <field> <value>`
Edits a single definition field and saves the definition.
```text
/minigame edit Beach_Race MaxPlayers 16
/minigame edit Beach_Race RoundLengthSeconds 600
```
### `/minigame enable <id>`
Sets `Enabled = true`.
### `/minigame disable <id>`
Sets `Enabled = false`. This prevents new starts but does not necessarily stop an already running runtime.
### `/minigame validate <id>`
Runs definition validation. Current validation focuses on definition fields. The old custom-volume requirement for `MinigameAreaVolume` no longer applies.
### `/minigame delete <id>`
Deletes the minigame definition.
### `/minigame export <id>`
Exports the current definition JSON.
### `/minigame import <file>`
Imports a definition from a JSON file path relative to the data directory.
## Runtime Management
### `/minigame start <id>`
Starts a queued runtime for the given minigame. The selected map comes from `MapSelectionMode`: highest vote for `VOTE`, random discovered map for `RANDOM`.
### `/minigame stop <id>`
Stops all active runtimes for the minigame ID. An optional greedy `reason` argument can override the default admin-stop reason.
### `/minigame reset <id>`
Resets all active runtimes for the minigame ID.
## Map Commands
### `/minigame maps <id>`
Lists maps discovered from trigger volumes in the sender's current world. Run this command as a player in the world containing the tagged volumes.
### `/minigame vote <id> <mapId>`
Casts or updates the sender's vote in the minigame waiting session.
## Player Commands
### `/minigame join <id> [player]`
Adds the command sender, or the optional player target, to the minigame waiting queue.
### `/minigame leave [player]`
Currently registered but not implemented. Use the `LeaveMinigameQueue` trigger effect for in-world queue leaving until the command is wired.
### `/minigame spectate <id>`
Currently registered but not implemented. The command validates that the minigame exists and has a running runtime, then returns a not-implemented message.
## Volume Commands
The old custom minigame volume system was removed. Use Hytale's trigger-volume editor for real volume placement and attach the minigame trigger effects and conditions there.
`/minigame volume create <type>` currently remains as a command stub for future Hytale trigger-volume integration. It should not be used as the primary way to define arenas.
+138
View File
@@ -0,0 +1,138 @@
# Events, Conditions & Effects
Minigame logic is implemented as Hytale-native trigger conditions and trigger effects. These are registered into `TriggerCondition.CODEC` and `TriggerEffect.CODEC`, so they appear beside Hytale's built-in trigger-volume logic.
## Events
`MinigameEvent` implements `IEvent<String>` and is dispatched through Hytale's event bus, keyed by minigame ID.
Current runtime events emitted by the service layer:
| Event ID | Fired When |
|----------|------------|
| `on_game_start` | A minigame runtime starts. |
| `on_game_end` | A minigame runtime ends. |
| `on_arena_reset` | A minigame runtime is reset. |
| `on_phase_change` | `SetMinigamePhase` changes the runtime phase. |
| `on_round_start` | `SetRound` starts a round, or the round timer advances to the next round. |
| `on_round_end` | The internal round timer expires. |
| `on_game_rounds_complete` | The final configured round ends. |
| `on_timer_expired` | A named signal timer expires. The internal `$round` timer is consumed by runtime round progression and is not re-dispatched. |
| `on_player_eliminated` | `SetPlayerStatus` sets `ELIMINATED`, or `ModifyPlayerLives` reduces lives to zero. |
| `on_objective_complete` | An objective status becomes `COMPLETE` or objective progress reaches completion. |
| `on_kill_score` | The kill scoring system awards points for a configured kill. |
The event payload is a string-object map. Runtime events include `runtime_id`, `minigame_id`, `map_id`, and `variant_id`. End events also include `reason`.
## Conditions
All minigame conditions share a `MinigameId` field. In the editor this is registered as an asset picker backed by `MinigameDefinition` assets.
| Type ID | Fields | Description |
|---------|--------|-------------|
| `MinigamePhase` | `MinigameId`, `Phase` | Passes when the runtime phase matches. |
| `PlayerInMinigame` | `MinigameId`, optional `PlayerId` | Passes when the player is in the runtime. If `PlayerId` is blank, the trigger context player is used. |
| `PlayerStatus` | `MinigameId`, optional `PlayerId`, `Status` | Passes when the player's minigame status matches. |
| `PlayerScore` | `MinigameId`, optional `PlayerId`, `Compare`, `Value` | Compares a player's score. |
| `TeamScore` | `MinigameId`, `TeamId`, `Compare`, `Value` | Compares a team's score. |
| `Counter` | `MinigameId`, `CounterId`, `Compare`, `Value` | Compares a named runtime counter. |
| `CurrentRound` | `MinigameId`, `Compare`, `Round` | Compares the current runtime round number. |
| `PlayerLives` | `MinigameId`, optional `PlayerId`, `Compare`, `Value` | Compares a player's remaining lives. |
| `TeamCondition` | `MinigameId`, optional `PlayerId`, `TeamId` | Passes when the player is assigned to the expected team. |
| `TimerElapsed` | `MinigameId`, `TimerName` | Passes when a named runtime timer exists and has elapsed. |
| `ObjectiveStatus` | `MinigameId`, `ObjectiveId`, `Status` | Passes when an objective has the expected status. |
| `WinConditionMet` | `MinigameId`, optional `TargetScore` | Evaluates the runtime definition's win condition for `LAST_ALIVE`, `FIRST_TO_SCORE`, and `OBJECTIVE_COMPLETE`. |
### Compare Values
`PlayerScore`, `TeamScore`, `Counter`, `CurrentRound`, and `PlayerLives` use the `NumberCompare` enum:
| Value | Meaning |
|-------|---------|
| `EQUAL` | Actual value equals `Value`. |
| `NOT_EQUAL` | Actual value does not equal `Value`. |
| `GREATER_THAN` | Actual value is greater than `Value`. |
| `GREATER_THAN_OR_EQUAL` | Actual value is greater than or equal to `Value`. |
| `LESS_THAN` | Actual value is less than `Value`. |
| `LESS_THAN_OR_EQUAL` | Actual value is less than or equal to `Value`. |
## Effects
All minigame effects that operate on a runtime include a `MinigameId` field. In the editor this is registered as an asset picker backed by `MinigameDefinition` assets.
| Type ID | Fields | Description |
|---------|--------|-------------|
| `StartMinigame` | `MinigameId` | Starts a minigame runtime using the triggering volume's map tags when present. |
| `JoinMinigameQueue` | `MinigameId` | Adds the triggering player to the minigame waiting session. |
| `LeaveMinigameQueue` | `MinigameId` | Removes the triggering player from the minigame waiting session and clears their vote. |
| `VoteForMap` | `MinigameId`, optional `MapId` | Votes for a map in the waiting session. If `MapId` is blank, the triggering volume's `MapId` tag is used. |
| `StartQueuedMinigame` | `MinigameId` | Starts a queued match using `MapSelectionMode` and discovered map tags. |
| `SpawnItem` | optional `MinigameId`, `ItemId`, optional `Amount`, `RespawnDelaySeconds`, `SpawnKey`, `OffsetX`, `OffsetY`, `OffsetZ` | Spawns an item entity on the ground at the trigger volume position. When attached to a `TICK` volume, it respawns after the previous item is picked up or despawns and the delay has elapsed. |
| `AwardKillScore` | optional `MinigameId`, `TargetIds`, `Points`, `AwardTarget` | Configures kill scoring for deaths inside this volume. `TargetIds` accepts `Player`, NPC role ids such as `fox`, or `*`. `AwardTarget` is `PLAYER`, `TEAM`, or `BOTH`. |
| `EndMinigame` | `MinigameId`, optional `Reason` | Ends the selected minigame runtime. |
| `ResetMinigame` | `MinigameId` | Resets the selected minigame runtime. |
| `SetMinigamePhase` | `MinigameId`, `Phase` | Sets the runtime phase directly. |
| `ResetScores` | `MinigameId` | Resets all player and team scores for the runtime. |
| `ModifyPlayerScore` | `MinigameId`, optional `PlayerId`, `Operation`, `Amount` | Sets, adds, or subtracts a player's score. |
| `ModifyTeamScore` | `MinigameId`, `TeamId`, `Operation`, `Amount` | Sets, adds, or subtracts a team's score. |
| `ModifyCounter` | `MinigameId`, `CounterId`, `Operation`, `Amount` | Sets, adds, or subtracts a named runtime counter. |
| `ModifyPlayerLives` | `MinigameId`, optional `PlayerId`, `Operation`, `Amount` | Sets, adds, or subtracts a player's remaining lives. |
| `SetPlayerStatus` | `MinigameId`, optional `PlayerId`, `Status` | Sets a player's minigame status. |
| `SetRound` | `MinigameId`, `Operation`, `Amount` | Sets, adds, or subtracts the runtime round number and dispatches `on_round_start`. |
| `StartTimer` | `MinigameId`, `TimerName`, `DurationSeconds` | Starts a named runtime timer and signal timer. |
| `StopTimer` | `MinigameId`, `TimerName` | Stops a named runtime timer and cancels the signal timer. |
| `SetSpawnPoint` | `MinigameId`, optional `PlayerId`, `DestinationId` | Stores a player's respawn destination/checkpoint. |
| `RespawnPlayer` | `MinigameId`, optional `PlayerId`, optional `DestinationId` | Respawns a player using explicit destination, checkpoint, then team spawn fallback. |
| `SendMinigameMessage` | `MinigameId`, `MessageKey`, `Target` | Sends a translated message to `PLAYER`, `TEAM`, or `ALL`. |
| `AssignTeam` | `MinigameId`, optional `PlayerId`, `TeamId` | Assigns a player to a team in the runtime. |
| `SaveInventory` | `MinigameId`, optional `PlayerId` | Saves the player's current inventory into runtime state. |
| `RestoreInventory` | `MinigameId`, optional `PlayerId` | Restores the player's saved runtime inventory. |
| `ClearInventory` | `MinigameId`, optional `PlayerId` | Clears the player's inventory. |
| `SetObjectiveStatus` | `MinigameId`, `ObjectiveId`, `Status` | Creates or updates an objective status and dispatches `on_objective_complete` when complete. |
| `ModifyObjectiveProgress` | `MinigameId`, `ObjectiveId`, `Operation`, `Amount` | Sets, adds, or subtracts objective progress and completes an active objective when progress reaches the required threshold. |
### Operation Values
Score, counter, and lives mutation effects use:
| Value | Meaning |
|-------|---------|
| `SET` | Replace the current value with `Amount`. |
| `ADD` | Add `Amount` to the current value. |
| `SUBTRACT` | Subtract `Amount` from the current value. |
## Player Resolution
Several conditions and effects allow `PlayerId` to be omitted. When it is blank, the implementation attempts to resolve the triggering player from Hytale's trigger context. This is the intended setup for normal enter/interact trigger volumes.
Use an explicit `PlayerId` only when the trigger is not naturally tied to the player who caused it.
## Respawn Resolution
`RespawnPlayer` resolves where to send the player in this order:
1. The effect's explicit `DestinationId`.
2. The player's stored checkpoint from `SetSpawnPoint`.
3. A random team spawn volume matching the player's `TeamId`.
Team spawn volumes are normal Hytale trigger volumes tagged with `MinigameId`, `MapId`, optional `VariantId`, `SpawnRole=TeamSpawn`, and `TeamId=<team id>`.
## Item Spawners
`SpawnItem` creates a normal Hytale item drop, not a direct inventory grant. Use it for health packs, ammo, weapons, armor, and other pickups placed in the arena.
For automatic respawns, attach it to a trigger volume that fires on `TICK`. The effect keeps one active item per volume plus `SpawnKey`; once that item reference is invalid, it waits `RespawnDelaySeconds` before spawning the next copy. If one volume hosts several item spawners, give each effect a different `SpawnKey`.
## Kill Scoring
`AwardKillScore` is configured on a normal trigger volume, but scoring is applied by the minigame death system rather than by volume enter/exit/tick. The volume must cover the area where deaths should count and should use the normal `MinigameId`, `MapId`, and optional `VariantId` tags for runtime routing.
Use `TargetIds = ["Player"]` for player kills, NPC role ids such as `fox` for specific NPC kills, or `["*"]` for any supported target. Friendly kills use `FriendlyFireKillScoreMode` on the `MinigameDefinition`.
## Runtime Resolution
Effects and conditions resolve a runtime in this order:
1. The triggering player's active runtime.
2. A runtime matching the triggering volume's `MinigameId`, `MapId`, and `VariantId` tags.
3. The configured `MinigameId` as a fallback.
+101
View File
@@ -0,0 +1,101 @@
# Examples
These examples describe the current Hytale-native setup: minigame definitions are assets, and world interaction is done with normal Hytale trigger volumes using the minigame trigger condition/effect types.
## Simple Start Pad
Definition file: `Server/Minigames/Quick_Brawl.json`
```json
{
"Id": "Quick_Brawl",
"Name": "Quick Brawl",
"Description": "A short solo score test.",
"Enabled": true,
"MinPlayers": 1,
"MaxPlayers": 8,
"TeamMode": "SOLO",
"WinCondition": "HIGHEST_SCORE",
"RoundLengthSeconds": 120,
"CountdownSeconds": 5,
"AllowPvp": true
}
```
Editor setup:
1. Create a normal Hytale trigger volume at the start pad.
2. Add the `StartMinigame` effect.
3. Set `MinigameId` to `Quick_Brawl` using the asset picker.
## Score Zone
Definition file: `Server/Minigames/Hill_Points.json`
```json
{
"Id": "Hill_Points",
"Name": "Hill Points",
"Enabled": true,
"MinPlayers": 1,
"MaxPlayers": 12,
"TeamMode": "SOLO",
"WinCondition": "FIRST_TO_SCORE",
"RoundLengthSeconds": 300
}
```
Editor setup:
1. Create a normal Hytale trigger volume around the scoring area.
2. Add `PlayerInMinigame` as a condition.
3. Add `ModifyPlayerScore` as an effect.
4. Set `MinigameId = Hill_Points`, `Operation = ADD`, and `Amount = 1`.
If the trigger context already contains the player who entered the volume, leave `PlayerId` blank.
## End When Score Reaches a Target
Condition:
- Type: `PlayerScore`
- `MinigameId = Hill_Points`
- `Compare = GREATER_THAN_OR_EQUAL`
- `Value = 100`
Effect:
- Type: `EndMinigame`
- `MinigameId = Hill_Points`
- `Reason = score_reached`
## Team Score Zone
Use `ModifyTeamScore` when the score belongs to a team instead of a player.
Required fields:
- `MinigameId`
- `TeamId`
- `Operation`
- `Amount`
The current implementation expects an explicit `TeamId`.
## Counter Gate
Increment:
- Effect: `ModifyCounter`
- `CounterId = captured_points`
- `Operation = ADD`
- `Amount = 1`
Gate:
- Condition: `Counter`
- `CounterId = captured_points`
- `Compare = GREATER_THAN_OR_EQUAL`
- `Value = 3`
Then attach a follow-up effect, such as `SetMinigamePhase` or `EndMinigame`.
+147
View File
@@ -0,0 +1,147 @@
# Minigame Definition Reference
Minigame definitions live in `src/main/resources/Server/Minigames/<Id>.json`. JSON keys use PascalCase. The filename should match the `Id` field.
## Identity
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `Id` | String | Yes | none | Unique asset ID. Use underscores instead of spaces. |
| `Name` | String | Yes | none | Display name shown in commands and UI. |
| `Description` | String | No | `""` | Short display description. |
| `Enabled` | Boolean | No | `true` | Whether the minigame can be started. |
| `Debug` | Boolean | No | `false` | Enables verbose minigame debug messages when supported by runtime logic. |
| `GameType` | Enum | No | `GENERIC` | `GENERIC`, `BATTLE`, `PUZZLE`, `RACE`, `CAPTURE`, or `SURVIVAL`. |
## World Binding
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `WorldId` | String | No | `""` | Optional world identifier. |
| `RequiredGameMode` | String | No | `"adventure"` | Game mode expected for players. |
## Players
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `MinPlayers` | Integer | No | `1` | Minimum player count. |
| `MaxPlayers` | Integer | No | `16` | Maximum player count. |
| `AllowJoinMidgame` | Boolean | No | `false` | Whether players can join after the game starts. |
| `AllowSpectators` | Boolean | No | `true` | Whether spectators are allowed. |
## Teams
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `TeamMode` | Enum | No | `SOLO` | `SOLO`, `TEAMS`, or `FREE_FOR_ALL`. |
| `TeamCount` | Integer | No | `0` | Number of teams when team mode is enabled. `0` means automatic. |
| `PlayersPerTeam` | Integer | No | `0` | Maximum players per team. `0` means no explicit limit. |
## Timing
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `RoundLengthSeconds` | Integer | No | `300` | Active round duration. |
| `TotalRounds` | Integer | No | `1` | Number of rounds to run. Values less than or equal to `0` allow open-ended round progression. |
| `CountdownSeconds` | Integer | No | `10` | Pre-game countdown duration. |
| `OvertimeEnabled` | Boolean | No | `false` | Enables overtime handling. |
| `SuddenDeathEnabled` | Boolean | No | `false` | Enables sudden-death handling. |
## Win Condition
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `WinCondition` | Enum | No | `HIGHEST_SCORE` | How the winner is determined. |
| `MapSelectionMode` | Enum | No | `VOTE` | How a waiting session chooses a discovered map: `VOTE` or `RANDOM`. |
| `FriendlyFireKillScoreMode` | Enum | No | `NO_POINTS` | How same-team player kills affect kill scoring: `OFF`, `LOSES_POINTS`, or `NO_POINTS`. |
Supported values:
| Value | Description |
|-------|-------------|
| `HIGHEST_SCORE` | Highest score at the end wins. |
| `FIRST_TO_SCORE` | First player or team to a target score wins. |
| `LAST_ALIVE` | Last active player or team wins. |
| `OBJECTIVE_COMPLETE` | Objective completion determines the winner. |
| `CUSTOM` | Trigger logic determines the winner. |
## World Rules
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `AllowPvp` | Boolean | No | `false` | Whether players can damage each other. |
| `AllowBlockBreaking` | Boolean | No | `false` | Whether blocks can be broken. |
| `AllowBlockPlacing` | Boolean | No | `false` | Whether blocks can be placed. |
## Inventory
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `SavePlayerInventory` | Boolean | No | `true` | Save player inventory on join. |
| `RestorePlayerInventory` | Boolean | No | `true` | Restore player inventory when the game ends. |
| `ResetOnEnd` | Boolean | No | `true` | Reset runtime state when the game ends. |
## Items
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `StartItems` | `ItemStackConfig[]` | No | `[]` | Items given to players at game start. |
| `Rewards` | `RewardConfig[]` | No | `[]` | Rewards distributed at game end. |
## ItemStackConfig
```json
{
"ItemId": "event_fishing_rod",
"Amount": 1
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `ItemId` | String | Yes | Hytale item ID. |
| `Amount` | Integer | Yes | Stack amount. |
## RewardConfig
```json
{
"Target": "winner",
"Items": [
{ "ItemId": "gold_coin", "Amount": 25 }
]
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `Target` | String | Yes | `winner`, `participant`, `team_winner`, or a team ID. |
| `Items` | `ItemStackConfig[]` | Yes | Items to grant. |
## Minimal Example
```json
{
"Id": "Quick_Brawl",
"Name": "Quick Brawl",
"Enabled": true,
"MinPlayers": 2,
"MaxPlayers": 8,
"TeamMode": "SOLO",
"WinCondition": "LAST_ALIVE",
"RoundLengthSeconds": 120,
"AllowPvp": true
}
```
## Validation
Current validation is definition-focused:
- `Name` must not be blank.
- `MinPlayers` must be greater than or equal to 1.
- `MaxPlayers` must be greater than or equal to `MinPlayers`.
- `RoundLengthSeconds` must be greater than 0.
- `CountdownSeconds` must be greater than or equal to 0.
The old custom `MinigameAreaVolume` validation requirement no longer applies. Minigame areas are normal Hytale trigger volumes configured in the editor.
+103
View File
@@ -0,0 +1,103 @@
# Trigger Volume Tags
core-minigames uses normal Hytale trigger-volume tags for map discovery, runtime routing, team spawns, kill scoring, and runtime signals. Tag keys and values are case-sensitive.
## Map And Runtime Tags
These tags identify which minigame map or variant a volume belongs to.
| Tag | Required | Values | Used By |
|-----|----------|--------|---------|
| `MinigameId` | Yes for discovered map volumes | A loaded `MinigameDefinition` ID, such as `KOTH` | Map discovery, runtime routing, queue start, kill scoring, respawns, signals |
| `MapId` | Yes for discovered map volumes | Map or arena ID, such as `Castle` | Map discovery, voting, runtime routing |
| `VariantId` | No | Variant ID, such as `Night` or `AltA` | Runtime routing and random variant selection |
| `MapRole` | No | `MainArena` | Marks the primary arena volume for a discovered map candidate |
Minimum map candidate:
```text
MinigameId=KOTH
MapId=Castle
MapRole=MainArena
```
Variant candidate:
```text
MinigameId=KOTH
MapId=Castle
VariantId=Night
MapRole=MainArena
```
Overlapping volumes are allowed. Volumes with the same `MinigameId`, `MapId`, and `VariantId` are grouped into one discovered map candidate.
## Spawn Tags
Team spawn volumes are normal trigger volumes used as teleport destinations by `RespawnPlayer`.
| Tag | Required | Values | Used By |
|-----|----------|--------|---------|
| `MinigameId` | Recommended | Minigame ID | Filters spawns to the current runtime's minigame |
| `MapId` | Recommended | Map ID | Filters spawns to the current runtime's map |
| `VariantId` | No | Variant ID | Filters spawns to the current runtime's variant when present |
| `SpawnRole` | Yes | `TeamSpawn` | Marks the volume as a team spawn candidate |
| `TeamId` | Yes | Team ID, such as `red` or `blue` | Selects spawns for the player's assigned team |
Example:
```text
MinigameId=KOTH
MapId=Castle
SpawnRole=TeamSpawn
TeamId=red
```
`RespawnPlayer` chooses a random matching team spawn when no explicit `DestinationId` and no player checkpoint are available.
## Signal Tags
Signal tags let runtime state changes fire normal Hytale trigger-volume logic by toggling a tag on matching volumes.
| Tag | Required | Values | Behavior |
|-----|----------|--------|----------|
| `SignalRoundStart` | No | Round number, such as `1` | Fires once when that round starts. |
| `SignalRoundTick` | No | Round number, such as `1` | Fires repeatedly while that round is active. |
| `SignalPhaseTick` | No | `MinigamePhase` enum name, such as `ACTIVE` | Fires repeatedly while the runtime is in that phase. |
| `SignalTickDelay` | No | Seconds, such as `1` or `0.5` | Delay between repeated signal fires. Minimum effective delay is 50ms. |
| `SignalOnTimerExpire` | No | Timer name from `StartTimer` | Fires once when the named timer expires. |
| `MinigameSignal` | Internal | `0` or `1` | Written by the runtime signal service to trigger Hytale `TAG_ADDED` behavior. Do not configure manually. |
Signal volumes must also have `MinigameId=<id>`. Timer names starting with `$` are internal runtime timers and do not fire trigger-volume signal tags.
Round-start example:
```text
MinigameId=KOTH
MapId=Castle
SignalRoundStart=1
```
Active-phase tick example:
```text
MinigameId=KOTH
MapId=Castle
SignalPhaseTick=ACTIVE
SignalTickDelay=1
```
Timer-expire example:
```text
MinigameId=KOTH
MapId=Castle
SignalOnTimerExpire=overtime_warning
```
## Validation Notes
- A volume with `MapId` but no `MinigameId` produces a warning and is ignored for map discovery.
- A discovered map candidate without `MapRole=MainArena` produces a warning but does not stop the minigame from loading.
- Missing map discovery is a warning, not a startup failure.
- Duplicate or overlapping variants are allowed when they intentionally represent alternate zones for the same map.
+122
View File
@@ -0,0 +1,122 @@
# Volumes
Minigame areas now use Hytale's built-in trigger volumes. The mod no longer defines custom volume types such as `MinigameAreaVolume`, `JoinQueueVolume`, or `ScoreVolume`.
This is intentional: the area, shape, enter/exit behavior, tags, and trigger-effect asset wiring belong to Hytale's trigger-volume system. core-minigames adds minigame-specific conditions and effects that can be attached to those trigger volumes.
## Defining Minigame Maps
1. Create a normal Hytale trigger volume in the editor.
2. Shape it around the arena or interaction area.
3. Tag it with `MinigameId=<MinigameId>` and `MapId=<MapId>`.
4. Add `VariantId=<VariantId>` when the same map can randomly use overlapping alternate zones.
5. Add `MapRole=MainArena` to the primary arena volume for each map or variant.
6. Attach trigger effects and conditions for the relevant minigame.
7. Use the `MinigameId` dropdown to select the `MinigameDefinition` asset.
There is no required root `MinigameAreaVolume` type. If validation needs an arena boundary later, it should inspect Hytale trigger-volume metadata instead of requiring a custom minigame volume class.
Map candidates are discovered from trigger-volume tags. See [Trigger Volume Tags](tags.md) for the full tag reference.
Overlapping volumes are valid. Runtime routing uses the selected `MinigameId`, `MapId`, and `VariantId`, so the same Hytale build can host multiple arena definitions.
## Common Patterns
### Start Zone
Use a normal trigger volume at an entrance, sign, NPC, or lobby pad.
Attach:
- Effect: `JoinMinigameQueue`
- Field: `MinigameId = <your minigame>`
For exits or cancel pads:
- Effect: `LeaveMinigameQueue`
- Field: `MinigameId = <your minigame>`
For voting buttons or pads:
- Effect: `VoteForMap`
- Field: `MapId = <map id>`, or leave blank to use the volume's `MapId` tag.
To start the queued match:
- Effect: `StartQueuedMinigame`
- Field: `MinigameId = <your minigame>`
Optionally guard it with:
- Condition: `MinigamePhase`
- Field: `Phase = WAITING_FOR_PLAYERS`
### Score Zone
Use a normal trigger volume around the scoring area.
Attach:
- Condition: `PlayerInMinigame`
- Effect: `ModifyPlayerScore`
- Fields: `Operation = ADD`, `Amount = <points>`
If the score is team-based, use `ModifyTeamScore` instead.
### Phase Gate
Use Hytale's normal volume behavior for the area and guard attached effects with:
- Condition: `MinigamePhase`
- Field: `Phase = ACTIVE`
### Team Spawns
Use normal trigger volumes as spawn markers. The volume position is the teleport destination.
Tag each team spawn volume with:
- `MinigameId=<your minigame>`
- `MapId=<map id>`
- optional `VariantId=<variant id>`
- `SpawnRole=TeamSpawn`
- `TeamId=<team id>`
`RespawnPlayer` resolves destinations in this order:
1. The effect's explicit `DestinationId`.
2. The player's stored checkpoint from `SetSpawnPoint`.
3. A random matching team spawn volume for the player's `TeamId`.
This means checkpoint-based games can still override spawns per player, while team arena games can rely on tagged team spawn volumes.
### Counters
Use a trigger volume to increment a named runtime counter:
- Effect: `ModifyCounter`
- Fields: `CounterId = <name>`, `Operation = ADD`, `Amount = 1`
Then use the `Counter` condition to branch once a threshold is reached.
### Kill Scoring
Use a trigger volume covering the arena or scoring zone and attach:
- Effect: `AwardKillScore`
- Fields: `TargetIds = ["Player"]` or NPC role ids such as `["fox"]`, `Points = <points>`, `AwardTarget = PLAYER`
The death system checks this volume when an entity dies inside it. It routes by the volume's `MinigameId`, `MapId`, and optional `VariantId` tags, so overlapping arenas can have different kill scoring rules.
### Pickup Spawners
Use a small trigger volume at the pickup location and attach:
- Effect: `SpawnItem`
- Fields: `ItemId = <item id>`, `Amount = <stack size>`, `RespawnDelaySeconds = <seconds>`
The item spawns at the volume position plus optional `OffsetX`, `OffsetY`, and `OffsetZ`. Attach the effect to a `TICK` trigger when the pickup should automatically respawn after being collected or despawning. If the same volume has multiple item spawners, set a different `SpawnKey` for each one.
## Reusable Effect Assets
For repeated logic, define a Hytale trigger effect asset and attach it to multiple trigger volumes. The minigame trigger condition/effect entries are normal codec types, so they can be reused the same way Hytale's built-in trigger-volume effects are reused.