Files
core-minigames/docs/events-conditions-effects.md
T
HeruEdhel ca11bbd4c0 Add new minigame volume templates and corresponding tests
- Introduced various minigame volume templates including map vote pad, NPC spawn, objective progress, player activation spawn, race, score zone, solo arena, team arena, team assignment pad, timed heal pack, and waiting area.
- Each template includes specific configurations for positions, shapes, effects, and tags relevant to their functionality.
- Implemented unit tests for minigame volume template export and spawn functionalities to ensure correct behavior and tag rewriting.
- Added tests for minigame map discovery service to validate player respawn logic based on team assignments.
- Enhanced minigame queue service tests to cover player join midgame scenarios.
- Created a data store test for saving and loading player defaults in the minigame template builder.
2026-06-21 13:04:16 -07:00

177 lines
18 KiB
Markdown

# 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 via `ResetMinigame`, or a game ends with `ResetOnEnd=true`. |
| `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_overtime_start` | The final round ends and `OvertimeEnabled=true`. Phase is set to `OVERTIME`. Fired before `on_game_rounds_complete`. |
| `on_sudden_death_start` | The final round ends and `SuddenDeathEnabled=true`. Phase is set to `SUDDEN_DEATH`. Fired before `on_game_rounds_complete`. |
| `on_game_rounds_complete` | The final configured round ends (after any overtime/sudden-death events). |
| `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 `ModifyLives` 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. Item and block fields are likewise registered as the editor's built-in pickers: `SpawnItem`'s `ItemId` uses the `Item` picker and `PlaceBlocks`'s `BlockId` uses the `BlockType` picker (the same menus the vanilla `GiveItem` / `PlaceBlock` effects use), so designers select from the live asset list instead of typing raw ids.
| 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`, optional `IgnoreRespawnTimer` | 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. When `IgnoreRespawnTimer` is `true`, it ignores whether the previous item still exists and spawns a fresh copy every `RespawnDelaySeconds` (or every tick when the delay is `0`). |
| `SpawnNpc` | optional `MinigameId`, `RoleId`, optional `Count`, `RespawnDelaySeconds`, `SpawnKey`, `OffsetX`, `OffsetY`, `OffsetZ`, `Yaw`, `Pitch`, `Roll`, `SpreadRadius` | Spawns one or more NPCs at the trigger volume position. Spawned NPCs are tracked as temporary runtime entities and can respawn after all active NPCs for the spawn key are gone. |
| `MountPlayer` | optional `MinigameId`, `PlayerId`, `NpcId`, `NpcRoleId`, `MaxDistance`, `AnchorX`, `AnchorY`, `AnchorZ`, `MovementConfig`, `EmptyRoleId`, optional `PreventManualDismount` | Mounts the triggering or configured player onto the nearest matching tracked NPC. When `PreventManualDismount` is `true`, the player cannot dismount manually until a `DismountPlayer` effect runs. |
| `DismountPlayer` | optional `MinigameId`, optional `PlayerId`, optional `NpcRoleId`, optional `RemoveNpc` | Dismounts the player from their NPC mount. `RemoveNpc` defaults to `true` and removes the mount entity from the world. Clears any manual-dismount lock set by `MountPlayer`. |
| `PlaceBlocks` | optional `MinigameId`, `BlockId`, `X1`, `Y1`, `Z1`, `X2`, `Y2`, `Z2`, optional `MaxBlocks` | Places `BlockId` in the inclusive cuboid between the two world-coordinate corners. `MaxBlocks` defaults to `32768` as a guard against accidental large fills. |
| `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. |
| `ModifyScore` | `MinigameId`, `TargetType`, `TargetSource`, `Operation`, `Amount` | Sets, adds, or subtracts player or team score. `TargetType=TEAM` with `TargetSource=TAG_TEAM_ID` uses the volume's `TeamId` tag. |
| `ModifyCounter` | `MinigameId`, `CounterId`, `Operation`, `Amount` | Sets, adds, or subtracts a named runtime counter. |
| `ModifyLives` | `MinigameId`, `TargetType`, `TargetSource`, `Operation`, `Amount` | Sets, adds, or subtracts one player's lives or all players on a resolved team. `TargetType=TEAM` with `TargetSource=TAG_TEAM_ID` uses the volume's `TeamId` tag. |
| `SetPlayerStatus` | `MinigameId`, optional `PlayerId`, `Status` | Sets a player's minigame status. Setting `ACTIVE` triggers the activation lifecycle: saves inventory (if `SavePlayerInventory`), gives loadout or start items, and applies `RequiredGameMode`. |
| `SetPlayerLoadout` | `MinigameId`, optional `PlayerId`, `LoadoutId` | Assigns a named loadout to a player. The loadout must exist in `StartLoadouts`. Takes effect the next time the player becomes `ACTIVE`. Pass an empty `LoadoutId` to clear the assignment. |
| `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`, `X`, `Y`, `Z` | Stores a player's respawn destination/checkpoint. |
| `RespawnPlayer` | `MinigameId`, optional `PlayerId`, optional `DestinationId` | Respawns a player using explicit destination, checkpoint, then team spawn fallback. |
| `RespawnAllPlayers` | `MinigameId`, optional `DestinationId`, optional `ClearCheckpoints`, optional `ActiveOnly`, optional `ReactivateEliminated` | Respawns every player in the runtime in one pass (round reset). `ClearCheckpoints` wipes stored checkpoints first so players go to their team spawn / start instead of their last checkpoint. `DestinationId` overrides every player's spawn for this respawn (e.g. a shared race start). `ActiveOnly` (default `true`) skips spectators and eliminated players. `ReactivateEliminated` returns eliminated players to `ACTIVE` before respawning them. |
| `SwapTeams` | `MinigameId`, optional `Rotation`, optional `ClearCheckpoints`, optional `Respawn` | Rotates players between teams for alternating-sides games. `Rotation` (default `1`) shifts each team's members forward in the id-sorted team list; `1` is a straight swap for two teams. `ClearCheckpoints` (default `true`) wipes checkpoints so respawn resolves the new side's spawn. `Respawn` (default `true`) teleports reassigned `ACTIVE` players to their new team spawn. Needs at least two teams. |
| `SendMinigameMessage` | `MinigameId`, `MessageKey`, `Target` | Sends a translated message to `PLAYER`, `TEAM`, or `ALL`. |
| `AssignTeam` | `MinigameId`, optional `PlayerId`, `TeamId`, optional `BalanceTeams` | Assigns a player to a team in the runtime. If `BalanceTeams` is true, evenly distributes all runtime players across existing teams or auto-creates `team1..teamN` using `TeamCount`, or `PlayersPerTeam` when `TeamCount` is `0`. |
| `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. |
| `OpenMinigameQueueUI` | optional `MinigameId`, optional `MapId` | Opens the queue UI page for the triggering player. Mode is inferred: no `MinigameId` → Global browser; `MinigameId` only → Single-game map list; both `MinigameId` and `MapId` → Local arena popup. |
| `StartPlayerTimer` | `MinigameId`, optional `PlayerId` | Starts a per-player stopwatch for the triggering player. Records the start timestamp in player state. Use on race start pads. If `PlayerId` is blank, the trigger context player is used. |
| `StopPlayerTimer` | `MinigameId`, optional `PlayerId`, optional `Operation` | Stops the player's stopwatch and applies the lap time to the player's score. `Operation` controls multi-round behaviour: `SET` (default) replaces the score, `ADD` accumulates lap times for a total time, `BEST` keeps the lowest recorded lap. Ignored if `StartPlayerTimer` was never called for this player. |
### 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`.
4. A random shared player spawn volume.
Shared spawn volumes are normal Hytale trigger volumes tagged with `MinigameId`, `MapId`, optional `VariantId`, and `SpawnRole=PlayerSpawn`. Team spawn volumes add `SpawnRole=TeamSpawn` and `TeamId=<team id>`. Runtime chooses a matching team spawn first, then falls back to a shared `PlayerSpawn`.
Player deaths during a minigame use the same resolution order automatically. `RespawnPlayer.DestinationId` accepts coordinate strings, such as `100,64,200` or `world_name,100,64,200`; `SetSpawnPoint` exposes separate `X`, `Y`, and `Z` fields and stores them as the player's checkpoint.
`RespawnAllPlayers` applies the same resolution to every player at once, making it the building block for resetting the arena between rounds. Because checkpoints (step 2) take priority over tagged spawn volumes (steps 3-4), set `ClearCheckpoints=true` when you want players sent back to their start line or team spawn rather than wherever they last checkpointed. A typical round transition chains `SetRound``ResetScores` (optional) → `SwapTeams` (for alternating-sides maps) → `RespawnAllPlayers`.
`SwapTeams` rotates team membership for maps where sides alternate each round (attack/defend). It clears checkpoints by default so the follow-up respawn resolves the new team's spawn; this relies on each side having team spawn volumes tagged as above, with shared `PlayerSpawn` volumes as fallback. With its `Respawn` flag left on, it moves players to their new side immediately and no separate `RespawnAllPlayers` is needed.
## 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`.
Set `IgnoreRespawnTimer=true` to drop the one-active-item rule and instead spawn a fresh copy on a fixed cadence regardless of whether the previous item was picked up or removed. The cadence is `RespawnDelaySeconds` between spawns (or every tick when the delay is `0`). Use this for a steady drip of items rather than a single replaceable pickup — be mindful that uncollected items accumulate in the world.
## NPC Spawners
`SpawnNpc` uses Hytale NPC role ids. `RoleId` is registered as a spawnable NPC role asset picker in the editor. Use `Count` plus `SpreadRadius` for small packs, and use `RespawnDelaySeconds` on a repeatedly firing volume when the encounter should come back after all NPCs for the same `SpawnKey` are gone.
## Player Mounts
`MountPlayer` uses Hytale's built-in NPC mount component. It mounts the player onto the nearest tracked NPC in the trigger volume, optionally filtered by `NpcRoleId` or exact `NpcId`. The trigger volume must track the NPC as well as the player, so include NPC target types on the volume when using nearest-NPC mounting.
`AnchorX`, `AnchorY`, and `AnchorZ` define the rider anchor passed to the client. `EmptyRoleId` defaults to `Empty_Role`, matching Hytale's built-in mount action. `MovementConfig` is optional; set it when the mounted player should use horse-style movement settings.
Set `PreventManualDismount=true` to lock the player onto the mount. The client's dismount packet is silently dropped until `DismountPlayer` is called. `DismountPlayer` always clears the lock, dismounts the player, and removes the NPC entity from the world by default (`RemoveNpc=true`). Filter by `NpcRoleId` if multiple mount types are active at the same time.
## Block Placement
`PlaceBlocks` fills an inclusive world-coordinate cuboid with one block type. Configure `BlockId`, `X1`, `Y1`, `Z1`, `X2`, `Y2`, and `Z2`; coordinates can be provided in either corner order.
The effect clamps Y to the valid world range `0..319`. Use `MaxBlocks` when a fill intentionally exceeds the default `32768` block guard.
## 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.