diff --git a/src/es03_game/entities.rs b/src/es03_game/entities.rs index 5ccc681..b23647b 100644 --- a/src/es03_game/entities.rs +++ b/src/es03_game/entities.rs @@ -159,12 +159,24 @@ impl Entity { /// 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, floor: &mut Floor) -> bool { - self.behavior.update(floor.get_limited_view_floor(self)); - if self.is_alive() && matches!(self.compute_action(floor), Some(_)) { - self.compute_effects(floor); - return true; + if !self.is_alive() { + self.behavior.you_died(floor.get_limited_view_floor(self)); + return false; } - false + + self.behavior.update(floor.get_limited_view_floor(self)); + let action = self.compute_action(floor); + if action.is_none() { + return false; + } + + if !self.is_alive() { + self.behavior.you_died(floor.get_limited_view_floor(self)); + return false; + } + + self.compute_effects(floor); + true } /// calcola gli effetti e li applica all'entità. @@ -231,7 +243,7 @@ impl Action { direction.move_from(&mut entity.position); entity.direction = direction; - let cell = floor.get_cell(&mut entity.position); + let cell = floor.get_cell(&entity.position); cell.entity_over(entity); } } diff --git a/src/es03_game/floor.rs b/src/es03_game/floor.rs index b1bf9a9..062c0fc 100644 --- a/src/es03_game/floor.rs +++ b/src/es03_game/floor.rs @@ -68,11 +68,11 @@ impl Floor { /// 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 { + pub fn get_cell(&mut self, pos: &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] + let x = pos.0.min(len); + let y = pos.1.min(len); + &mut self.grid[x][y] } /// Restituisce la posizione dell'entrata del piano.\ @@ -95,16 +95,39 @@ impl Floor { /// 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![]; + let mut next_floor = 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); + next_floor.push(player); } } - remove + next_floor + } + + /// Ritorna un eventuale giocatore che si trova sopra la cella di uscita del piano.\ + /// Nel caso in cui non ci siano giocatori sopra, questo metodo ritornerà None. + pub fn get_player_at_exit(&mut self) -> Option { + let index = self + .players + .iter() + .enumerate() + .filter_map(|(i, player)| { + let pos = &player.position; + match &self.grid[pos.0][pos.1] { + Cell::Exit => Some(i), + _ => None, + } + }) + .next(); + + if let Some(i) = index { + self.players.remove(i) + } else { + None + } } /// Fa l'update di tutte le entità e rimuove eventualmente quelle non più in vita @@ -218,27 +241,6 @@ impl<'a> FloorView<'a> { }) .collect::>() } - - /// todo!() add docs - pub fn box_of(size: usize, iter: impl Iterator) -> impl Iterator { - std::iter::once('╔') - .chain(std::iter::repeat('═').take(size + 2)) - .chain(['╗', '\n'].into_iter()) - .chain(iter.enumerate().flat_map(move |(i, c)| { - let modulo = i % size; - if modulo == 0 { - vec!['║', ' ', c] - } else if modulo == size - 1 { - vec![c, ' ', '║', '\n'] - } else { - vec![c] - } - .into_iter() - })) - .chain(std::iter::once('╚')) - .chain(std::iter::repeat('═').take(size + 2)) - .chain(['╝', '\n'].into_iter()) - } } impl<'a> Display for FloorView<'a> { diff --git a/src/es03_game/game.rs b/src/es03_game/game.rs index 16741f8..203fa47 100644 --- a/src/es03_game/game.rs +++ b/src/es03_game/game.rs @@ -90,23 +90,24 @@ impl Dungeon { /// - Update di tutte le entità del piano /// - Modifica di piano di eventuali giocatori pub fn compute_turn(&mut self) { - let moved: Option> = self.floors.iter_mut().fold(None, |moved, floor| { - let removed = floor.has_players().then(|| { - let removed = floor.update_players(); + let moved = self.floors.iter_mut().fold(None, |moved, floor| { + if floor.has_players() { + let _ = floor.update_players(); //todo!() evantually return the dead players? idk floor.update_entities(); - removed - }); - if let Some(mut moved) = moved { - moved.drain(..).for_each(|player| floor.add_player(player)); } - removed + + if let Some(player) = moved { + floor.add_player(player); + } + + floor.get_player_at_exit() }); - if let Some(mut moved) = moved { + if let Some(player) = 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)); + floor.add_player(player); } } diff --git a/src/es03_game/generator.rs b/src/es03_game/generator.rs index 149896c..3cab6b9 100644 --- a/src/es03_game/generator.rs +++ b/src/es03_game/generator.rs @@ -56,14 +56,21 @@ impl<'a> Generator<'a> { pub fn build_floor(mut self) -> Floor { let maze_gen = &self.config.maze_generation; let room_size = self.config.maze_generation.room_size.clone(); - let mut grid = MazeGenerator::new(self.size, room_size, &mut self.rng) + let mut gen = MazeGenerator::new(self.size, room_size, &mut self.rng); + let mut grid = gen .generate_rooms(maze_gen.room_placing_attempts) .generate_labyrinth(maze_gen.straight_percentage) .connect_regions() .remove_dead_ends(maze_gen.dead_ends) .finalize(Cell::Wall, Cell::Empty); - self.rand_place(&mut grid, Cell::Entance); + let index = gen.get_random_room_index(); + let entrance = gen.get_room_ranges(index); + let index = gen.get_random_room_index(); + let exit = gen.get_room_ranges(index); + + self.rand_place(&mut grid, Cell::Entance, entrance.0, entrance.1); + self.rand_place(&mut grid, Cell::Exit, exit.0, exit.1); self.rand_place_effects(&mut grid); Floor::new(self.level, self.rng, vec![], grid) } @@ -78,18 +85,24 @@ impl<'a> Generator<'a> { let index = self.rng.gen_range(0..effects.len()); let effect = effects[index].effect.clone(); let cell = Cell::Special(effect); - self.rand_place(grid, cell); + self.rand_place(grid, cell, 0..self.size, 0..self.size); } } /// 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(&mut self, grid: &mut Vec>, cell: Cell) -> (usize, usize) { + fn rand_place( + &mut self, + grid: &mut Vec>, + cell: Cell, + range_x: Range, + range_y: Range, + ) -> Position { loop { - let x = self.rng.gen_range(0..self.size.clone()); - let y = self.rng.gen_range(0..self.size); + let x = self.rng.gen_range(range_x.clone()); + let y = self.rng.gen_range(range_y.clone()); if let Cell::Empty = grid[x][y] { grid[x][y] = cell; - return (x, y); + return Position(x, y); } } } @@ -183,7 +196,7 @@ impl<'a> MazeGenerator<'a> { /// Nel caso si può decidere di lasciare qualche zona che non va a collegarsi da nessuna parte /// mettendo un numero > 0 nel cutoff.\ /// Questo indicherà che nel labirinto ci saranno al massimo N corridioi senza uscita. - pub fn remove_dead_ends(mut self, cutoff: u32) -> Self { + pub fn remove_dead_ends(&mut self, cutoff: u32) -> &mut Self { let mut dead_ends = (0..self.size) .into_iter() .flat_map(|x| { @@ -215,7 +228,7 @@ impl<'a> MazeGenerator<'a> { /// Questa funzione serve per fare proprio quello, ovvero il collegamento fra di essi.\ /// Il labirinto si può vedere come un grafo nel quale ci sono delle regioni (stanze e corridoi) scollegate /// fra di loro, e l'unico modo per metterle assieme è quello di preare degli archi (rompere i muri).\ - pub fn connect_regions(mut self) -> Self { + pub fn connect_regions(&mut self) -> &mut Self { let mut connectors = self.get_regions_connectors(); let mut merged = MergeSets::new(1, self.current_region); let mut keys = connectors.keys().map(|pos| pos.clone()).collect::>(); @@ -272,7 +285,7 @@ impl<'a> MazeGenerator<'a> { /// andare dritto quando crea il labirinto.\ /// Con percentuali alte si avranno molti corridoi lunghi, con percentuali basse si avranno /// molte svolte. - pub fn generate_labyrinth(mut self, mut straight_percentage: u32) -> Self { + pub fn generate_labyrinth(&mut self, mut straight_percentage: u32) -> &mut Self { straight_percentage = straight_percentage.min(100); // cap at 100 for x in (1..self.size).step_by(2) { @@ -347,7 +360,7 @@ impl<'a> MazeGenerator<'a> { /// Nel caso in cui questo metodo venga chiamato dopo la generazione del labirinto, e le stanze venissero /// inserite senza collisioni con quelle precedenti, il labirinto sottostante sarebbe sovrascritto.\ /// Il parametro attempts indica dopo quanti inserimenti falliti si deve fermare. - pub fn generate_rooms(mut self, mut attempts: u32) -> Self { + pub fn generate_rooms(&mut self, mut attempts: u32) -> &mut Self { while attempts > 0 { let room = Room::rand(self.rng, self.size, self.rooms_size.clone()); if self.rooms.iter().any(|other| room.collide(other)) { @@ -392,6 +405,17 @@ impl<'a> MazeGenerator<'a> { fn get(&self, pos: &Position) -> Option { self.regions[pos.0][pos.1] } + /// Ritorna un indice a caso fra quelli possibili riguardo le stanze create. + pub fn get_random_room_index(&mut self) -> usize { + self.rng.gen_range(0..self.rooms.len()) + } + /// Ritorna una coppia di ranges che indicano la zona in cui si trova la stanza indicata fra quelle generate. + pub fn get_room_ranges(&self, index: usize) -> (Range, Range) { + let room = &self.rooms[index.min(self.rooms.len())]; + let x = room.lo.0..(room.hi.0 + 1); + let y = room.lo.1..(room.hi.1 + 1); + (x, y) + } } /// Struttura ausiliaria usata per contenere le posizioni.\ diff --git a/src/es03_game/mod.rs b/src/es03_game/mod.rs index 9a7cf20..307fa11 100644 --- a/src/es03_game/mod.rs +++ b/src/es03_game/mod.rs @@ -48,9 +48,9 @@ pub mod generator; * Se volete potete anche cambiare le regole del gioco. * Mettere main e definizioni in files separati (le definizioni in uno o più files) e scrivete i test in una directory a parte. */ -pub fn run_console(player: String) { +pub fn run_console(player: String, seed: u64) { let mut config = Config::default(); - config.game_seed = rand::random(); + config.game_seed = seed; let mut game = Dungeon::new_with(config); game.add_player(player, Box::new(ConsoleInput)); @@ -64,6 +64,17 @@ pub fn run_console(player: String) { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ConsoleInput; impl ConsoleInput { + fn print_floor(&self, floor: FloorView, other:String) { + let mut term = console::Term::stdout(); + let _ = term.clear_screen(); + let _ = term.write_fmt(format_args!( + "{}{}\n{}\n", + Self::floor_as_string(&floor), + floor.entity, + other + )); + } + /// todo!() add docs fn floor_as_string(floor: &FloorView) -> String { let view = 5; let size = (2 * view) * 3; @@ -82,24 +93,36 @@ impl ConsoleInput { }) }); - FloorView::box_of(size, iter).collect() + Self::box_of(size, iter).collect() + } + /// todo!() add docs + fn box_of(size: usize, iter: impl Iterator) -> impl Iterator { + std::iter::once('╔') + .chain(std::iter::repeat('═').take(size + 2)) + .chain(['╗', '\n'].into_iter()) + .chain(iter.enumerate().flat_map(move |(i, c)| { + let modulo = i % size; + if modulo == 0 { + vec!['║', ' ', c] + } else if modulo == size - 1 { + vec![c, ' ', '║', '\n'] + } else { + vec![c] + } + .into_iter() + })) + .chain(std::iter::once('╚')) + .chain(std::iter::repeat('═').take(size + 2)) + .chain(['╝', '\n'].into_iter()) } } #[typetag::serde] impl Behavior for ConsoleInput { fn update(&self, floor: FloorView) { - let mut term = console::Term::stdout(); - let _ = term.clear_screen(); - let _ = term.write_fmt(format_args!( - "{}{}\n", - Self::floor_as_string(&floor), - floor.entity - )); + self.print_floor(floor, "".to_string()); } fn you_died(&self, floor: FloorView) { - let mut term = console::Term::stdout(); - let _ = term.clear_screen(); - let _ = term.write_fmt(format_args!("{}\nYOU DIED!\n", floor)); + self.print_floor(floor, "YOU DIED!".to_string()); } fn get_next_action(&self) -> Option { let mut term = console::Term::stdout();