mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Forest of Shadows + Sunken Temple of Dar'eth registered. New bestiary entries (dire wolf, bandit captain, owlbear, corrupted dryad, displacer beast, green hag, kuo-toa + whip, water elemental, merrow, aboleth thrall, Hollow King boss, Dreaming Aboleth boss) per dungeon zones doc §5. L1 tier-gate test expectation bumped from 2→4 zones now that T2 is visible to T1 players. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
349 lines
14 KiB
Go
349 lines
14 KiB
Go
package plugin
|
|
|
|
// Phase 7 — D&D starter bestiary.
|
|
//
|
|
// These are named monster definitions ready to slot into encounter content.
|
|
// Phase 7 ships them as data only — wiring them into specific dungeons or
|
|
// arena rounds is a future content task. The combat engine consumes them
|
|
// via the existing DnDMonsterTemplate→CombatStats path (see toCombatStats).
|
|
|
|
// DnDMonsterTemplate is the bestiary record for a named creature. Mirrors
|
|
// v1.0 §8.1's stat block schema, simplified to the subset combat actually
|
|
// uses (HP, AC, attack, speed, ability proc, special tags).
|
|
type DnDMonsterTemplate struct {
|
|
ID string
|
|
Name string
|
|
CR float32 // challenge rating (display only for now)
|
|
HP int
|
|
AC int
|
|
Attack int // engine's "Attack" stat (raw damage value)
|
|
AttackBonus int // d20 to-hit bonus
|
|
Speed int
|
|
BlockRate float64
|
|
Ability *MonsterAbility
|
|
XPValue int
|
|
Notes string
|
|
}
|
|
|
|
// dndBestiary is the canonical lookup. Keyed by ID.
|
|
var dndBestiary = map[string]DnDMonsterTemplate{
|
|
"goblin": {
|
|
ID: "goblin", Name: "Goblin",
|
|
CR: 0.25, HP: 7, AC: 13, Attack: 6, AttackBonus: 4, Speed: 14,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Nimble Escape", Phase: "any", ProcChance: 0.20, Effect: "stun"},
|
|
XPValue: 30,
|
|
Notes: "Fast and skittish. Escapes pin attempts; will retreat if obviously outmatched.",
|
|
},
|
|
"skeleton": {
|
|
ID: "skeleton", Name: "Skeleton",
|
|
CR: 0.25, HP: 13, AC: 13, Attack: 8, AttackBonus: 4, Speed: 10,
|
|
BlockRate: 0.10,
|
|
// Skeletons are immune to poison; we'd model that with a future
|
|
// CombatModifiers.PoisonImmunity flag. For Phase 7, no ability.
|
|
XPValue: 50,
|
|
Notes: "Bone-and-rust. Resistant to slashing weapons; vulnerable to bludgeoning.",
|
|
},
|
|
"orc_grunt": {
|
|
ID: "orc_grunt", Name: "Orc Grunt",
|
|
CR: 0.5, HP: 15, AC: 13, Attack: 10, AttackBonus: 5, Speed: 11,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Aggressive", Phase: "opening", ProcChance: 0.30, Effect: "enrage"},
|
|
XPValue: 100,
|
|
Notes: "Charges. Bonus damage on its first hit if it goes first.",
|
|
},
|
|
"troll": {
|
|
ID: "troll", Name: "Troll",
|
|
CR: 5, HP: 84, AC: 15, Attack: 22, AttackBonus: 7, Speed: 8,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Regeneration", Phase: "any", ProcChance: 0.50, Effect: "lifesteal"},
|
|
XPValue: 1800,
|
|
Notes: "Regenerates each round unless burned or acid-burned. Fire/acid damage stops the regen.",
|
|
},
|
|
"wyvern": {
|
|
ID: "wyvern", Name: "Wyvern",
|
|
CR: 6, HP: 110, AC: 13, Attack: 28, AttackBonus: 7, Speed: 16,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Poison Sting", Phase: "decisive", ProcChance: 0.60, Effect: "poison"},
|
|
XPValue: 2300,
|
|
Notes: "Aerial; opens with dive attack. Tail sting injects poison (CON DC 15 in tabletop).",
|
|
},
|
|
"ancient_dragon": {
|
|
ID: "ancient_dragon", Name: "Ancient Dragon",
|
|
CR: 20, HP: 367, AC: 22, Attack: 65, AttackBonus: 14, Speed: 18,
|
|
BlockRate: 0.15,
|
|
Ability: &MonsterAbility{Name: "Frightful Presence", Phase: "opening", ProcChance: 0.80, Effect: "stun"},
|
|
XPValue: 25000,
|
|
Notes: "Legendary. Breath weapon (cleave-equivalent), frightful presence on opening, regenerates between phases. Tabletop equivalent has legendary actions; the engine approximates with elevated stats.",
|
|
},
|
|
}
|
|
|
|
// ---- Tier 1 zone roster (Phase 11 D1a) ---------------------------------------
|
|
//
|
|
// Goblin Warrens + Crypt of Valdris enemy entries. Stat blocks per
|
|
// gogobee_dungeon_zones.md §5. AttackBonus and Attack are derived from
|
|
// each creature's primary attack profile in 5e (rounded for the engine's
|
|
// integer "Attack" stat — engine treats this as average damage).
|
|
|
|
var _ = func() bool {
|
|
tier1 := map[string]DnDMonsterTemplate{
|
|
"goblin_sneak": {
|
|
ID: "goblin_sneak", Name: "Goblin Sneak",
|
|
CR: 0.25, HP: 7, AC: 13, Attack: 6, AttackBonus: 4, Speed: 14,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Pack Tactics", Phase: "any", ProcChance: 0.25, Effect: "advantage"},
|
|
XPValue: 30,
|
|
Notes: "Nimble Escape; +1d4 if ally adjacent.",
|
|
},
|
|
"goblin_archer": {
|
|
ID: "goblin_archer", Name: "Goblin Archer",
|
|
CR: 0.25, HP: 7, AC: 13, Attack: 5, AttackBonus: 4, Speed: 14,
|
|
BlockRate: 0.0,
|
|
Ability: &MonsterAbility{Name: "Scurry", Phase: "any", ProcChance: 0.30, Effect: "evade"},
|
|
XPValue: 30,
|
|
Notes: "Ranged only. Disengages after attack.",
|
|
},
|
|
"hobgoblin_grunt": {
|
|
ID: "hobgoblin_grunt", Name: "Hobgoblin Grunt",
|
|
CR: 0.5, HP: 11, AC: 18, Attack: 9, AttackBonus: 3, Speed: 11,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Martial Advantage", Phase: "any", ProcChance: 0.30, Effect: "bonus_damage"},
|
|
XPValue: 100,
|
|
Notes: "Pack-tactics variant. Formation bonus when grouped.",
|
|
},
|
|
"worg": {
|
|
ID: "worg", Name: "Worg",
|
|
CR: 0.5, HP: 26, AC: 13, Attack: 10, AttackBonus: 5, Speed: 17,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Knock Prone", Phase: "any", ProcChance: 0.25, Effect: "stun"},
|
|
XPValue: 100,
|
|
Notes: "Carries goblin riders. STR DC 13 to avoid prone on hit.",
|
|
},
|
|
"goblin_shaman": {
|
|
ID: "goblin_shaman", Name: "Goblin Shaman",
|
|
CR: 1, HP: 22, AC: 11, Attack: 12, AttackBonus: 4, Speed: 12,
|
|
BlockRate: 0.0,
|
|
Ability: &MonsterAbility{Name: "Burning Hands", Phase: "opening", ProcChance: 0.50, Effect: "aoe_fire"},
|
|
XPValue: 200,
|
|
Notes: "Burning Hands 2d6 fire DEX DC 11; Healing Word on allies.",
|
|
},
|
|
"hobgoblin_warchief": {
|
|
ID: "hobgoblin_warchief", Name: "Hobgoblin Warchief",
|
|
CR: 2, HP: 52, AC: 18, Attack: 18, AttackBonus: 5, Speed: 11,
|
|
BlockRate: 0.15,
|
|
Ability: &MonsterAbility{Name: "Leadership Aura", Phase: "any", ProcChance: 0.40, Effect: "ally_buff"},
|
|
XPValue: 450,
|
|
Notes: "Elite. +1d4 to ally attacks; multiattack.",
|
|
},
|
|
"zombie": {
|
|
ID: "zombie", Name: "Zombie",
|
|
CR: 0.25, HP: 22, AC: 10, Attack: 5, AttackBonus: 3, Speed: 6,
|
|
BlockRate: 0.0,
|
|
Ability: &MonsterAbility{Name: "Undead Fortitude", Phase: "decisive", ProcChance: 0.50, Effect: "survive_at_1"},
|
|
XPValue: 50,
|
|
Notes: "CON DC 5 + damage dealt or survive at 1 HP. AC bumped 8→10 to satisfy engine min.",
|
|
},
|
|
"shadow": {
|
|
ID: "shadow", Name: "Shadow",
|
|
CR: 0.5, HP: 16, AC: 12, Attack: 9, AttackBonus: 4, Speed: 12,
|
|
BlockRate: 0.0,
|
|
Ability: &MonsterAbility{Name: "Strength Drain", Phase: "any", ProcChance: 0.35, Effect: "stat_drain"},
|
|
XPValue: 100,
|
|
Notes: "Hit reduces STR by 1d4 until long rest. Resist non-magical physical.",
|
|
},
|
|
"specter": {
|
|
ID: "specter", Name: "Specter",
|
|
CR: 1, HP: 22, AC: 12, Attack: 10, AttackBonus: 4, Speed: 14,
|
|
BlockRate: 0.0,
|
|
Ability: &MonsterAbility{Name: "Life Drain", Phase: "any", ProcChance: 0.40, Effect: "lifesteal"},
|
|
XPValue: 200,
|
|
Notes: "Incorporeal: non-magical weapons deal half damage.",
|
|
},
|
|
"wight": {
|
|
ID: "wight", Name: "Wight",
|
|
CR: 3, HP: 45, AC: 14, Attack: 14, AttackBonus: 4, Speed: 12,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Life Drain", Phase: "any", ProcChance: 0.45, Effect: "lifesteal"},
|
|
XPValue: 700,
|
|
Notes: "Elite. Raises slain humanoids as zombies.",
|
|
},
|
|
"flameskull": {
|
|
ID: "flameskull", Name: "Flameskull",
|
|
CR: 4, HP: 40, AC: 13, Attack: 18, AttackBonus: 5, Speed: 15,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Fireball", Phase: "decisive", ProcChance: 0.55, Effect: "aoe_fire"},
|
|
XPValue: 1100,
|
|
Notes: "Elite. Fireball 8d6 DEX DC 13. Rejuvenation in 1h unless holy water used.",
|
|
},
|
|
"boss_grol_unbroken": {
|
|
ID: "boss_grol_unbroken", Name: "Grol the Unbroken",
|
|
CR: 3, HP: 65, AC: 17, Attack: 22, AttackBonus: 5, Speed: 12,
|
|
BlockRate: 0.15,
|
|
Ability: &MonsterAbility{Name: "Surprise Attack", Phase: "opening", ProcChance: 1.0, Effect: "bonus_damage"},
|
|
XPValue: 700,
|
|
Notes: "Goblin Warrens boss. Bugbear war-chief.",
|
|
},
|
|
"boss_valdris_unburied": {
|
|
ID: "boss_valdris_unburied", Name: "Valdris the Unburied",
|
|
CR: 5, HP: 97, AC: 17, Attack: 28, AttackBonus: 7, Speed: 12,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Corrupting Touch", Phase: "any", ProcChance: 0.50, Effect: "max_hp_drain"},
|
|
XPValue: 1800,
|
|
Notes: "Crypt of Valdris boss. Lich-aspirant; phase 2 below 50% HP.",
|
|
},
|
|
}
|
|
for id, m := range tier1 {
|
|
dndBestiary[id] = m
|
|
}
|
|
return true
|
|
}()
|
|
|
|
// ---- Tier 2 zone roster (Phase 11 D3a) ---------------------------------------
|
|
//
|
|
// Forest of Shadows + Sunken Temple of Dar'eth enemy entries. Stat blocks
|
|
// per gogobee_dungeon_zones.md §5. Same Attack-as-average-damage convention
|
|
// as Tier 1: Attack ≈ avg of primary attack profile, AttackBonus = d20
|
|
// to-hit modifier. Elites flagged via the zone roster, not here.
|
|
|
|
var _ = func() bool {
|
|
tier2 := map[string]DnDMonsterTemplate{
|
|
"dire_wolf": {
|
|
ID: "dire_wolf", Name: "Dire Wolf",
|
|
CR: 1, HP: 37, AC: 14, Attack: 10, AttackBonus: 5, Speed: 16,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Pack Tactics", Phase: "any", ProcChance: 0.30, Effect: "advantage"},
|
|
XPValue: 200,
|
|
Notes: "Knock Prone on hit (STR DC 13); +1d6 if ally adjacent.",
|
|
},
|
|
"bandit_captain": {
|
|
ID: "bandit_captain", Name: "Bandit Captain",
|
|
CR: 2, HP: 65, AC: 15, Attack: 14, AttackBonus: 5, Speed: 12,
|
|
BlockRate: 0.20,
|
|
Ability: &MonsterAbility{Name: "Parry", Phase: "any", ProcChance: 0.35, Effect: "block"},
|
|
XPValue: 450,
|
|
Notes: "Multiattack 3x; reaction Parry adds +3 AC vs one attack.",
|
|
},
|
|
"owlbear": {
|
|
ID: "owlbear", Name: "Owlbear",
|
|
CR: 3, HP: 59, AC: 13, Attack: 16, AttackBonus: 7, Speed: 12,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Grapple", Phase: "any", ProcChance: 0.40, Effect: "stun"},
|
|
XPValue: 700,
|
|
Notes: "Beak + Claw multiattack. Claw hit triggers grapple attempt.",
|
|
},
|
|
"dryad_corrupted": {
|
|
ID: "dryad_corrupted", Name: "Corrupted Dryad",
|
|
CR: 2, HP: 22, AC: 11, Attack: 11, AttackBonus: 4, Speed: 12,
|
|
BlockRate: 0.0,
|
|
Ability: &MonsterAbility{Name: "Fey Charm", Phase: "any", ProcChance: 0.40, Effect: "stun"},
|
|
XPValue: 450,
|
|
Notes: "Barkskin self-buff (+2 AC); WIS DC 14 vs charm or waste a turn.",
|
|
},
|
|
"displacer_beast": {
|
|
ID: "displacer_beast", Name: "Displacer Beast",
|
|
CR: 3, HP: 85, AC: 13, Attack: 14, AttackBonus: 6, Speed: 14,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Displacement", Phase: "opening", ProcChance: 0.50, Effect: "evade"},
|
|
XPValue: 700,
|
|
Notes: "First hit on player each round has disadvantage; Avoidance trait.",
|
|
},
|
|
"green_hag": {
|
|
ID: "green_hag", Name: "Green Hag",
|
|
CR: 3, HP: 82, AC: 17, Attack: 15, AttackBonus: 5, Speed: 12,
|
|
BlockRate: 0.15,
|
|
Ability: &MonsterAbility{Name: "Invisible Passage", Phase: "any", ProcChance: 0.35, Effect: "evade"},
|
|
XPValue: 700,
|
|
Notes: "Elite. Illusory Appearance; Weakness curse on hit.",
|
|
},
|
|
"kuo_toa": {
|
|
ID: "kuo_toa", Name: "Kuo-toa",
|
|
CR: 0.25, HP: 18, AC: 13, Attack: 5, AttackBonus: 3, Speed: 10,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Sticky Shield", Phase: "any", ProcChance: 0.25, Effect: "block"},
|
|
XPValue: 30,
|
|
Notes: "Amphibious. Slippery (auto-escape grapple).",
|
|
},
|
|
"kuo_toa_whip": {
|
|
ID: "kuo_toa_whip", Name: "Kuo-toa Whip",
|
|
CR: 1, HP: 65, AC: 11, Attack: 11, AttackBonus: 3, Speed: 10,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Divine Eminence", Phase: "decisive", ProcChance: 0.50, Effect: "bonus_damage"},
|
|
XPValue: 200,
|
|
Notes: "Pincer Staff grapple on hit; +2d6 psychic on Eminence proc.",
|
|
},
|
|
"water_elemental": {
|
|
ID: "water_elemental", Name: "Water Elemental",
|
|
CR: 5, HP: 114, AC: 14, Attack: 26, AttackBonus: 7, Speed: 12,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Whelm", Phase: "any", ProcChance: 0.40, Effect: "stun"},
|
|
XPValue: 1800,
|
|
Notes: "Elite. Whelm grapples + drowning (CON DC 13). Freeze reaction.",
|
|
},
|
|
"merrow": {
|
|
ID: "merrow", Name: "Merrow",
|
|
CR: 2, HP: 45, AC: 13, Attack: 13, AttackBonus: 5, Speed: 12,
|
|
BlockRate: 0.05,
|
|
Ability: &MonsterAbility{Name: "Harpoon Pull", Phase: "opening", ProcChance: 0.40, Effect: "stun"},
|
|
XPValue: 450,
|
|
Notes: "Amphibious. Harpoon yanks target 15 ft; multiattack.",
|
|
},
|
|
"aboleth_thrall": {
|
|
ID: "aboleth_thrall", Name: "Aboleth Thrall",
|
|
CR: 3, HP: 60, AC: 12, Attack: 14, AttackBonus: 5, Speed: 11,
|
|
BlockRate: 0.0,
|
|
Ability: &MonsterAbility{Name: "Psychic Bond", Phase: "any", ProcChance: 0.35, Effect: "reveal_action"},
|
|
XPValue: 700,
|
|
Notes: "WIS DC 14 or telegraph next action. Mucus Cloud aura.",
|
|
},
|
|
"boss_hollow_king": {
|
|
ID: "boss_hollow_king", Name: "The Hollow King",
|
|
CR: 6, HP: 142, AC: 15, Attack: 30, AttackBonus: 7, Speed: 13,
|
|
BlockRate: 0.15,
|
|
Ability: &MonsterAbility{Name: "Root Surge", Phase: "any", ProcChance: 0.45, Effect: "stun"},
|
|
XPValue: 2300,
|
|
Notes: "Forest of Shadows boss. Phase 2 below 40% HP — summons 2 Dire Wolves.",
|
|
},
|
|
"boss_dreaming_aboleth": {
|
|
ID: "boss_dreaming_aboleth", Name: "The Dreaming Aboleth",
|
|
CR: 10, HP: 135, AC: 17, Attack: 36, AttackBonus: 9, Speed: 10,
|
|
BlockRate: 0.10,
|
|
Ability: &MonsterAbility{Name: "Enslave", Phase: "any", ProcChance: 0.40, Effect: "stun"},
|
|
XPValue: 5900,
|
|
Notes: "Sunken Temple boss. Tentacle multiattack; legendary actions; psychic drain.",
|
|
},
|
|
}
|
|
for id, m := range tier2 {
|
|
dndBestiary[id] = m
|
|
}
|
|
return true
|
|
}()
|
|
|
|
// dndBestiaryByCR returns templates whose CR is at or below the given cap.
|
|
// Useful for procedurally selecting a monster appropriate to player level.
|
|
func dndBestiaryByCR(maxCR float32) []DnDMonsterTemplate {
|
|
var out []DnDMonsterTemplate
|
|
for _, m := range dndBestiary {
|
|
if m.CR <= maxCR {
|
|
out = append(out, m)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// toCombatStats converts a bestiary entry into the engine's CombatStats +
|
|
// CombatModifiers shape. Future encounter content can use this to spawn
|
|
// named monsters in the existing combat pipeline.
|
|
func (m DnDMonsterTemplate) toCombatStats() (CombatStats, CombatModifiers) {
|
|
stats := CombatStats{
|
|
MaxHP: m.HP,
|
|
Attack: m.Attack,
|
|
Defense: 0,
|
|
Speed: m.Speed,
|
|
BlockRate: m.BlockRate,
|
|
AC: m.AC,
|
|
AttackBonus: m.AttackBonus,
|
|
}
|
|
mods := CombatModifiers{DamageReduct: 1.0}
|
|
return stats, mods
|
|
}
|