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.\
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Entity> {
|
||||
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<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
|
||||
@@ -218,27 +241,6 @@ impl<'a> FloorView<'a> {
|
||||
})
|
||||
.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> {
|
||||
|
||||
@@ -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<Vec<Entity>> = 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<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 {
|
||||
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::<Vec<_>>();
|
||||
@@ -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<usize> {
|
||||
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.\
|
||||
|
||||
@@ -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<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]
|
||||
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<Action> {
|
||||
let mut term = console::Term::stdout();
|
||||
|
||||
Reference in New Issue
Block a user