944 lines
26 KiB
Rust
944 lines
26 KiB
Rust
|
use std::{collections::HashMap, path::Path};
|
||
|
|
||
|
use anyhow::Result;
|
||
|
use engine::prelude::cgmath::{Rad, Vector2};
|
||
|
|
||
|
// sql
|
||
|
use rusqlite::{
|
||
|
types::{ToSql, Type},
|
||
|
Connection, Error,
|
||
|
};
|
||
|
|
||
|
use super::mapdata::Coordinate;
|
||
|
|
||
|
struct DBMetaInfo {
|
||
|
name: String,
|
||
|
version: String,
|
||
|
width: u32,
|
||
|
height: u32,
|
||
|
}
|
||
|
|
||
|
pub struct DBTextureInfo {
|
||
|
pub index: u32,
|
||
|
pub name: String,
|
||
|
}
|
||
|
|
||
|
pub struct DBEntityInfo {
|
||
|
pub x_tile: u32,
|
||
|
pub y_tile: u32,
|
||
|
|
||
|
pub world: Vector2<f32>,
|
||
|
pub rotation: Rad<f32>,
|
||
|
|
||
|
pub entity: String,
|
||
|
}
|
||
|
|
||
|
pub struct DBNPCSpawnInfo {
|
||
|
pub x_tile: u32,
|
||
|
pub y_tile: u32,
|
||
|
|
||
|
pub radius: f32,
|
||
|
|
||
|
pub min_count: u32,
|
||
|
pub max_count: u32,
|
||
|
|
||
|
pub normal_npc: String,
|
||
|
pub elite_npc: String,
|
||
|
}
|
||
|
|
||
|
pub struct DBBossSpawnInfo {
|
||
|
pub x_tile: u32,
|
||
|
pub y_tile: u32,
|
||
|
|
||
|
pub boss: String,
|
||
|
}
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
|
||
|
pub struct EntityDBType {
|
||
|
table_name: String,
|
||
|
}
|
||
|
|
||
|
impl EntityDBType {
|
||
|
pub fn entity() -> Self {
|
||
|
Self {
|
||
|
table_name: "entitypositions".to_string(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn spawn_location() -> Self {
|
||
|
Self {
|
||
|
table_name: "spawnlocations".to_string(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn leave_location() -> Self {
|
||
|
Self {
|
||
|
table_name: "leavelocations".to_string(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct MapDBVersions;
|
||
|
|
||
|
#[allow(unused)]
|
||
|
impl MapDBVersions {
|
||
|
const VERSION_0_1_0: &'static str = "0.1.0";
|
||
|
const VERSION_0_1_1: &'static str = "0.1.1";
|
||
|
const VERSION_0_2_0: &'static str = "0.2.0";
|
||
|
}
|
||
|
|
||
|
pub struct MapDataBase {
|
||
|
sql: Connection,
|
||
|
|
||
|
name: String,
|
||
|
version: String,
|
||
|
width: u32,
|
||
|
height: u32,
|
||
|
}
|
||
|
|
||
|
impl MapDataBase {
|
||
|
pub fn new(path: impl AsRef<Path>, name: &str, width: u32, height: u32) -> Result<Self> {
|
||
|
let mut me = Self::open_file(path)?;
|
||
|
|
||
|
me.create_tables()?;
|
||
|
me.init_default_values(name, width, height)?;
|
||
|
|
||
|
me.read_meta_data()?;
|
||
|
|
||
|
Ok(me)
|
||
|
}
|
||
|
|
||
|
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
|
||
|
let mut me = Self::open_file(path)?;
|
||
|
|
||
|
me.check_version()?;
|
||
|
me.read_meta_data()?;
|
||
|
|
||
|
Ok(me)
|
||
|
}
|
||
|
|
||
|
fn open_file(path: impl AsRef<Path>) -> Result<Self> {
|
||
|
Ok(Self {
|
||
|
sql: Connection::open(path)?,
|
||
|
|
||
|
name: Default::default(),
|
||
|
version: Default::default(),
|
||
|
width: 0,
|
||
|
height: 0,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl MapDataBase {
|
||
|
pub fn name(&self) -> &str {
|
||
|
&self.name
|
||
|
}
|
||
|
|
||
|
pub fn width(&self) -> u32 {
|
||
|
self.width
|
||
|
}
|
||
|
|
||
|
pub fn height(&self) -> u32 {
|
||
|
self.height
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl MapDataBase {
|
||
|
fn init_default_values(&self, name: &str, width: u32, height: u32) -> Result<()> {
|
||
|
// meta data
|
||
|
self.sql.execute(
|
||
|
"INSERT INTO meta (name, version, width, height)
|
||
|
VALUES (?1, ?2, ?3, ?4)",
|
||
|
&[
|
||
|
&name.to_string() as &dyn ToSql,
|
||
|
&MapDBVersions::VERSION_0_1_1.to_string() as &dyn ToSql,
|
||
|
&width,
|
||
|
&height,
|
||
|
],
|
||
|
)?;
|
||
|
|
||
|
// tiles
|
||
|
let tile_count = width * height;
|
||
|
let mut insert_tiles = "INSERT INTO tiles (id, texture) VALUES ".to_string();
|
||
|
|
||
|
for i in 0..tile_count {
|
||
|
if i != 0 {
|
||
|
insert_tiles.push(',');
|
||
|
}
|
||
|
|
||
|
insert_tiles.push_str(format!("({}, 1)", i + 1).as_str());
|
||
|
}
|
||
|
|
||
|
self.sql.execute(insert_tiles.as_str(), [])?;
|
||
|
|
||
|
// heights
|
||
|
let height_point_count = (width + 1) * (height + 1);
|
||
|
let mut insert_height_points = "INSERT INTO heights (id, height) VALUES".to_string();
|
||
|
|
||
|
for i in 0..height_point_count {
|
||
|
if i != 0 {
|
||
|
insert_height_points.push(',');
|
||
|
}
|
||
|
|
||
|
insert_height_points.push_str(format!("({}, 0.0)", i + 1).as_str());
|
||
|
}
|
||
|
|
||
|
self.sql.execute(insert_height_points.as_str(), [])?;
|
||
|
|
||
|
// textures
|
||
|
self.sql.execute(
|
||
|
"INSERT INTO textures (id, name) VALUES (1, ?1)",
|
||
|
&[&"grass"],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_tables(&self) -> Result<()> {
|
||
|
// --------------------------------------------------------
|
||
|
// -------- meta information (name, width, height) --------
|
||
|
// --------------------------------------------------------
|
||
|
self.create_meta_table()?;
|
||
|
|
||
|
// --------------------------------------------------------
|
||
|
// --- tiles information (which tile has which texture) ---
|
||
|
// --------------------------------------------------------
|
||
|
self.create_tiles_table()?;
|
||
|
|
||
|
// --------------------------------------------------------
|
||
|
// ------------ textures (name of the texture) ------------
|
||
|
// --------------------------------------------------------
|
||
|
self.create_textures_table()?;
|
||
|
|
||
|
// --------------------------------------------------------
|
||
|
// ----------------------- heights ------------------------
|
||
|
// --------------------------------------------------------
|
||
|
self.create_heights_table()?;
|
||
|
|
||
|
// --------------------------------------------------------
|
||
|
// ------------------- entity positions -------------------
|
||
|
// --------------------------------------------------------
|
||
|
self.create_entity_positions_table()?;
|
||
|
|
||
|
self.create_spawn_table()?;
|
||
|
self.create_leave_table()?;
|
||
|
self.create_mob_spawn_table()?;
|
||
|
self.create_boss_spawn_table()?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_meta_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS meta (
|
||
|
id INTEGER PRIMARY KEY,
|
||
|
name TEXT NOT NULL,
|
||
|
version TEXT NOT NULL,
|
||
|
width INTEGER NOT NULL,
|
||
|
height INTEGER NOT NULL
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM meta", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_tiles_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS tiles (
|
||
|
id INTEGER PRIMARY KEY,
|
||
|
texture INTEGER NOT NULL
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM tiles", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_textures_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS textures (
|
||
|
id INTEGER PRIMARY KEY,
|
||
|
name TEXT NOT NULL
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM textures", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_heights_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS heights (
|
||
|
id INTEGER PRIMARY KEY,
|
||
|
height REAL NOT NULL
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM heights", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_entity_positions_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS entitypositions (
|
||
|
x_tile INTEGER NOT NULL,
|
||
|
y_tile INTEGER NOT NULL,
|
||
|
entity_id TEXT NOT NULL,
|
||
|
x_world REAL NOT NULL,
|
||
|
y_world REAL NOT NULL,
|
||
|
rotation REAL NOT NULL,
|
||
|
primary key (x_tile, y_tile)
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM entitypositions", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_spawn_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS spawnlocations (
|
||
|
x_tile INTEGER NOT NULL,
|
||
|
y_tile INTEGER NOT NULL,
|
||
|
entity_id TEXT NOT NULL,
|
||
|
x_world REAL NOT NULL,
|
||
|
y_world REAL NOT NULL,
|
||
|
rotation REAL NOT NULL,
|
||
|
primary key (x_tile, y_tile)
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM spawnlocations", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_leave_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS leavelocations (
|
||
|
x_tile INTEGER NOT NULL,
|
||
|
y_tile INTEGER NOT NULL,
|
||
|
entity_id TEXT NOT NULL,
|
||
|
x_world REAL NOT NULL,
|
||
|
y_world REAL NOT NULL,
|
||
|
rotation REAL NOT NULL,
|
||
|
primary key (x_tile, y_tile)
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM leavelocations", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_mob_spawn_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS mobspawnlocations (
|
||
|
x_tile INTEGER NOT NULL,
|
||
|
y_tile INTEGER NOT NULL,
|
||
|
radius REAL NOT NULL,
|
||
|
normal_npc TEXT,
|
||
|
elite_npc TEXT,
|
||
|
min_count INTEGER,
|
||
|
max_count INTEGER,
|
||
|
primary key (x_tile, y_tile)
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM mobspawnlocations", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn create_boss_spawn_table(&self) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"CREATE TABLE IF NOT EXISTS bossspawnlocations (
|
||
|
x_tile INTEGER NOT NULL,
|
||
|
y_tile INTEGER NOT NULL,
|
||
|
boss TEXT,
|
||
|
primary key (x_tile, y_tile)
|
||
|
)",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute("DELETE FROM bossspawnlocations", [])?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl MapDataBase {
|
||
|
fn read_meta_data(&mut self) -> Result<()> {
|
||
|
let info = self.sql.query_row(
|
||
|
"SELECT name, version, width, height FROM meta WHERE id=1",
|
||
|
[],
|
||
|
|row| {
|
||
|
Ok(DBMetaInfo {
|
||
|
name: row.get(0)?,
|
||
|
version: row.get(1)?,
|
||
|
width: row.get(2)?,
|
||
|
height: row.get(3)?,
|
||
|
})
|
||
|
},
|
||
|
)?;
|
||
|
|
||
|
self.name = info.name;
|
||
|
self.version = info.version;
|
||
|
self.width = info.width;
|
||
|
self.height = info.height;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn read_tiles(&self) -> Result<HashMap<u32, u32>> {
|
||
|
let mut tiles_stmt = self.sql.prepare("SELECT * FROM tiles")?;
|
||
|
|
||
|
let mapped_tiles = tiles_stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
|
||
|
|
||
|
let mut tiles = HashMap::new();
|
||
|
|
||
|
for row in mapped_tiles {
|
||
|
let (id, tile_id) = row?;
|
||
|
|
||
|
tiles.insert(id, tile_id);
|
||
|
}
|
||
|
|
||
|
Ok(tiles)
|
||
|
}
|
||
|
|
||
|
pub fn read_heights(&self) -> Result<HashMap<u32, f64>> {
|
||
|
let mut height_stmt = self.sql.prepare("SELECT * FROM heights")?;
|
||
|
|
||
|
let mapped_heights = height_stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?;
|
||
|
|
||
|
let mut heights = HashMap::new();
|
||
|
|
||
|
for row in mapped_heights {
|
||
|
let (id, height) = row?;
|
||
|
|
||
|
heights.insert(id, height);
|
||
|
}
|
||
|
|
||
|
Ok(heights)
|
||
|
}
|
||
|
|
||
|
pub fn read_textures(&self) -> Result<Vec<DBTextureInfo>> {
|
||
|
let mut texture_stmt = self.sql.prepare("SELECT * FROM textures")?;
|
||
|
|
||
|
let mapped_textures = texture_stmt.query_map([], |row| {
|
||
|
Ok(DBTextureInfo {
|
||
|
index: row.get(0)?,
|
||
|
name: row.get(1)?,
|
||
|
})
|
||
|
})?;
|
||
|
|
||
|
let mut textures = Vec::new();
|
||
|
|
||
|
for row in mapped_textures {
|
||
|
textures.push(row?);
|
||
|
}
|
||
|
|
||
|
Ok(textures)
|
||
|
}
|
||
|
|
||
|
fn read_ent_pos(&self, table_name: &str) -> Result<Vec<DBEntityInfo>> {
|
||
|
let mut entity_pos_stmt = self.sql.prepare(&format!("SELECT * FROM {}", table_name))?;
|
||
|
|
||
|
let mapped_entity_pos = entity_pos_stmt.query_map([], |row| {
|
||
|
Ok(DBEntityInfo {
|
||
|
x_tile: row.get(0)?,
|
||
|
y_tile: row.get(1)?,
|
||
|
world: Vector2::new(row.get(3)?, row.get(4)?),
|
||
|
rotation: Rad(row.get(5)?),
|
||
|
entity: row.get(2)?,
|
||
|
})
|
||
|
})?;
|
||
|
|
||
|
let mut entity_positions = Vec::new();
|
||
|
|
||
|
for row in mapped_entity_pos {
|
||
|
entity_positions.push(row?);
|
||
|
}
|
||
|
|
||
|
Ok(entity_positions)
|
||
|
}
|
||
|
|
||
|
pub fn read_entities(&self) -> Result<Vec<DBEntityInfo>> {
|
||
|
self.read_ent_pos("entitypositions")
|
||
|
}
|
||
|
|
||
|
pub fn read_spawn_locations(&self) -> Result<Vec<DBEntityInfo>> {
|
||
|
self.read_ent_pos("spawnlocations")
|
||
|
}
|
||
|
|
||
|
pub fn read_leave_locations(&self) -> Result<Vec<DBEntityInfo>> {
|
||
|
self.read_ent_pos("leavelocations")
|
||
|
}
|
||
|
|
||
|
pub fn read_mob_spawn_locations(&self) -> Result<Vec<DBNPCSpawnInfo>> {
|
||
|
let mut entity_pos_stmt = self.sql.prepare("SELECT * FROM mobspawnlocations")?;
|
||
|
|
||
|
let mapped_entity_pos = entity_pos_stmt.query_map([], |row| {
|
||
|
Ok(DBNPCSpawnInfo {
|
||
|
x_tile: row.get(0)?,
|
||
|
y_tile: row.get(1)?,
|
||
|
radius: row.get(2)?,
|
||
|
min_count: row.get(5)?,
|
||
|
max_count: row.get(6)?,
|
||
|
normal_npc: match row.get(3) {
|
||
|
Ok(npc) => npc,
|
||
|
Err(err) => match &err {
|
||
|
Error::InvalidColumnType(_, _, row_type) => match row_type {
|
||
|
Type::Null => String::new(),
|
||
|
|
||
|
_ => return Err(err),
|
||
|
},
|
||
|
|
||
|
_ => return Err(err),
|
||
|
},
|
||
|
},
|
||
|
elite_npc: match row.get(4) {
|
||
|
Ok(npc) => npc,
|
||
|
Err(err) => match &err {
|
||
|
Error::InvalidColumnType(_, _, row_type) => match row_type {
|
||
|
Type::Null => String::new(),
|
||
|
|
||
|
_ => return Err(err),
|
||
|
},
|
||
|
|
||
|
_ => return Err(err),
|
||
|
},
|
||
|
},
|
||
|
})
|
||
|
})?;
|
||
|
|
||
|
let mut entity_positions = Vec::new();
|
||
|
|
||
|
for row in mapped_entity_pos {
|
||
|
entity_positions.push(row?);
|
||
|
}
|
||
|
|
||
|
Ok(entity_positions)
|
||
|
}
|
||
|
|
||
|
pub fn read_boss_spawn_locations(&self) -> Result<Vec<DBBossSpawnInfo>> {
|
||
|
let mut boss_pos_stmt = self.sql.prepare("SELECT * FROM bossspawnlocations")?;
|
||
|
|
||
|
let mapped_boss_pos = boss_pos_stmt.query_map([], |row| {
|
||
|
Ok(DBBossSpawnInfo {
|
||
|
x_tile: row.get(0)?,
|
||
|
y_tile: row.get(1)?,
|
||
|
boss: match row.get(2) {
|
||
|
Ok(boss) => boss,
|
||
|
Err(err) => match &err {
|
||
|
Error::InvalidColumnType(_, _, row_type) => match row_type {
|
||
|
Type::Null => String::new(),
|
||
|
|
||
|
_ => return Err(err),
|
||
|
},
|
||
|
|
||
|
_ => return Err(err),
|
||
|
},
|
||
|
},
|
||
|
})
|
||
|
})?;
|
||
|
|
||
|
let mut boss_positions = Vec::new();
|
||
|
|
||
|
for row in mapped_boss_pos {
|
||
|
boss_positions.push(row?);
|
||
|
}
|
||
|
|
||
|
Ok(boss_positions)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl MapDataBase {
|
||
|
pub fn insert_npc_spawn(
|
||
|
&self,
|
||
|
tile: (u32, u32),
|
||
|
radius: f32,
|
||
|
min_count: u32,
|
||
|
max_count: u32,
|
||
|
) -> Result<()> {
|
||
|
let params: &[&dyn ToSql] = &[&tile.0, &tile.1, &(radius as f64), &min_count, &max_count];
|
||
|
|
||
|
self.sql.execute(
|
||
|
"INSERT INTO mobspawnlocations (x_tile, y_tile, radius, min_count, max_count)
|
||
|
VALUES (?1, ?2, ?3, ?4, ?5)",
|
||
|
params,
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn remove_npc_spawn(&self, tile: (u32, u32)) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"DELETE FROM mobspawnlocations
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&tile.0, &tile.1],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn insert_boss_spawn(&self, tile: (u32, u32)) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"INSERT INTO bossspawnlocations (x_tile, y_tile)
|
||
|
VALUES (?1, ?2)",
|
||
|
[&tile.0, &tile.1],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn remove_boss_spawn(&self, tile: (u32, u32)) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"DELETE FROM bossspawnlocations
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&tile.0, &tile.1],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_boss_name(&self, coordinate: &Coordinate, name: &str) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE bossspawnlocations
|
||
|
SET boss=?3
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&coordinate.x, &coordinate.y, &name as &dyn ToSql],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn insert_entity(
|
||
|
&self,
|
||
|
entity_db_type: EntityDBType,
|
||
|
tile: (u32, u32),
|
||
|
entity_name: String,
|
||
|
position: Vector2<f32>,
|
||
|
rotation: Rad<f32>,
|
||
|
) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
&format!(
|
||
|
"INSERT INTO {} (x_tile, y_tile, entity_id, x_world, y_world, rotation)
|
||
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||
|
entity_db_type.table_name
|
||
|
),
|
||
|
&[
|
||
|
&tile.0,
|
||
|
&tile.1,
|
||
|
&entity_name as &dyn ToSql,
|
||
|
&(position.x as f64),
|
||
|
&(position.y as f64),
|
||
|
&(rotation.0 as f64),
|
||
|
],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn remove_entity(&self, entity_db_type: EntityDBType, tile: (u32, u32)) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
&format!(
|
||
|
"DELETE FROM {}
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
entity_db_type.table_name
|
||
|
),
|
||
|
&[&tile.0, &tile.1],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn set_texture(&self, tile_index: u32, texture_index: u32) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE tiles
|
||
|
SET texture=?1
|
||
|
WHERE id=?2",
|
||
|
&[&texture_index, &tile_index],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn insert_new_texture(&self, texture_index: u32, texture_name: String) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"INSERT INTO textures (id, name)
|
||
|
VALUES (?1, ?2)",
|
||
|
&[&texture_index, &texture_name as &dyn ToSql],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn remove_texture(&self, texture_index: u32) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"DELETE FROM textures
|
||
|
WHERE id=?1",
|
||
|
&[&texture_index],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_texture_index(&self, broken_index: u32, last_index: u32) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE tiles
|
||
|
SET texture=?1
|
||
|
WHERE texture=?2",
|
||
|
&[&broken_index, &last_index],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute(
|
||
|
"UPDATE textures
|
||
|
SET id=?1
|
||
|
WHERE id=?2",
|
||
|
&[&broken_index, &last_index],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_height(&self, index: u32, height: f32) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE heights
|
||
|
SET height=?1
|
||
|
WHERE id=?2",
|
||
|
&[&(height as f64), &index as &dyn ToSql],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_npc_spawn_radius(&self, coordinate: &Coordinate, radius: f32) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE mobspawnlocations
|
||
|
SET radius=?3
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&coordinate.x, &coordinate.y, &(radius as f64) as &dyn ToSql],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_npc_spawn_min_npc_count(
|
||
|
&self,
|
||
|
coordinate: &Coordinate,
|
||
|
min_npc_count: u32,
|
||
|
) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE mobspawnlocations
|
||
|
SET min_count=?3
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&coordinate.x, &coordinate.y, &min_npc_count],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_npc_spawn_max_npc_count(
|
||
|
&self,
|
||
|
coordinate: &Coordinate,
|
||
|
max_npc_count: u32,
|
||
|
) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE mobspawnlocations
|
||
|
SET max_count=?3
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&coordinate.x, &coordinate.y, &max_npc_count],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_npc_spawn_normal_npc(
|
||
|
&self,
|
||
|
coordinate: &Coordinate,
|
||
|
normal_npc: String,
|
||
|
) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE mobspawnlocations
|
||
|
SET normal_npc=?3
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&coordinate.x, &coordinate.y, &normal_npc as &dyn ToSql],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn update_npc_spawn_elite_npc(
|
||
|
&self,
|
||
|
coordinate: &Coordinate,
|
||
|
elite_npc: String,
|
||
|
) -> Result<()> {
|
||
|
self.sql.execute(
|
||
|
"UPDATE mobspawnlocations
|
||
|
SET elite_npc=?3
|
||
|
WHERE x_tile=?1 AND y_tile=?2",
|
||
|
&[&coordinate.x, &coordinate.y, &elite_npc as &dyn ToSql],
|
||
|
)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl MapDataBase {
|
||
|
fn get_version(&self) -> Result<String, rusqlite::Error> {
|
||
|
Ok(
|
||
|
match self
|
||
|
.sql
|
||
|
.query_row("SELECT version FROM meta WHERE id=1", [], |row| row.get(0))
|
||
|
{
|
||
|
Ok(version) => version,
|
||
|
Err(err) => {
|
||
|
// everything before version 0.1.1 did not have a version and can be considered as version 0.1.0
|
||
|
if let Error::SqliteFailure(_sqlite_error, msg) = &err {
|
||
|
if let Some(msg) = msg {
|
||
|
if msg.contains("version") {
|
||
|
return Ok(MapDBVersions::VERSION_0_1_0.to_string());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Err(err);
|
||
|
}
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn check_version(&self) -> Result<()> {
|
||
|
let mut version = self.get_version()?;
|
||
|
|
||
|
// incrementally upgrade to the current version
|
||
|
loop {
|
||
|
match version.as_str() {
|
||
|
MapDBVersions::VERSION_0_1_0 => {
|
||
|
// update mob spawn table
|
||
|
{
|
||
|
// Add new columns to the table
|
||
|
self.sql.execute(
|
||
|
"ALTER TABLE mobspawnlocations
|
||
|
ADD COLUMN radius REAL",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute(
|
||
|
"ALTER TABLE mobspawnlocations
|
||
|
ADD COLUMN normal_npc TEXT",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute(
|
||
|
"ALTER TABLE mobspawnlocations
|
||
|
ADD COLUMN elite_npc TEXT",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute(
|
||
|
"ALTER TABLE mobspawnlocations
|
||
|
ADD COLUMN min_count INTEGER",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
self.sql.execute(
|
||
|
"ALTER TABLE mobspawnlocations
|
||
|
ADD COLUMN max_count INTEGER",
|
||
|
[],
|
||
|
)?;
|
||
|
|
||
|
// create default values that were used at this time
|
||
|
let radius: f32 = 5.0;
|
||
|
let min_count: u32 = 3;
|
||
|
let max_count: u32 = 7;
|
||
|
|
||
|
// fill new data
|
||
|
self.sql.execute(
|
||
|
"UPDATE mobspawnlocations
|
||
|
SET
|
||
|
radius = ?1,
|
||
|
min_count = ?2,
|
||
|
max_count = ?3;",
|
||
|
&[&radius as &dyn ToSql, &min_count, &max_count],
|
||
|
)?;
|
||
|
}
|
||
|
|
||
|
// update meta table, it is possible that version 0.1.0 has no version inside the meta table
|
||
|
// thus the old information is read, the table is recreated and the data is inserted back into it
|
||
|
{
|
||
|
// read old meta data table
|
||
|
let (name, width, height): (String, u32, u32) = self.sql.query_row(
|
||
|
"SELECT name, width, height FROM meta WHERE id=1",
|
||
|
[],
|
||
|
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
||
|
)?;
|
||
|
|
||
|
// remove old meta table
|
||
|
self.sql.execute("DROP TABLE meta", [])?;
|
||
|
|
||
|
// create new meta table
|
||
|
self.create_meta_table()?;
|
||
|
|
||
|
// insert meta information back into table
|
||
|
self.sql.execute(
|
||
|
"INSERT INTO meta (name, version, width, height)
|
||
|
VALUES (?1, ?2, ?3, ?4)",
|
||
|
&[
|
||
|
&name,
|
||
|
&MapDBVersions::VERSION_0_1_1.to_string() as &dyn ToSql,
|
||
|
&width,
|
||
|
&height,
|
||
|
],
|
||
|
)?;
|
||
|
}
|
||
|
|
||
|
// update version string
|
||
|
version = MapDBVersions::VERSION_0_1_1.to_string();
|
||
|
}
|
||
|
|
||
|
MapDBVersions::VERSION_0_1_1 => {
|
||
|
// add boss spawn table
|
||
|
self.create_boss_spawn_table()?;
|
||
|
|
||
|
// update version in meta table
|
||
|
self.sql.execute(
|
||
|
"UPDATE meta
|
||
|
SET
|
||
|
version = ?1;",
|
||
|
[&MapDBVersions::VERSION_0_2_0.to_string() as &dyn ToSql],
|
||
|
)?;
|
||
|
|
||
|
// update version string
|
||
|
version = MapDBVersions::VERSION_0_2_0.to_string();
|
||
|
}
|
||
|
|
||
|
// break at the current version
|
||
|
MapDBVersions::VERSION_0_2_0 => {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
_ => unreachable!("version string {} not known", version),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|