MultiFloor
- game now has multi-floor enabled - changed logic of update_players
This commit is contained in:
@@ -159,12 +159,24 @@ impl Entity {
|
|||||||
/// Nel caso in cui l'entità non riesca a fare l'update viene ritornato false.\
|
/// Nel caso in cui l'entità non riesca a fare l'update viene ritornato false.\
|
||||||
/// Cio significa che l'entità verrà rimossa dal gioco.
|
/// Cio significa che l'entità verrà rimossa dal gioco.
|
||||||
pub fn update(&mut self, floor: &mut Floor) -> bool {
|
pub fn update(&mut self, floor: &mut Floor) -> bool {
|
||||||
self.behavior.update(floor.get_limited_view_floor(self));
|
if !self.is_alive() {
|
||||||
if self.is_alive() && matches!(self.compute_action(floor), Some(_)) {
|
self.behavior.you_died(floor.get_limited_view_floor(self));
|
||||||
self.compute_effects(floor);
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
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à.
|
/// calcola gli effetti e li applica all'entità.
|
||||||
@@ -231,7 +243,7 @@ impl Action {
|
|||||||
direction.move_from(&mut entity.position);
|
direction.move_from(&mut entity.position);
|
||||||
entity.direction = direction;
|
entity.direction = direction;
|
||||||
|
|
||||||
let cell = floor.get_cell(&mut entity.position);
|
let cell = floor.get_cell(&entity.position);
|
||||||
cell.entity_over(entity);
|
cell.entity_over(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ impl Floor {
|
|||||||
/// Nel caso in cui la posizione non sia all'interno del piano, essa viene modificata
|
/// Nel caso in cui la posizione non sia all'interno del piano, essa viene modificata
|
||||||
/// facendola rientrare nei limiti di esso.\
|
/// facendola rientrare nei limiti di esso.\
|
||||||
/// Es. pos(2,3) ma il piano è di max 2 allora diventa -> pos(2,2)
|
/// 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;
|
let len = self.grid.len() - 1;
|
||||||
pos.0 = pos.0.min(len);
|
let x = pos.0.min(len);
|
||||||
pos.1 = pos.1.min(len);
|
let y = pos.1.min(len);
|
||||||
&mut self.grid[pos.0][pos.1]
|
&mut self.grid[x][y]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restituisce la posizione dell'entrata del piano.\
|
/// 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
|
/// 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<Entity> {
|
pub fn update_players(&mut self) -> Vec<Entity> {
|
||||||
let mut remove = vec![];
|
let mut next_floor = vec![];
|
||||||
for _ in 0..self.players.len() {
|
for _ in 0..self.players.len() {
|
||||||
let mut player = self.players.pop_front().unwrap();
|
let mut player = self.players.pop_front().unwrap();
|
||||||
if player.update(self) {
|
if player.update(self) {
|
||||||
self.players.push_back(player);
|
self.players.push_back(player);
|
||||||
} else {
|
} 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<Entity> {
|
||||||
|
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
|
/// Fa l'update di tutte le entità e rimuove eventualmente quelle non più in vita
|
||||||
@@ -218,27 +241,6 @@ impl<'a> FloorView<'a> {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// todo!() add docs
|
|
||||||
pub fn box_of(size: usize, iter: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
|
|
||||||
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> {
|
impl<'a> Display for FloorView<'a> {
|
||||||
|
|||||||
@@ -90,23 +90,24 @@ impl Dungeon {
|
|||||||
/// - Update di tutte le entità del piano
|
/// - Update di tutte le entità del piano
|
||||||
/// - Modifica di piano di eventuali giocatori
|
/// - Modifica di piano di eventuali giocatori
|
||||||
pub fn compute_turn(&mut self) {
|
pub fn compute_turn(&mut self) {
|
||||||
let moved: Option<Vec<Entity>> = self.floors.iter_mut().fold(None, |moved, floor| {
|
let moved = self.floors.iter_mut().fold(None, |moved, floor| {
|
||||||
let removed = floor.has_players().then(|| {
|
if floor.has_players() {
|
||||||
let removed = floor.update_players();
|
let _ = floor.update_players(); //todo!() evantually return the dead players? idk
|
||||||
floor.update_entities();
|
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();
|
self.build_next_floor();
|
||||||
let len = self.floors.len();
|
let len = self.floors.len();
|
||||||
let floor = &mut self.floors[len - 1];
|
let floor = &mut self.floors[len - 1];
|
||||||
moved.drain(..).for_each(|player| floor.add_player(player));
|
floor.add_player(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,14 +56,21 @@ impl<'a> Generator<'a> {
|
|||||||
pub fn build_floor(mut self) -> Floor {
|
pub fn build_floor(mut self) -> Floor {
|
||||||
let maze_gen = &self.config.maze_generation;
|
let maze_gen = &self.config.maze_generation;
|
||||||
let room_size = self.config.maze_generation.room_size.clone();
|
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_rooms(maze_gen.room_placing_attempts)
|
||||||
.generate_labyrinth(maze_gen.straight_percentage)
|
.generate_labyrinth(maze_gen.straight_percentage)
|
||||||
.connect_regions()
|
.connect_regions()
|
||||||
.remove_dead_ends(maze_gen.dead_ends)
|
.remove_dead_ends(maze_gen.dead_ends)
|
||||||
.finalize(Cell::Wall, Cell::Empty);
|
.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);
|
self.rand_place_effects(&mut grid);
|
||||||
Floor::new(self.level, self.rng, vec![], 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 index = self.rng.gen_range(0..effects.len());
|
||||||
let effect = effects[index].effect.clone();
|
let effect = effects[index].effect.clone();
|
||||||
let cell = Cell::Special(effect);
|
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.\
|
/// piazza una cella in un punto casuale del piano.\
|
||||||
/// il metodo contiuna a provare a piazzare la cella finche non trova una cella Empty.
|
/// il metodo contiuna a provare a piazzare la cella finche non trova una cella Empty.
|
||||||
fn rand_place(&mut self, grid: &mut Vec<Vec<Cell>>, cell: Cell) -> (usize, usize) {
|
fn rand_place(
|
||||||
|
&mut self,
|
||||||
|
grid: &mut Vec<Vec<Cell>>,
|
||||||
|
cell: Cell,
|
||||||
|
range_x: Range<usize>,
|
||||||
|
range_y: Range<usize>,
|
||||||
|
) -> Position {
|
||||||
loop {
|
loop {
|
||||||
let x = self.rng.gen_range(0..self.size.clone());
|
let x = self.rng.gen_range(range_x.clone());
|
||||||
let y = self.rng.gen_range(0..self.size);
|
let y = self.rng.gen_range(range_y.clone());
|
||||||
if let Cell::Empty = grid[x][y] {
|
if let Cell::Empty = grid[x][y] {
|
||||||
grid[x][y] = cell;
|
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
|
/// Nel caso si può decidere di lasciare qualche zona che non va a collegarsi da nessuna parte
|
||||||
/// mettendo un numero > 0 nel cutoff.\
|
/// mettendo un numero > 0 nel cutoff.\
|
||||||
/// Questo indicherà che nel labirinto ci saranno al massimo N corridioi senza uscita.
|
/// 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)
|
let mut dead_ends = (0..self.size)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|x| {
|
.flat_map(|x| {
|
||||||
@@ -215,7 +228,7 @@ impl<'a> MazeGenerator<'a> {
|
|||||||
/// Questa funzione serve per fare proprio quello, ovvero il collegamento fra di essi.\
|
/// 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
|
/// 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).\
|
/// 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 connectors = self.get_regions_connectors();
|
||||||
let mut merged = MergeSets::new(1, self.current_region);
|
let mut merged = MergeSets::new(1, self.current_region);
|
||||||
let mut keys = connectors.keys().map(|pos| pos.clone()).collect::<Vec<_>>();
|
let mut keys = connectors.keys().map(|pos| pos.clone()).collect::<Vec<_>>();
|
||||||
@@ -272,7 +285,7 @@ impl<'a> MazeGenerator<'a> {
|
|||||||
/// andare dritto quando crea il labirinto.\
|
/// andare dritto quando crea il labirinto.\
|
||||||
/// Con percentuali alte si avranno molti corridoi lunghi, con percentuali basse si avranno
|
/// Con percentuali alte si avranno molti corridoi lunghi, con percentuali basse si avranno
|
||||||
/// molte svolte.
|
/// 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
|
straight_percentage = straight_percentage.min(100); // cap at 100
|
||||||
|
|
||||||
for x in (1..self.size).step_by(2) {
|
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
|
/// 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.\
|
/// inserite senza collisioni con quelle precedenti, il labirinto sottostante sarebbe sovrascritto.\
|
||||||
/// Il parametro attempts indica dopo quanti inserimenti falliti si deve fermare.
|
/// 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 {
|
while attempts > 0 {
|
||||||
let room = Room::rand(self.rng, self.size, self.rooms_size.clone());
|
let room = Room::rand(self.rng, self.size, self.rooms_size.clone());
|
||||||
if self.rooms.iter().any(|other| room.collide(other)) {
|
if self.rooms.iter().any(|other| room.collide(other)) {
|
||||||
@@ -392,6 +405,17 @@ impl<'a> MazeGenerator<'a> {
|
|||||||
fn get(&self, pos: &Position) -> Option<usize> {
|
fn get(&self, pos: &Position) -> Option<usize> {
|
||||||
self.regions[pos.0][pos.1]
|
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<usize>, Range<usize>) {
|
||||||
|
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.\
|
/// Struttura ausiliaria usata per contenere le posizioni.\
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ pub mod generator;
|
|||||||
* Se volete potete anche cambiare le regole del gioco.
|
* 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.
|
* 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();
|
let mut config = Config::default();
|
||||||
config.game_seed = rand::random();
|
config.game_seed = seed;
|
||||||
|
|
||||||
let mut game = Dungeon::new_with(config);
|
let mut game = Dungeon::new_with(config);
|
||||||
game.add_player(player, Box::new(ConsoleInput));
|
game.add_player(player, Box::new(ConsoleInput));
|
||||||
@@ -64,6 +64,17 @@ pub fn run_console(player: String) {
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct ConsoleInput;
|
pub struct ConsoleInput;
|
||||||
impl 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 {
|
fn floor_as_string(floor: &FloorView) -> String {
|
||||||
let view = 5;
|
let view = 5;
|
||||||
let size = (2 * view) * 3;
|
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<Item = char>) -> impl Iterator<Item = char> {
|
||||||
|
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]
|
#[typetag::serde]
|
||||||
impl Behavior for ConsoleInput {
|
impl Behavior for ConsoleInput {
|
||||||
fn update(&self, floor: FloorView) {
|
fn update(&self, floor: FloorView) {
|
||||||
let mut term = console::Term::stdout();
|
self.print_floor(floor, "".to_string());
|
||||||
let _ = term.clear_screen();
|
|
||||||
let _ = term.write_fmt(format_args!(
|
|
||||||
"{}{}\n",
|
|
||||||
Self::floor_as_string(&floor),
|
|
||||||
floor.entity
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
fn you_died(&self, floor: FloorView) {
|
fn you_died(&self, floor: FloorView) {
|
||||||
let mut term = console::Term::stdout();
|
self.print_floor(floor, "YOU DIED!".to_string());
|
||||||
let _ = term.clear_screen();
|
|
||||||
let _ = term.write_fmt(format_args!("{}\nYOU DIED!\n", floor));
|
|
||||||
}
|
}
|
||||||
fn get_next_action(&self) -> Option<Action> {
|
fn get_next_action(&self) -> Option<Action> {
|
||||||
let mut term = console::Term::stdout();
|
let mut term = console::Term::stdout();
|
||||||
|
|||||||
Reference in New Issue
Block a user