From a75aaa13c75db88b511c61f027cf896685120648 Mon Sep 17 00:00:00 2001 From: Berack96 Date: Sat, 11 May 2024 14:16:29 +0200 Subject: [PATCH] Documenting 03 - added docs for all things - added methods for accessing entity stats - refactored some names --- src/es03_game.rs | 8 -- src/es03_game/cell.rs | 46 +++++++- src/es03_game/config.rs | 35 +++++- src/es03_game/entities.rs | 218 +++++++++++++++++++++++++++++-------- src/es03_game/floor.rs | 27 ++++- src/es03_game/game.rs | 58 ++++++---- src/es03_game/generator.rs | 29 ++++- 7 files changed, 330 insertions(+), 91 deletions(-) diff --git a/src/es03_game.rs b/src/es03_game.rs index 510986b..9e937e1 100644 --- a/src/es03_game.rs +++ b/src/es03_game.rs @@ -39,11 +39,3 @@ pub mod generator; pub mod cell; pub mod config; pub mod entities; - -pub fn start_game() { - let mut game = game::Rogue::new(); - loop { - println!("{}", game); - game.compute_turn(); - } -} \ No newline at end of file diff --git a/src/es03_game/cell.rs b/src/es03_game/cell.rs index a416ea7..334932a 100644 --- a/src/es03_game/cell.rs +++ b/src/es03_game/cell.rs @@ -3,6 +3,10 @@ use dyn_clone::{clone_trait_object, DynClone}; use rand::Rng; use serde::{Deserialize, Serialize}; +/// Rappresentazione di una cella di spazio.\ +/// Essa ha diversi valori in base a cosa si può fare o meno su di essa. +/// Nel caso in cui passi sopra una entià esiste un metodo entity_over che +/// gestisce le varie casistiche. #[derive(Clone, Deserialize, Serialize)] pub enum Cell { Entance, @@ -13,6 +17,12 @@ pub enum Cell { } impl Cell { + /// Data una entità che passa sopra questa cella di spazio + /// modifica la posizione e la fa tornare indietro nel caso sia un muro, + /// nel caso di una cella speciale, applica l'effetto all'entità, + /// e i tutti gli altri casi non fa nulla.\ + /// Il movimento tra piani tramite Exit e Entrance non è gestito in questa funzione + /// data la complessità di muovere l'entità. pub fn entity_over(&mut self, entity: &mut Entity) { match self { Cell::Special(effect) => { @@ -30,13 +40,37 @@ impl Cell { } } +/// Trait che permette di implementare un effetto speciale di una +/// cella di spazio.\ +/// Il trait è taggato con typetag in modo che possa essere utilizzato +/// nella serializzazione e deserializzazione di serde. +/// Esso permette di trasformare le implementazioni di Effect in una +/// spiecie di Enum senza il bisogno di farlo manualmente.\ +/// Quello che viene richiesto è che, nell'implementazione di una +/// struttura concreta di questo trait, venga messo sopra impl X for Effect:\ +/// #\[typetag::serde\]\ +/// \ +/// In questo modo si possono creare molteplici effetti che implementano +/// questo trait senza il bisogno di avere un Enum con essi #[typetag::serde(tag = "type")] pub trait Effect: DynClone { + /// Indica se l'effetto rimane nel terreno dopo la sua applicazione ad una entità.\ + /// Nel caso di true, l'effetto non verrà rimosso dal terreno, + /// eltrimenti la cella dove si trova questo effetto diventerà Empty fn is_persistent(&self) -> bool; + /// Applica l'effetto ad una entità.\ + /// L'effetto può essere di tutto a partire da un danno a qualcosa di più + /// elaborato come una trappola di nemici.\ + /// Tramite l'entità si può anche accedere al piano dove si trova per + /// poter modificare eventualmente qualcosa. fn apply_to(&self, entity: &mut Entity); } clone_trait_object!(Effect); +/// Permette di dare un danno istantaneo a qualunque entità ci passi sopra.\ +/// Una volta utilizzato verrà rimosso dal piano.\ +/// Nel caso in cui il danno sia negativo, l'entità verrà curata +/// (sempre che la sua vita sia un valore positivo e non negativo) #[derive(Clone, Serialize, Deserialize)] pub struct InstantDamage(pub i32); #[typetag::serde] @@ -45,10 +79,14 @@ impl Effect for InstantDamage { false } fn apply_to(&self, entity: &mut Entity) { - entity.health += self.0; + entity.apply_damage(self.0); } } +/// Permettere di infliggere lo stato di confuzione ad una entità.\ +/// Esso ignora il successivo comando che verrà impartito all'entità +/// con una probabilità del 50% e inserirà un movimento in una direzione casuale.\ +/// Come parametro si può passare per quanti turni l'effetto dura #[derive(Clone, Serialize, Deserialize)] pub struct Confusion(pub u8); #[typetag::serde] @@ -71,6 +109,10 @@ impl Effect for Confusion { } } +/// Permette di infliggere un danno nel tempo.\ +/// Similmente a InstantDamage, se il danno è negativo allora il personaggio verrà curato, +/// sempre a patto che la sua vita sia un valore positivo.\ +/// L'effetto dura un determinato numero di turni. #[derive(Clone, Serialize, Deserialize)] pub struct TurnBasedDamage { time: u8, @@ -83,7 +125,7 @@ impl Effect for TurnBasedDamage { } fn apply_to(&self, entity: &mut Entity) { if self.time > 0 { - entity.health += self.damage; + entity.apply_damage(self.damage); entity.add_effect(Box::new(Self { time: self.time - 1, damage: self.damage, diff --git a/src/es03_game/config.rs b/src/es03_game/config.rs index 82c929f..ca6290b 100644 --- a/src/es03_game/config.rs +++ b/src/es03_game/config.rs @@ -5,11 +5,10 @@ use super::{ use serde::{Deserialize, Serialize}; use std::ops::Range; -/** - * Struttura Config usata per definire il gioco, ha alcune cose utili - * TODO sarebbe bello poterle prendere da file. - */ - +/// Struttura di configurazione per la creazione di un dungeon.\ +/// Ogni elemento indica un parametro per la generazione di un piano o di una entitità.\ +/// Esiste una implementazione di default di questa struttura che genera un dungeon +/// molto semplice e standard. #[derive(Clone, Deserialize, Serialize)] pub struct Config { pub game_seed: u64, @@ -19,8 +18,15 @@ pub struct Config { pub effects: Vec, pub entities_total: usize, pub entities: Vec, + pub player_stats: ConfigPlayer, } +/// Un effetto che si può trovare per terra nel dungeon.\ +/// La priorità indica quanto verrà spawnato l'effetto in media.\ +/// \ +/// Es. effetto A priorità 1 ed effetto B con priorità 2\ +/// Se in Config mettiamo 15 effetti per piano, allora avremo +/// in media 10 A e 5 B per ogni piano. #[derive(Clone, Deserialize, Serialize)] pub struct ConfigEffect { pub floors: Range, @@ -28,6 +34,20 @@ pub struct ConfigEffect { pub priority: usize, } +/// Valori di base per le statistiche di un giocatore.\ +/// Esse verranno utilizzate quando un giocatore verrà creato. +#[derive(Clone, Deserialize, Serialize)] +pub struct ConfigPlayer { + pub health: i32, + pub attack: i32, +} + +/// Una entità che si può trovare in un piano nel dungeon.\ +/// La priorità indica quanto verrà spawnata l'entità in media.\ +/// \ +/// Es. entità A priorità 1 ed entità B con priorità 2\ +/// Se in Config mettiamo 15 entità per piano, allora avremo +/// in media 10 A e 5 B per ogni piano. #[derive(Clone, Deserialize, Serialize)] pub struct ConfigEntity { pub floors: Range, @@ -35,6 +55,7 @@ pub struct ConfigEntity { pub decider: Box, pub health: i32, pub attack: i32, + pub priority: usize, } impl Default for Config { @@ -63,6 +84,10 @@ impl Default for Config { effects_total: 45, entities: vec![], entities_total: 0, + player_stats: ConfigPlayer { + health: 1000, + attack: 100, + } } } } diff --git a/src/es03_game/entities.rs b/src/es03_game/entities.rs index 8a0fb21..9e2384d 100644 --- a/src/es03_game/entities.rs +++ b/src/es03_game/entities.rs @@ -3,45 +3,61 @@ use dyn_clone::{clone_trait_object, DynClone}; use rand::Rng; use rand_pcg::Pcg32; use serde::{Deserialize, Serialize}; -use std::{collections::VecDeque, fmt::Display}; +use std::{collections::VecDeque, fmt::Display, mem}; +/// Tupla nominata Position in modo che nel codice sia più chiaro a cosa serve.\ +/// È molto più facile capire a colpo d'occhio Position rispetto a (usize, usize)\ +/// I due valori sono la posizione sull'asse X e sull'asse Y\ +/// Il punto (0,0) si trova in basso a sinista. #[derive(Clone, Copy, Deserialize, Serialize)] pub struct Position(pub usize, pub usize); +/// Indica la direzione dove una entità sta guardando.\ +/// È possibile anche non guardare in nessuna direzione tramite None. #[derive(Clone, Deserialize, Serialize)] pub enum Direction { - UP, - DOWN, - LEFT, - RIGHT, - NONE, + Up, + Down, + Left, + Right, + None, } impl Direction { + /// Inverte la direzione attuale. (es. dx -> sx)\ + /// Questo metodo modifica la direzione inplace. pub fn invert(&mut self) { *self = match *self { - Direction::UP => Direction::DOWN, - Direction::DOWN => Direction::UP, - Direction::RIGHT => Direction::LEFT, - Direction::LEFT => Direction::RIGHT, - _ => Direction::NONE, + Direction::Up => Direction::Down, + Direction::Down => Direction::Up, + Direction::Right => Direction::Left, + Direction::Left => Direction::Right, + _ => Direction::None, }; } + /// Calcola la nuova posizione in base a dove si stà guardando.\ + /// La posizione viene modificata come se si stesse avanzando di una + /// unità di spazio.\ + /// Es. (0,0) Up -> aumento la y di uno (0,1) pub fn move_from(&self, pos: Position) -> Position { match *self { - Direction::UP => Position(pos.0, pos.1 + 1), - Direction::DOWN => Position(pos.0, pos.1 - 1), - Direction::RIGHT => Position(pos.0 + 1, pos.1), - Direction::LEFT => Position(pos.0 - 1, pos.1), - Direction::NONE => pos, + Direction::Up => Position(pos.0, pos.1 + 1), + Direction::Down => Position(pos.0, pos.1 - 1), + Direction::Right => Position(pos.0 + 1, pos.1), + Direction::Left => Position(pos.0 - 1, pos.1), + Direction::None => pos, } } + /// Restituisce una direzione casuale a partire da un generatore.\ + /// La direzione viene generata con una distribuzione uniforme, ovvero non + /// c'è una direzione preferita o con più probabilità. pub fn random(rng: &mut Pcg32) -> Self { - match rng.gen_range(0..4) { - 0 => Direction::UP, - 1 => Direction::DOWN, - 2 => Direction::LEFT, - _ => Direction::RIGHT, + match rng.gen_range(0..5) { + 0 => Direction::Up, + 1 => Direction::Down, + 2 => Direction::Left, + 3 => Direction::Right, + _ => Direction::None, } } } @@ -49,17 +65,18 @@ impl Direction { impl Display for Direction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let c = match self { - Self::UP => '▲', - Self::DOWN => '▼', - Self::LEFT => '◄', - Self::RIGHT => '►', - Self::NONE => '■', + Self::Up => '▲', + Self::Down => '▼', + Self::Left => '◄', + Self::Right => '►', + Self::None => '■', }; write!(f, "{}", c) } } +/// Rappresenta una entità all'interno del dungeon. #[derive(Clone, Deserialize, Serialize)] pub struct Entity { name: String, @@ -69,40 +86,98 @@ pub struct Entity { pub buffer: Action, pub position: Position, pub direction: Direction, - pub health: i32, + health_max: i32, + health: i32, attack: i32, } impl Entity { - pub fn new(name: String, decider: Box, mut floor: FloorPtr) -> Self { + /// Costruttore che crea una nuova entità a partire dal suo nome, vita, danno di attacco, + /// il decisore che permette di muoversi (giocatore o IA) e il piano in cui si trova.\ + /// La posizione sarà all'entrata del piano (o in una cella vicina nel caso ci siano altre entità sopra), + /// non avrà effetti, azioni o una direzione in particolare. + pub fn new( + name: String, + health: i32, + attack: i32, + decider: Box, + mut floor: FloorPtr, + ) -> Self { let position = floor.get().get_entrance(); Self { name, floor, decider, position, + attack, + health, + health_max: health, buffer: Action::DoNothing, effects: VecDeque::new(), - direction: Direction::NONE, - attack: 100, - health: 100, + direction: Direction::None, } } + + /// Aggiunge l'effetto passato in input all'entità.\ + /// Questo non viene calcolato immediatamente, ma solo quando si chiama la + /// funzione update.\ + /// È stato fatto in questo modo dato che ci possono essere effetti che ne aggiungono altri + /// e quindi si farebbe una ricorsione infinita. pub fn add_effect(&mut self, effect: Box) { self.effects.push_back(effect); } + + /// Indica se l'entità è considerata ancora in gioco o meno.\ + /// Per far si che l'entità non sia più in gioco bisobna far arrivare la vita a 0. + /// Nota: una entità con vita negativa è considerata "viva" + pub fn is_alive(&self) -> bool { + self.health != 0 + } + + /// Restituisce il valore della vita dell'entità.\ + pub fn get_health(&self) -> i32 { + self.health + } + + /// Applica il valore inserito come danno alla vita.\ + /// Nel caso in cui il danno sia negativo allora verrà interpretato come cura.\ + /// Nel caso in cui la vita sia negativa la logica sarà inversa.\ + /// Il danno/cura non potrà comunque superare lo 0 o la vita massima. + pub fn apply_damage(&mut self, damage: i32) { + let health = self.health - damage; + self.health = if self.health_max > 0 { + health.min(self.health_max).max(0) + } else { + health.max(self.health_max).min(0) + }; + } + + /// Restituisce il piano in cui si trova l'entità in questo momento. pub fn get_floor(&self) -> FloorPtr { self.floor.clone() } + + /// Modifica il piano dell'entità e la mette all'entrata di quello nuovo. pub fn set_floor(&mut self, floor: FloorPtr) { self.floor = floor; self.position = self.floor.get().get_entrance(); } - pub fn update(&mut self) { - self.compute_action(); - self.compute_effects(); + + /// Permette all'entità di fare un'azione e successivamente calcola + /// tutti gli effetti che devono essere applicati ad essa.\ + /// Nel caso in cui l'entità non sia più in vita questo metodo ritornerà false + /// e non permetterà all'entità di fare un update.\ + /// Nel caso in cui l'entità non riesca a fare l'update viene ritornato false.\ + /// Cio significa che l'entità verrà rimossa dal gioco. + pub fn update(&mut self) -> bool { + if self.is_alive() && matches!(self.compute_action(), Some(_)) { + self.compute_effects(); + return true; + } + false } + /// calcola gli effetti e li applica all'entità. fn compute_effects(&mut self) { let total = self.effects.len(); // len could change for _ in 0..total { @@ -111,24 +186,52 @@ impl Entity { } } } - fn compute_action(&mut self) { - let action = self.decider.get_next_action(); - match self.buffer { - Action::DoNothing => action.apply(self), - _ => (), - } - self.buffer = Action::DoNothing; + /// prende una decisione e applica l'azione da fare + /// L'azione compiuta viene restituita, altrimenti None + fn compute_action(&mut self) -> Option { + let action = self.decider.get_next_action()?; + let action = match self.buffer { + Action::DoNothing => action, + _ => mem::replace(&mut self.buffer, Action::DoNothing), + }; + + let result = Some(action.clone()); + action.apply(self); + result + } + + /// Metodo statico per l'update e l'eventuale eliminazione di entità da un vettore. + /// Le entità rimosse sono quelle che non riescono a fare l'update o che eventualmente + /// non sono più in vita + pub fn update_from_vec(entities: &mut Vec) { + let to_remove: Vec = entities + .iter_mut() + .enumerate() + .filter_map(|(i, entity)| if entity.update() { Some(i) } else { None }) + .rev() + .collect(); + to_remove.iter().for_each(|i| { + entities.remove(*i); + }); } } +/// Azione che una qualsiasi entità può fare. +/// L'azione DoNothing permette all'entità di saltare il turno nel caso in cui sia utile. #[derive(Clone, Deserialize, Serialize)] pub enum Action { Move(Direction), - // attack + //Attack(Direction), DoNothing, } impl Action { + /// Applica l'azione all'entità passata.\ + /// Dopo la chiamata di questa funzione l'azione non sarà più disponibile.\ + /// Per ogni tipo di azione l'entità viene modificata opportunamente.\ + /// \ + /// Es. Move(Up) sposterà l'entità da una posizione (x,y) -> (x,y+1)\ + /// e applicherà qualunque effetto che si trovi sulla cella di destinazione pub fn apply(self, entity: &mut Entity) { match self { Action::DoNothing => {} @@ -146,17 +249,40 @@ impl Action { } } -clone_trait_object!(Decider); +/// Questo trait è molto importante per le entità perchè è responsabile del loro comportamento.\ +/// Con questo trait si possono creare diversi comportamenti semplicemente implementandolo +/// e utilizzandolo come parametro nella generazione di una entità.\ +/// \ +/// Il trait è taggato con typetag in modo che possa essere utilizzato +/// nella serializzazione e deserializzazione di serde. +/// Esso permette di trasformare le implementazioni di Decider in una +/// spiecie di Enum senza il bisogno di farlo manualmente.\ +/// Quello che viene richiesto è che, nell'implementazione di una +/// struttura concreta di questo trait, venga messo sopra impl X for Decider:\ +/// #\[typetag::serde\]\ +/// \ +/// In questo modo si possono creare molteplici comoprtamenti che implementano +/// questo trait senza il bisogno di avere un Enum con essi #[typetag::serde(tag = "type")] pub trait Decider: DynClone { - fn get_next_action(&self) -> Action; + /// Genera una azione che poi verrà usata per l'entità associata a questo Decider.\ + /// L'azione può essere generata in qualunque modo: casuale, sempre la stessa, + /// tramite interazione con console, o tramite una connessione ad un client.\ + /// \ + /// Nel caso in cui venga restituito None come valore, l'entità verrà rimossa dal gioco.\ + /// Questo viene fatto in modo che si possa avere una possibilità di rimozione del giocatore, + /// ma anche una possibilità che alcune entità rare possano sparire. + fn get_next_action(&self) -> Option; } +clone_trait_object!(Decider); +/// Semplice implementazione di un possibile comportamento di una entità.\ +/// In questo caso l'entità resterà immobile nel punto in cui si trova per sempre. #[derive(Clone, Serialize, Deserialize)] pub struct Immovable; #[typetag::serde] impl Decider for Immovable { - fn get_next_action(&self) -> Action { - Action::DoNothing + fn get_next_action(&self) -> Option { + Some(Action::DoNothing) } } diff --git a/src/es03_game/floor.rs b/src/es03_game/floor.rs index ef4ddb3..6090c9a 100644 --- a/src/es03_game/floor.rs +++ b/src/es03_game/floor.rs @@ -9,19 +9,30 @@ use std::{ rc::Rc, }; +/// Tupla creata per poter implementare qualche metodo sulla struttura Rc>\ +/// In questo modo ho incapsulato i borrow e la creazione di questo oggetto per una +/// migliore lettura del codice (hopefully). #[derive(Clone, Deserialize, Serialize)] pub struct FloorPtr(Rc>); impl FloorPtr { + /// Crea un nuovo puntatore al piano indicato.\ + /// Il piano viene creato a partire dai parametri passati in input, che sono tutte cose + /// necessarie ad esso. pub fn new(level: usize, rng: Pcg32, entities: Vec, grid: Vec>) -> Self { Self(Rc::new(RefCell::new(Floor::new( level, rng, entities, grid, )))) } + + /// Permette di prendere il valore puntato al piano. pub fn get(&mut self) -> RefMut { self.0.borrow_mut() } } +/// Indica un piano del dungeon, in essa si possono trovare le celle in cui si +/// cammina e le entità che abitano il piano.\ +/// Per poter accedere a questa struttura è necessario utilizzare FloorPtr e fare get() #[derive(Clone, Deserialize, Serialize)] pub struct Floor { level: usize, @@ -31,6 +42,7 @@ pub struct Floor { } impl Floor { + /// Crea un piano secondo i parametri indicati fn new(level: usize, rng: Pcg32, entities: Vec, grid: Vec>) -> Self { Self { level, @@ -40,15 +52,25 @@ impl Floor { } } + /// Restituisce il livello di profondità del piano pub fn get_level(&self) -> usize { self.level } + + /// Restituisce il generatore di numeri casuali utilizzato per qualunque cosa + /// inerente al piano (generazione di entità, applicazione di effetti...) pub fn get_rng(&mut self) -> &mut Pcg32 { &mut self.rng } + + /// Restituisce la cella nella posizione indicata.\ + /// Con essa si può fare cio che si vuole, e quindi anche modificarla. pub fn get_cell(&mut self, pos: Position) -> &mut Cell { &mut self.grid[pos.0][pos.1] } + + /// Restituisce la posizione dell'entrata del piano.\ + /// Utile come spawn per quando i giocatori arrivano al piano. pub fn get_entrance(&mut self) -> Position { self.grid .iter() @@ -65,9 +87,8 @@ impl Floor { .expect("Entrance of the floor should be inside the grid!") } + /// Fa l'update di tutte le entità e rimuove eventualmente quelle non più in vita pub fn update_entities(&mut self) { - for entity in &mut self.entities { - entity.update(); - } + Entity::update_from_vec(&mut self.entities); } } diff --git a/src/es03_game/game.rs b/src/es03_game/game.rs index 25a0a62..cb67cc2 100644 --- a/src/es03_game/game.rs +++ b/src/es03_game/game.rs @@ -8,29 +8,25 @@ use super::{ use rand::{RngCore, SeedableRng}; use rand_pcg::Pcg32; use serde::{Deserialize, Serialize}; -use std::fmt::Display; -/** - * Struttura del gioco generico che implementa un RogueLike. - */ +/// 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 Rogue { +pub struct Dungeon { floors: Vec, rng: Pcg32, config: Config, players: Vec, } -impl Display for Rogue { - fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() - } -} - -impl Rogue { +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), @@ -41,33 +37,41 @@ impl Rogue { game.build_next_floor(); game } + + /// 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) { let floor = self.floors[0].clone(); let decider = Box::new(Immovable); - let entity = Entity::new(name, decider, floor); + let stats = &self.config.player_stats; + let entity = Entity::new(name, stats.health, stats.attack, decider, floor); self.players.push(entity); } + + /// 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) -> FloorPtr { let floors = self.floors.len() - 1; let index = level.min(floors); self.floors[index].clone() } - pub 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); - } + + /// Funzione principale del dungeon.\ + /// In essa viene fatto fare l'update ai giocatori e ad ogni piano. + /// In generale l'algoritmo è il seguente:\ + /// - 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 tutti i piani in cui c'è almeno un giocatore + /// - Modifica di piano di eventuali giocatori pub fn compute_turn(&mut self) { let mut update_floors = vec![false; self.floors.len()]; let mut change_floors = vec![0; self.players.len()]; + Entity::update_from_vec(&mut self.players); self.players.iter_mut().enumerate().for_each(|(i, player)| { let mut floor = player.get_floor(); let mut floor = floor.get(); - - player.update(); update_floors[floor.get_level()] = true; if let Cell::Exit = floor.get_cell(player.position) { change_floors[i] = floor.get_level() + 1; @@ -89,6 +93,16 @@ impl Rogue { player.set_floor(floor); }); } + + /// 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); + } + /// restituisce il piano indicato o ne crea uno nuovo se il livello è troppo profondo fn get_floor_or_build(&mut self, level: usize) -> FloorPtr { let mut level = level; if level > self.floors.len() { diff --git a/src/es03_game/generator.rs b/src/es03_game/generator.rs index 33db08b..e357a0b 100644 --- a/src/es03_game/generator.rs +++ b/src/es03_game/generator.rs @@ -7,6 +7,12 @@ use rand::{Rng, SeedableRng}; use rand_pcg::Pcg32; use std::ops::Range; +/// Generatore del gioco che può creare dei piani del dungeon. +/// Idealmente questo generatore si comporta come il pattern Factory. +/// Per far si che funzioni ha bisongo di un seed per la generazione del piano +/// verrà utilizzato poi dal piano stesso per eventuali altri calcoli. +/// Inoltre ad esso viene passato una struttura di config che permette +/// di scegliere meglio come poter generare il piano. pub struct Generator { pub rng: Pcg32, pub level: usize, @@ -20,6 +26,8 @@ pub struct Generator { } impl Generator { + /// Costruttore standard di un generatore + /// il generatore creato avrà come entità quelle registrate nella configurazione pub fn new(floor_seed: u64, floor_level: usize, config: &Config) -> Self { let mut rand_pcg = Pcg32::seed_from_u64(floor_seed); let floor_size = rand_pcg.gen_range(config.floor_size.clone()); @@ -29,17 +37,19 @@ impl Generator { size: floor_size, size_rooms: config.room_size.clone(), effects_total: config.effects_total, - effects: Self::clone_vec_filter(&config.effects, |val| { + effects: Self::vec_filter(&config.effects, |val| { val.floors.contains(&floor_level) }), entities_total: config.entities_total, - entities: Self::clone_vec_filter(&config.entities, |val| { + entities: Self::vec_filter(&config.entities, |val| { val.floors.contains(&floor_level) }), grid: Self::grid_with_only(floor_size, Cell::Wall), } } + /// Questo metodo è il più semplice per la generazione del piano.\ + /// Crea una enorme stanza con dei muri attorno e piazza tutti gli effetti. pub fn build_floor(mut self) -> FloorPtr { for x in 1..(self.size - 1) { for y in 1..(self.size - 1) { @@ -51,15 +61,19 @@ impl Generator { self.rand_place_effects(); FloorPtr::new(self.level, self.rng, vec![], self.grid) } + + /// TODO pub fn build_floor_catacomb(mut self) -> Floor { // init to WALLS // reserve some cells for rooms ?? todo!() } + /// TODO fn build_rooms(&mut self) { todo!() } + /// crea una stanza in un punto casuale del piano e restituisce il suo boundingbox fn rand_build_room(&mut self) -> (usize, usize, usize, usize) { loop { let (x, y) = self.rand_2d(0..self.size); @@ -76,7 +90,8 @@ impl Generator { } } } - + /// piazza gli effetti della confgurazione in modo casuale su tutto il piano.\ + /// essi vengono piazzati solamente sulle celle Empty fn rand_place_effects(&mut self) { for _ in 0..self.effects_total { let index = self.rng.gen_range(0..self.effects.len()); @@ -85,6 +100,8 @@ impl Generator { self.rand_place_cell(cell); } } + /// piazza una cella in un punto casuale del piano.\ + /// il metodo contiuna a provare a piazzare la cella finche non trova una cella Empty. fn rand_place_cell(&mut self, cell: Cell) -> (usize, usize) { loop { let (x, y) = self.rand_2d(0..self.size); @@ -94,19 +111,21 @@ impl Generator { } } } + /// genera una tupla di due valori randomici nel range passato in input fn rand_2d(&mut self, range: Range) -> (usize, usize) { let x = self.rng.gen_range(range.clone()); let y = self.rng.gen_range(range); (x, y) } - - fn clone_vec_filter(original: &Vec, filter: impl Fn(&T) -> bool) -> Vec { + /// crea una vista del vettore passato in input dopo aver applicato la funzione di filtro + fn vec_filter(original: &Vec, filter: impl Fn(&T) -> bool) -> Vec { original .clone() .into_iter() .filter_map(|val| if filter(&val) { Some(val) } else { None }) .collect() } + /// crea un campo con solamente la cella specificata clonata su tutto di esso fn grid_with_only(size: usize, cell: Cell) -> Vec> { vec![vec![cell; size]; size] }