Implement currency, shops, and enhanced NPC interaction system
This commit is contained in:
45
src/world.rs
45
src/world.rs
@@ -79,10 +79,22 @@ pub struct RoomFile {
|
||||
pub exits: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct ShopFile {
|
||||
pub buys: Vec<String>, // List of item kinds or IDs the shop buys
|
||||
pub sells: Vec<String>, // List of item IDs the shop sells
|
||||
#[serde(default)]
|
||||
pub markup: f32, // Multiplier for sell price (default 1.0)
|
||||
#[serde(default)]
|
||||
pub markdown: f32, // Multiplier for buy price (default 0.5)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct NpcDialogue {
|
||||
#[serde(default)]
|
||||
pub greeting: Option<String>,
|
||||
#[serde(default)]
|
||||
pub keywords: HashMap<String, String>, // keyword -> response
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -113,6 +125,14 @@ pub struct NpcFile {
|
||||
pub dialogue: Option<NpcDialogue>,
|
||||
#[serde(default)]
|
||||
pub combat: Option<NpcCombatFile>,
|
||||
#[serde(default)]
|
||||
pub shop: Option<ShopFile>,
|
||||
#[serde(default)]
|
||||
pub gold: i32,
|
||||
#[serde(default)]
|
||||
pub silver: i32,
|
||||
#[serde(default)]
|
||||
pub copper: i32,
|
||||
}
|
||||
|
||||
fn default_attitude() -> Attitude {
|
||||
@@ -143,6 +163,12 @@ pub struct ObjectFile {
|
||||
pub takeable: bool,
|
||||
#[serde(default)]
|
||||
pub stats: Option<ObjectStatsFile>,
|
||||
#[serde(default)]
|
||||
pub value_gold: i32,
|
||||
#[serde(default)]
|
||||
pub value_silver: i32,
|
||||
#[serde(default)]
|
||||
pub value_copper: i32,
|
||||
}
|
||||
|
||||
// --- Race TOML schema ---
|
||||
@@ -409,7 +435,12 @@ pub struct Npc {
|
||||
pub fixed_class: Option<String>,
|
||||
pub respawn_secs: Option<u64>,
|
||||
pub greeting: Option<String>,
|
||||
pub keywords: HashMap<String, String>,
|
||||
pub combat: Option<NpcCombatStats>,
|
||||
pub shop: Option<ShopFile>,
|
||||
pub gold: i32,
|
||||
pub silver: i32,
|
||||
pub copper: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
@@ -429,6 +460,9 @@ pub struct Object {
|
||||
pub slot: Option<String>,
|
||||
pub takeable: bool,
|
||||
pub stats: ObjectStats,
|
||||
pub value_gold: i32,
|
||||
pub value_silver: i32,
|
||||
pub value_copper: i32,
|
||||
}
|
||||
|
||||
pub const DEFAULT_HUMANOID_SLOTS: &[&str] = &[
|
||||
@@ -637,14 +671,18 @@ impl World {
|
||||
|
||||
load_entities_from_dir(®ion_path.join("npcs"), ®ion_name, &mut |id, content| {
|
||||
let nf: NpcFile = toml::from_str(content).map_err(|e| format!("Bad npc {id}: {e}"))?;
|
||||
let (greeting, keywords) = match nf.dialogue {
|
||||
Some(d) => (d.greeting, d.keywords),
|
||||
None => (None, HashMap::new()),
|
||||
};
|
||||
let combat = Some(nf.combat.map(|c| NpcCombatStats { max_hp: c.max_hp, attack: c.attack, defense: c.defense, xp_reward: c.xp_reward })
|
||||
.unwrap_or(NpcCombatStats { max_hp: 20, attack: 4, defense: 2, xp_reward: 5 }));
|
||||
let greeting = nf.dialogue.and_then(|d| d.greeting);
|
||||
npcs.insert(id.clone(), Npc {
|
||||
id: id.clone(), name: nf.name, description: nf.description, room: nf.room,
|
||||
base_attitude: nf.base_attitude, faction: nf.faction,
|
||||
fixed_race: nf.race, fixed_class: nf.class,
|
||||
respawn_secs: nf.respawn_secs, greeting, combat,
|
||||
respawn_secs: nf.respawn_secs, greeting, keywords, combat,
|
||||
shop: nf.shop, gold: nf.gold, silver: nf.silver, copper: nf.copper,
|
||||
});
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -656,6 +694,7 @@ impl World {
|
||||
id: id.clone(), name: of.name, description: of.description, room: of.room,
|
||||
kind: of.kind, slot: of.slot, takeable: of.takeable,
|
||||
stats: ObjectStats { damage: stats.damage, armor: stats.armor, heal_amount: stats.heal_amount },
|
||||
value_gold: of.value_gold, value_silver: of.value_silver, value_copper: of.value_copper,
|
||||
});
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Reference in New Issue
Block a user