1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use regex::Regex;

use crate::prelude::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Character {
    pub game_version: String,
    pub actor: ActorParams,
    pub items: Vec<ItemParams>,
    pub active_missions: Vec<String>,
    pub completed_missions: Vec<String>,
    pub chapter_index: usize,
    pub map_id: String,
    pub is_permadeath: bool,
}

impl Character {
    pub fn with_map(self, chapter_index: usize, map_id: &str) -> Self {
        Character {
            chapter_index,
            map_id: map_id.to_string(),
            ..self
        }
    }

    #[cfg(not(any(target_family = "wasm", target_os = "android")))]
    pub fn save(&self) -> Result<()> {
        let json = serde_json::to_string_pretty(self)?;
        let path = character_name_to_path(&self.actor.name);
        fs::write(path, json)?;

        Ok(())
    }

    #[cfg(target_family = "wasm")]
    pub fn save(&self) -> Result<()> {
        let game_params = storage::get::<GameParams>();
        let json = serde_json::to_string_pretty(self)?;
        let mut storage = quad_storage::STORAGE.lock().unwrap();
        storage.set(&format!("{}_character", game_params.name), &json);

        Ok(())
    }

    pub(crate) fn spawn(&self, game_state: Handle<GameState>, position: Vec2) {
        let player = storage::get::<LocalPlayer>();
        let mut actor = Actor::from_saved(
            game_state,
            position,
            ActorControllerKind::local_player(&player.id),
            self,
        );

        actor.stats.recalculate_derived();
        actor.stats.restore_vitals();

        scene::add_node(actor);
    }
}

pub fn character_name_to_path(name: &str) -> PathBuf {
    let game_params = storage::get::<GameParams>();
    Path::new(&game_params.characters_path).join(&format!("{}.json", name))
}

#[cfg(not(any(target_family = "wasm", target_os = "android")))]
pub fn get_available_characters<P: AsRef<Path>>(path: P) -> Result<Vec<Character>> {
    let regex = Regex::new(r".json$")?;
    let mut res = Vec::new();
    for entry in (fs::read_dir(path)?).flatten() {
        if regex.is_match(&entry.path().to_string_lossy().to_string()) {
            let character = load_character(entry.path())?;
            res.push(character);
        }
    }

    Ok(res)
}

#[cfg(not(any(target_family = "wasm", target_os = "android")))]
pub fn load_character<P: AsRef<Path>>(path: P) -> Result<Character> {
    let json = fs::read(path)?;
    let character: Character = serde_json::from_slice(&json)?;
    Ok(character)
}

#[cfg(target_family = "wasm")]
pub fn get_available_characters(_: &str) -> io::Result<Vec<Character>> {
    let game_params = storage::get::<GameParams>();
    let storage = &mut quad_storage::STORAGE.lock().unwrap();
    let save_name = format!("{}_character", game_params.game_name);
    if let Some(json) = storage.get(&save_name) {
        let character: Character = serde_json::from_str(&json)?;
        return Ok(vec![character]);
    }

    Ok(Vec::new())
}

#[cfg(not(any(target_family = "wasm", target_os = "android")))]
pub fn delete_character(name: &str) -> Result<()> {
    let path = character_name_to_path(name);
    fs::remove_file(path)?;

    Ok(())
}

#[cfg(target_family = "wasm")]
pub fn delete_character(name: &str) -> Result<()> {
    let game_params = storage::get::<GameParams>();
    let storage = &mut quad_storage::STORAGE.lock().unwrap();
    let save_name = format!("{}_character", game_params.game_name);
    storage.remove(save_name);

    Ok(())
}