122 lines
4.4 KiB
Rust
122 lines
4.4 KiB
Rust
use super::{
|
|
config::Config,
|
|
entities::{Behavior, Entity},
|
|
floor::Floor,
|
|
generator::Generator,
|
|
};
|
|
use rand::{RngCore, SeedableRng};
|
|
use rand_pcg::Pcg32;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::{
|
|
fs::File,
|
|
io::{self, BufReader, BufWriter},
|
|
};
|
|
|
|
/// Rappresenta un Dungeon in stile RogueLike.\
|
|
/// In esso possiamo trovare dei piani generati casualmente
|
|
/// e dei giocatori che esplorano.
|
|
#[derive(Clone, Deserialize, Serialize)]
|
|
pub struct Dungeon {
|
|
floors: Vec<Floor>,
|
|
config: Config,
|
|
rng: Pcg32,
|
|
}
|
|
|
|
impl Dungeon {
|
|
/// Crea una nuova istanza di un dungeon con le configurazioni i default
|
|
pub fn new() -> Self {
|
|
Self::new_with(Config::default())
|
|
}
|
|
|
|
/// Crea una nuova istanza di un dungeon con le configurazioni passate in input
|
|
pub fn new_with(config: Config) -> Self {
|
|
let mut game = Self {
|
|
rng: Pcg32::seed_from_u64(config.game_seed),
|
|
floors: vec![],
|
|
config,
|
|
};
|
|
game.build_next_floor();
|
|
game
|
|
}
|
|
|
|
/// Carica il dungeon da un file.\
|
|
/// Il file deve essere formattato tramite json, altrimenti viene ritornato un errore.
|
|
pub fn load(filename: &str) -> io::Result<Self> {
|
|
let file = File::open(filename)?;
|
|
let reader = BufReader::new(file);
|
|
let dungeon: Self = serde_json::from_reader(reader)?;
|
|
Ok(dungeon)
|
|
}
|
|
|
|
/// Salva il dungeon corrente nel file indicato.\
|
|
/// Il salvataggio viene fatto tramite serializzazione JSON in modo che sia facile da vedere.\
|
|
/// Nel caso in cui ci siano problemi con I/O, viene ritornato un errore.
|
|
pub fn save(&mut self, filename: &str) -> io::Result<()> {
|
|
let file = File::create(filename)?;
|
|
let writer = BufWriter::new(file);
|
|
let _ = serde_json::to_writer_pretty(writer, self)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Aggiunge un giocatore al Dungeon, esso avrà le statistiche di base assegnate
|
|
/// ad esso tramite la configurazione indicata nel costruttore.\
|
|
/// Il giocatore appena inserito si troverà al piano 0.
|
|
pub fn add_player(&mut self, name: String, decider: Box<dyn Behavior>) {
|
|
let stats = &self.config.player_stats;
|
|
let player = Entity::new(name, stats.health, stats.attack, decider);
|
|
self.floors[0].add_player(player);
|
|
}
|
|
|
|
/// Indica se nel dungeon ci sono dei giocatori.\
|
|
/// Metodo utile, dato che nel caso in cui non ci siano, il dungen non verrà modificato
|
|
/// siccome per calcolare il turno successivo ho bisogno di giocatori.
|
|
pub fn has_players(&mut self) -> bool {
|
|
self.floors.iter().any(|floor| floor.has_players())
|
|
}
|
|
|
|
/// Restituisce il piano indicato dal livello di profondità.\
|
|
/// Nel caso il livello non esista, restituisce il piano con profondità maggiore.
|
|
pub fn get_floor(&self, level: usize) -> &Floor {
|
|
let floors = self.floors.len() - 1;
|
|
let index = level.min(floors);
|
|
&self.floors[index]
|
|
}
|
|
|
|
/// Funzione principale del dungeon.\
|
|
/// In essa viene fatto fare l'update ai giocatori e ad ogni piano.
|
|
/// In generale l'algoritmo è il seguente per ogni piano in cui si trova un giocatore:\
|
|
/// - I giocatori fanno le loro mosse.\
|
|
/// - Se un giocatore non è più in vita o non può indicare l'azione da fare, viene rimosso
|
|
/// - Update di tutte le entità del piano
|
|
/// - Modifica di piano di eventuali giocatori
|
|
pub fn compute_turn(&mut self) {
|
|
let moved: Option<Vec<Entity>> = self.floors.iter_mut().fold(None, |moved, floor| {
|
|
let removed = floor.has_players().then(|| {
|
|
let removed = floor.update_players();
|
|
floor.update_entities();
|
|
removed
|
|
});
|
|
if let Some(mut moved) = moved {
|
|
moved.drain(..).for_each(|player| floor.add_player(player));
|
|
}
|
|
removed
|
|
});
|
|
|
|
if let Some(mut moved) = moved {
|
|
self.build_next_floor();
|
|
let len = self.floors.len();
|
|
let floor = &mut self.floors[len - 1];
|
|
moved.drain(..).for_each(|player| floor.add_player(player));
|
|
}
|
|
}
|
|
|
|
/// permette di costruire il piano successivo
|
|
fn build_next_floor(&mut self) {
|
|
let floor_seed = self.rng.next_u64();
|
|
let floor_level = self.floors.len();
|
|
let generator = Generator::new(floor_seed, floor_level, &self.config);
|
|
let floor = generator.build_floor();
|
|
self.floors.push(floor);
|
|
}
|
|
}
|