use super::{ cell::Cell, entities::{Entity, Position}, }; use rand_pcg::Pcg32; use serde::{Deserialize, Serialize}; use std::{collections::VecDeque, fmt::Display}; /// 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, grid: Vec>, players: VecDeque, entities: VecDeque, rng: Pcg32, } impl Floor { /// Crea un nuovo piano al livello 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 { level, rng, players: VecDeque::new(), entities: VecDeque::from(entities), grid, } } /// Aggiunge un giocatore al piano e lo inserisce all'entrata. pub fn add_player(&mut self, mut player: Entity) { // todo!() check collision with other entities player.position = self.get_entrance(); self.players.push_back(player); } /// Indica se il piano ha almeno un giocatore in vita o meno pub fn has_players(&self) -> bool { self.players.iter().any(|player| player.is_alive()) } /// 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.\ /// Nel caso in cui la posizione non sia all'interno del piano, essa viene modificata /// facendola rientrare nei limiti di esso.\ /// Es. pos(2,3) ma il piano è di max 2 allora diventa -> pos(2,2) pub fn get_cell(&mut self, pos: &mut Position) -> &mut Cell { let len = self.grid.len() - 1; pos.0 = pos.0.min(len); pos.1 = pos.1.min(len); &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() .enumerate() .find_map(|(x, vec)| { vec.iter().enumerate().find_map(|(y, cell)| { if let Cell::Entance = cell { Some(Position(x, y)) } else { None } }) }) .expect("Entrance of the floor should be inside the grid!") } /// Fa l'update di tutti i giocatori e rimuove eventualmente quelli non più in vita, restituendoli dentro un vec pub fn update_players(&mut self) -> Vec { let mut remove = vec![]; for _ in 0..self.players.len() { let mut player = self.players.pop_front().unwrap(); if player.update(self) { self.players.push_back(player); } else { remove.push(player); } } remove } /// Fa l'update di tutte le entità e rimuove eventualmente quelle non più in vita pub fn update_entities(&mut self) { for _ in 0..self.entities.len() { let mut entity = self.entities.pop_front().unwrap(); if entity.update(self) { self.entities.push_back(entity); } } } /// Crea una view del piano con l'entità partecipante all'update. pub fn get_limited_view_floor<'a>(&'a self, entity: &'a Entity) -> FloorView<'a> { FloorView::new(self, entity) } } /// Struttura di mezzo tra un piano e il gioco vero e proprio.\ /// Utilizzata per la comunicazione con le entità per poter aggiornare quello che vedono.\ /// Infatti internamente ha solo alcuni pezzi del gioco per non far mostrare tutto.\ pub struct FloorView<'a> { pub entity: &'a Entity, pub floor: &'a Floor, } impl<'a> FloorView<'a> { /// Crea una vista del gioco corrente secondo la visione dell'entità passata in intput.\ /// Il SimpleFloor risultante avrà il piano, entità, livello e giocatori che si trovano /// in questo momento sul piano dell'entità passata in input. pub fn new(floor: &'a Floor, entity: &'a Entity) -> Self { Self { entity: &entity, floor: &floor, } } /// Rappresentazione del piano come matrice di char pub fn as_char_grid(&self) -> Vec> { let grid = &self.floor.grid; let size = grid.len(); let mut grid = (0..size) .map(|y| { (0..size) .flat_map(|x| { let cell = &grid[x][y]; let ch = cell.as_char(); match cell { Cell::Wall => [ch, ch, ch], _ => [' ', ch, ' '], } }) .chain(std::iter::once('\n')) .collect::>() }) .collect::>(); let pos = &self.entity.position; grid[pos.1][pos.0 * 3 + 1] = self.entity.direction.as_char(); grid } } impl<'a> Display for FloorView<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let grid: String = self .as_char_grid() .iter() .rev() .map(|row| row.iter().collect::()) .collect(); write!(f, "{}\n{}", grid, self.entity) } }