-
Notifications
You must be signed in to change notification settings - Fork 0
Combat and Taming
Instanced & turn-based. Combat is AI-ONLY (FGT-T1): every turn is owned by the judge LLM on the server (OpenAI, the core feature) in both single-player and multiplayer, routed through one shared resolver (server/combat.js aiTurn) so SP and MP resolve identical inputs identically. Both judge prompts are editable in /admin (server/prompts.js: combatJudgeV2System for the default v2 judge, combatSystem for the v1 fallback).
Structured judge (v2 — now the DEFAULT): the flag
combatJudgeV2(ON by default;server/judge.js) gives the judge a richer contract: it receives the action + full monster descriptions incl. passives + the running fight transcript, and returns per-field EDITS — integers as deltas, strings (status) as rewrites — plus a short display line and a special-actions channel (end battle / insta-win / flee) the combat loop acts on. A v2-generated monster fights with its own AI-authored attacks (each a 2-3 word title + a description the judge reads to resolve the move). SetcombatJudgeV2off in/adminto fall back to the v1 absolute-value judge.
Single-player holds no API key in the browser, so it routes each turn through the same server judge over HTTP (POST /api/combat/turn); MP routes it over the WebSocket. Combat needs a connection to the judge. If it's unreachable (offline / no key), the game shows a clear "combat needs connection" message and lets you retreat — it does not silently fall back to an offline fight. The deterministic engine (src/engine/combat.js) is kept ONLY as a transient crash-net: when the judge IS configured but a single call hangs/errors (the 10s abort), that one turn resolves offline so the fight can't freeze — no longer a normal gameplay path. Catch is the deterministic spirit-chain mechanic (not an LLM call), resolved identically in SP and MP.
The deterministic rules below define the crash-net and the baseline the AI is expected to roughly follow.
How a single attack resolves (crash-net baseline):
flowchart TD
A[Attacker's turn] --> B{Enough energy?}
B -->|no| S[Skip turn]
B -->|yes| C{Accuracy roll<br/>hit?}
C -->|miss| M[No damage]
C -->|hit| D[Damage = physical + elemental]
D --> E{Crit roll?}
E -->|yes| F[× critMultiplier]
E -->|no| G[base damage]
F --> I[Apply damage<br/>min 1]
G --> I
I --> J{Status roll?}
J -->|yes| K[Apply status<br/>Burn/Poison/Freeze/Stun]
J -->|no| L[End turn]
K --> L
Higher speed acts first; ties favor the player.
First-turn initiative (FGT-T9) overrides speed for turn 1 only, set by how the fight started:
- Walk into a wild monster → the monster acts first.
- Collide with another player (PvP) → a server-authoritative seeded coin-flip picks who acts first (neither client can influence it).
- Land a spirit chain (on a monster or a player) → the thrower acts first.
After turn 1 the initiative is consumed and order reverts to speed. The chosen initiator is carried into the AI judge prompt too, so AI-resolved turns honor it.
physical = attacker.strength * (attack.damage / 100)
- defender.defense * (1 - attack.penetration)
elemental = attacker.power * attack.elementalDiffusion
dmg = max(1, floor(physical + elemental))
// crit (see below) may multiply: dmg = floor(dmg * attack.critMultiplier)
// elements are FLAVOUR ONLY — no type-effectiveness multiplier; min 1 per hit
hit if rand(0..100) <= attack.accuracy * 100 + attacker.luck / 2
crit if rand(0..100) <= attack.critChance * 100 + attacker.luck / 4
on crit: damage *= attack.critMultiplier
Each attack costs energyCost. If a monster can't afford its action, it skips. Energy does not regenerate during a fight. Between encounters (decision Q8) every living team monster regains 50% of max energy (capped) at the start of each new fight, so a depleted team isn't stuck skipping forever. This applies in both SP and MP (server startCombat restores the same 50% as the SP scene — FGT-T5).
On your turn you may Fight (pick an attack — only the active monster's own attacks are honored), Catch (PvE only — attempt a capture, see Taming), Swap (switch to another living team monster — a free action that doesn't cost the turn and keeps any first-turn initiative), use an Item (when you have any — instead of attacking; see Combat Items), Skip, or Flee. Swap and Items both work in both SP and MP (FGT-T4 / #61).
Player-vs-player fights reuse the same AI turn resolver but differ from PvE in two ways:
- Interactive turns: both players secretly pick a move; the turn resolves through the AI judge once both have submitted (you wait on the opponent in between).
- Catch is disabled: you cannot capture another player's monster. Instead, defeating the opponent (KO'ing their last active monster) loots their entire active team into your vault (capped at your vault capacity); the loser refills from their vault or fresh starters and stays in the round. Fleeing is a no-contest (no loot). This is intentional (decision Q11 / FGT-T6).
Initiative on turn 1 follows the turn-order rules: a chain throw favors the thrower; a contact duel uses a server-authoritative seeded coin-flip (FGT-T9). After turn 1, speed order.
Simple one-shot combat items — a name + a short action description — usable only during a fight, in place of an attack or fleeing. Like monsters, items are AI-generated (an inspiration agent → a designer agent; prompts + model are admin-editable). Their effect is not a fixed stat change: the fight judge reads the item's action description and resolves it exactly like an attack (so an item can damage, heal, cure a status, buff, etc. — whatever its text says), then the item is consumed.
| Rule | Detail |
|---|---|
| Source | Loot chests roll one item (seeded, ~30% chance; none when the item pool is empty) |
| Storage | A small per-profile item bag (capped at GAME.ITEM_BAG_SIZE); synced to the client on connect |
| Use | In combat, tap Items → pick one (name + description). MP sends {kind:"item", itemId}; SP carries the item's {name, description} in the turn. PvE and PvP. |
| Resolution | The v2 structured judge reads the description and resolves it like an attack (items carry no numeric fields, so they need the v2 judge); the item is removed from the bag after use. |
| Status | Effect |
|---|---|
| Burn | 5% of max HP damage per turn |
| Poison | 3% of max HP per turn (stacks with burn in the spec) |
| Freeze | 30% chance to skip the turn |
| Stun | skip one turn, then clears |
Only one status at a time; a new status replaces the old. Applied via attack.statusChance. Now enforced by the deterministic engine (synonyms normalized: Stunned→Stun, Frozen→Freeze, Poisoned→Poison). Status is per-fight: it's only set during a fight (by the AI judge), and is cleared when the fight ends — your team never carries an affliction back to the overworld.
Resolved (Q7 — taxonomy shelved): the attack data names 60+ distinct status labels (Bleed, Blind, Confusion, Fear, Paralysis…) plus buffs (Heal, Shielded, Reflect). Rather than hand-code a taxonomy, the AI resolver interprets statuses freely during live fights — but the judge is now instructed that every status MUST have a real effect (never cosmetic): damage-over-time (Burn/Poison/Bleed), turn-loss (Stun/Freeze/Sleep), or damage-down (Weaken), ticking until it wears off. The four mechanics above are the deterministic fallback's canonical set for offline/crash-net play.
Catch with a spirit chain during combat. Base chance by enemy HP %, then scaled by the equipped chain's capture multiplier (and gated by its max rarity):
| Enemy HP | Base catch chance |
|---|---|
| < 25% | 70% |
| 25–50% | 40% |
| 50–75% | 20% |
| > 75% | 5% |
Modifiers, in order: +15% if the enemy has a status effect → × the chain's capture multiplier (0.50× tier 1 … 1.60× tier 5) → clamped to 95% (a guaranteed chain bypasses the clamp at ≤25% HP) → auto-fail if the enemy's rarity exceeds the chain's max rarity. The enemy still attacks during the attempt, except on a first-turn player-initiated catch (a thrown-chain engage), which skips the retaliation. On success the monster joins the team (or the vault if the team is full of 4), a chain charge is consumed, and the catch is stabilized to a usable amount of HP/energy (50% of max, GAME.CATCH_HEAL_FRACTION) so a caught-at-death monster isn't dead weight for the rest of the run (CB-9).
Tamer's Quest — game-logic wiki. This GitHub Wiki is the canonical, code-mirrored spec (it superseded the in-app public/wiki.html, now archived, on 2026-06-11). Pair with docs/IMPLEMENTATION_PLAN.md and docs/REQUIREMENTS.md in the repo.
Tamer's Quest Game Logic Wiki
- Home
- Onboarding and Title
- Multiplayer and Networking
- Movement and Sprint
- Monsters Stats and Elements
- Combat and Taming
- Spirit Chains
- Progression
- Map and Biomes
- Extraction Round
- Rendering and HUD
- Open Questions
Legend
[CURRENT] implemented
[PLANNED] vision
[OPEN] undecided