Implement guild system with multi-guild, spells, and combat casting

- Guild data model: world/guilds/*.toml defines guilds with stat growth,
  resource type (mana/endurance), spell lists, level caps, and race
  restrictions. Spells are separate files in world/spells/*.toml.

- Player resources: mana and endurance pools added to PlayerStats with
  DB migration. Passive regen for mana/endurance when out of combat.

- Guild commands: guild list/info/join/leave with multi-guild support.
  spells/skills command shows available spells per guild level.

- Spell casting: cast command works in and out of combat. Offensive
  spells queue as CombatAction::Cast and resolve on tick. Heal/utility
  spells resolve immediately out of combat. Mana/endurance costs,
  cooldowns, and damage types all enforced.

- Chargen integration: classes reference a guild via TOML field. On
  character creation, player auto-joins the seeded guild at level 1.
  Class selection shows "→ joins <Guild>" hint.

- Look command: now accepts optional target argument to inspect NPCs
  (stats/attitude), objects, exits, players, or inventory items.

- 4 guilds (warriors/rogues/mages/clerics) and 16 spells created for
  the existing class archetypes.

Made-with: Cursor
This commit is contained in:
AI Agent
2026-03-14 16:13:10 -06:00
parent bdd1a85ea2
commit 598360ac95
33 changed files with 1082 additions and 48 deletions

View File

@@ -83,8 +83,12 @@ impl ChargenState {
ansi::bold("=== Choose Your Class ===")
));
for (i, class) in world.classes.iter().enumerate() {
let guild_info = class.guild.as_ref()
.and_then(|gid| world.guilds.get(gid))
.map(|g| format!(" → joins {}", ansi::color(ansi::YELLOW, &g.name)))
.unwrap_or_default();
out.push_str(&format!(
" {}{}.{} {} {}\r\n {}\r\n {}HP:{} {}ATK:{} {}DEF:{}{}\r\n",
" {}{}.{} {} {}{}\r\n {}\r\n {}HP:{} {}ATK:{} {}DEF:{}{}\r\n",
ansi::BOLD,
i + 1,
ansi::RESET,
@@ -95,6 +99,7 @@ impl ChargenState {
class.growth.attack_per_level,
class.growth.defense_per_level,
)),
guild_info,
ansi::color(ansi::DIM, &class.description),
ansi::GREEN,
class.base_stats.max_hp,