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 }() // 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 }