194 lines
5.1 KiB
Rust
194 lines
5.1 KiB
Rust
use crate::types::Npc;
|
|
use crate::xml_parser::{parse_npcs_xml, XmlParseError};
|
|
use diesel::prelude::*;
|
|
use diesel::sqlite::SqliteConnection;
|
|
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
|
|
/// A database for managing NPCs loaded from XML files
|
|
#[derive(Debug, Clone)]
|
|
pub struct NpcDatabase {
|
|
npcs: Vec<Npc>,
|
|
npcs_by_id: HashMap<i32, usize>,
|
|
npcs_by_name: HashMap<String, Vec<usize>>,
|
|
}
|
|
|
|
impl NpcDatabase {
|
|
/// Create a new empty NpcDatabase
|
|
pub fn new() -> Self {
|
|
Self {
|
|
npcs: Vec::new(),
|
|
npcs_by_id: HashMap::new(),
|
|
npcs_by_name: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Load NPCs from an XML file
|
|
pub fn load_from_xml<P: AsRef<Path>>(path: P) -> Result<Self, XmlParseError> {
|
|
let npcs = parse_npcs_xml(path)?;
|
|
let mut db = Self::new();
|
|
db.add_npcs(npcs);
|
|
Ok(db)
|
|
}
|
|
|
|
/// Add NPCs to the database
|
|
pub fn add_npcs(&mut self, npcs: Vec<Npc>) {
|
|
for npc in npcs {
|
|
let index = self.npcs.len();
|
|
self.npcs_by_id.insert(npc.id, index);
|
|
|
|
self.npcs_by_name
|
|
.entry(npc.name.clone())
|
|
.or_insert_with(Vec::new)
|
|
.push(index);
|
|
|
|
self.npcs.push(npc);
|
|
}
|
|
}
|
|
|
|
/// Get an NPC by ID
|
|
pub fn get_by_id(&self, id: i32) -> Option<&Npc> {
|
|
self.npcs_by_id
|
|
.get(&id)
|
|
.and_then(|&index| self.npcs.get(index))
|
|
}
|
|
|
|
/// Get NPCs by name
|
|
pub fn get_by_name(&self, name: &str) -> Vec<&Npc> {
|
|
self.npcs_by_name
|
|
.get(name)
|
|
.map(|indices| {
|
|
indices
|
|
.iter()
|
|
.filter_map(|&index| self.npcs.get(index))
|
|
.collect()
|
|
})
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
/// Get all NPCs
|
|
pub fn all_npcs(&self) -> &[Npc] {
|
|
&self.npcs
|
|
}
|
|
|
|
/// Get all hostile NPCs (can fight and aggressive)
|
|
pub fn get_hostile(&self) -> Vec<&Npc> {
|
|
self.npcs
|
|
.iter()
|
|
.filter(|npc| {
|
|
npc.canfight == Some(1) && npc.aggressive == Some(1)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Get all interactable NPCs
|
|
pub fn get_interactable(&self) -> Vec<&Npc> {
|
|
self.npcs
|
|
.iter()
|
|
.filter(|npc| npc.interactable == Some(1))
|
|
.collect()
|
|
}
|
|
|
|
/// Get NPCs by tag
|
|
pub fn get_by_tag(&self, tag: &str) -> Vec<&Npc> {
|
|
self.npcs
|
|
.iter()
|
|
.filter(|npc| {
|
|
npc.tags
|
|
.as_ref()
|
|
.map(|tags| tags.split(',').any(|t| t.trim() == tag))
|
|
.unwrap_or(false)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Get NPCs that offer shops
|
|
pub fn get_shopkeepers(&self) -> Vec<&Npc> {
|
|
self.npcs
|
|
.iter()
|
|
.filter(|npc| npc.shop.is_some())
|
|
.collect()
|
|
}
|
|
|
|
/// Get number of NPCs in database
|
|
pub fn len(&self) -> usize {
|
|
self.npcs.len()
|
|
}
|
|
|
|
/// Check if database is empty
|
|
pub fn is_empty(&self) -> bool {
|
|
self.npcs.is_empty()
|
|
}
|
|
|
|
/// Prepare NPCs for SQL insertion (deprecated - use save_to_db instead)
|
|
#[deprecated(note = "Use save_to_db() to save directly to SQLite database")]
|
|
pub fn prepare_for_sql(&self) -> Vec<(i32, String, String)> {
|
|
self.npcs
|
|
.iter()
|
|
.map(|npc| {
|
|
let json = serde_json::to_string(npc).unwrap_or_else(|_| "{}".to_string());
|
|
(npc.id, npc.name.clone(), json)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Save all NPCs to SQLite database
|
|
pub fn save_to_db(&self, conn: &mut SqliteConnection) -> Result<usize, diesel::result::Error> {
|
|
use crate::schema::npcs;
|
|
|
|
let records: Vec<_> = self
|
|
.npcs
|
|
.iter()
|
|
.map(|npc| {
|
|
let json = serde_json::to_string(npc).unwrap_or_else(|_| "{}".to_string());
|
|
(
|
|
npcs::id.eq(npc.id),
|
|
npcs::name.eq(&npc.name),
|
|
npcs::data.eq(json),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
let mut count = 0;
|
|
for record in records {
|
|
diesel::insert_into(npcs::table)
|
|
.values(&record)
|
|
.execute(conn)?;
|
|
count += 1;
|
|
}
|
|
|
|
Ok(count)
|
|
}
|
|
|
|
/// Load all NPCs from SQLite database
|
|
pub fn load_from_db(conn: &mut SqliteConnection) -> Result<Self, diesel::result::Error> {
|
|
use crate::schema::npcs::dsl::*;
|
|
|
|
#[derive(Queryable)]
|
|
struct NpcRecord {
|
|
id: Option<i32>,
|
|
name: String,
|
|
data: String,
|
|
}
|
|
|
|
let records = npcs.load::<NpcRecord>(conn)?;
|
|
|
|
let mut loaded_npcs = Vec::new();
|
|
for record in records {
|
|
if let Ok(npc) = serde_json::from_str::<Npc>(&record.data) {
|
|
loaded_npcs.push(npc);
|
|
}
|
|
}
|
|
|
|
let mut db = Self::new();
|
|
db.add_npcs(loaded_npcs);
|
|
Ok(db)
|
|
}
|
|
}
|
|
|
|
impl Default for NpcDatabase {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|