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:
45
src/game.rs
45
src/game.rs
@@ -18,6 +18,10 @@ pub struct PlayerStats {
|
||||
pub level: i32,
|
||||
pub xp: i32,
|
||||
pub xp_to_next: i32,
|
||||
pub max_mana: i32,
|
||||
pub mana: i32,
|
||||
pub max_endurance: i32,
|
||||
pub endurance: i32,
|
||||
}
|
||||
|
||||
pub struct Player {
|
||||
@@ -28,6 +32,8 @@ pub struct Player {
|
||||
pub stats: PlayerStats,
|
||||
pub inventory: Vec<Object>,
|
||||
pub equipped: HashMap<String, Object>,
|
||||
pub guilds: HashMap<String, i32>,
|
||||
pub cooldowns: HashMap<String, i32>,
|
||||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
@@ -73,6 +79,7 @@ pub enum CombatAction {
|
||||
Defend,
|
||||
Flee,
|
||||
UseItem(usize),
|
||||
Cast(String),
|
||||
}
|
||||
|
||||
pub struct CombatState {
|
||||
@@ -234,6 +241,25 @@ impl GameState {
|
||||
let attack = base_atk + str_mod + dex_mod / 2;
|
||||
let defense = base_def + con_mod / 2;
|
||||
|
||||
// Compute starting mana/endurance from initial guild
|
||||
let initial_guild_id = class.and_then(|c| c.guild.clone());
|
||||
let (base_mana, base_endurance) = initial_guild_id.as_ref()
|
||||
.and_then(|gid| self.world.guilds.get(gid))
|
||||
.map(|g| (g.base_mana, g.base_endurance))
|
||||
.unwrap_or((0, 0));
|
||||
let int_mod = race.map(|r| r.stats.intelligence).unwrap_or(0);
|
||||
let wis_mod = race.map(|r| r.stats.wisdom).unwrap_or(0);
|
||||
let max_mana = base_mana + int_mod * 5 + wis_mod * 3;
|
||||
let max_endurance = base_endurance + con_mod * 3 + str_mod * 2;
|
||||
|
||||
let mut guilds = HashMap::new();
|
||||
if let Some(ref gid) = initial_guild_id {
|
||||
if self.world.guilds.contains_key(gid) {
|
||||
guilds.insert(gid.clone(), 1);
|
||||
self.db.save_guild_membership(&name, gid, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let stats = PlayerStats {
|
||||
max_hp,
|
||||
hp: max_hp,
|
||||
@@ -242,6 +268,10 @@ impl GameState {
|
||||
level: 1,
|
||||
xp: 0,
|
||||
xp_to_next: 100,
|
||||
max_mana,
|
||||
mana: max_mana,
|
||||
max_endurance,
|
||||
endurance: max_endurance,
|
||||
};
|
||||
|
||||
self.players.insert(
|
||||
@@ -255,6 +285,8 @@ impl GameState {
|
||||
stats,
|
||||
inventory: Vec::new(),
|
||||
equipped: HashMap::new(),
|
||||
guilds,
|
||||
cooldowns: HashMap::new(),
|
||||
is_admin: false,
|
||||
},
|
||||
channel,
|
||||
@@ -282,6 +314,9 @@ impl GameState {
|
||||
self.world.spawn_room.clone()
|
||||
};
|
||||
|
||||
let guilds_vec = self.db.load_guild_memberships(&saved.name);
|
||||
let guilds: HashMap<String, i32> = guilds_vec.into_iter().collect();
|
||||
|
||||
let stats = PlayerStats {
|
||||
max_hp: saved.max_hp,
|
||||
hp: saved.hp,
|
||||
@@ -290,6 +325,10 @@ impl GameState {
|
||||
level: saved.level,
|
||||
xp: saved.xp,
|
||||
xp_to_next: saved.level * 100,
|
||||
max_mana: saved.max_mana,
|
||||
mana: saved.mana,
|
||||
max_endurance: saved.max_endurance,
|
||||
endurance: saved.endurance,
|
||||
};
|
||||
|
||||
self.players.insert(
|
||||
@@ -303,6 +342,8 @@ impl GameState {
|
||||
stats,
|
||||
inventory,
|
||||
equipped,
|
||||
guilds,
|
||||
cooldowns: HashMap::new(),
|
||||
is_admin: saved.is_admin,
|
||||
},
|
||||
channel,
|
||||
@@ -333,6 +374,10 @@ impl GameState {
|
||||
defense: p.stats.defense,
|
||||
inventory_json: inv_json,
|
||||
equipped_json,
|
||||
mana: p.stats.mana,
|
||||
max_mana: p.stats.max_mana,
|
||||
endurance: p.stats.endurance,
|
||||
max_endurance: p.stats.max_endurance,
|
||||
is_admin: p.is_admin,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user