Refactoring
- simplified floor - simplified update tick
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use super::entities::{Action, Direction, Entity};
|
use super::{entities::{Action, Direction, Entity}, floor::Floor};
|
||||||
use dyn_clone::{clone_trait_object, DynClone};
|
use dyn_clone::{clone_trait_object, DynClone};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -81,7 +81,7 @@ pub trait Effect: DynClone {
|
|||||||
/// elaborato come una trappola di nemici.\
|
/// elaborato come una trappola di nemici.\
|
||||||
/// Tramite l'entità si può anche accedere al piano dove si trova per
|
/// Tramite l'entità si può anche accedere al piano dove si trova per
|
||||||
/// poter modificare eventualmente qualcosa.
|
/// poter modificare eventualmente qualcosa.
|
||||||
fn apply_to(&self, entity: &mut Entity);
|
fn apply_to(&self, entity: &mut Entity, floor: &mut Floor);
|
||||||
}
|
}
|
||||||
clone_trait_object!(Effect);
|
clone_trait_object!(Effect);
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ impl Effect for InstantDamage {
|
|||||||
fn is_persistent(&self) -> bool {
|
fn is_persistent(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
fn apply_to(&self, entity: &mut Entity) {
|
fn apply_to(&self, entity: &mut Entity, _floor: &mut Floor) {
|
||||||
entity.apply_damage(self.0);
|
entity.apply_damage(self.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,10 +112,8 @@ impl Effect for Confusion {
|
|||||||
fn is_persistent(&self) -> bool {
|
fn is_persistent(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn apply_to(&self, entity: &mut Entity) {
|
fn apply_to(&self, entity: &mut Entity, floor: &mut Floor) {
|
||||||
if self.0 > 0 {
|
if self.0 > 0 {
|
||||||
let mut floor = entity.get_floor();
|
|
||||||
let mut floor = floor.get();
|
|
||||||
let rng = floor.get_rng();
|
let rng = floor.get_rng();
|
||||||
let coin_flip = rng.gen_range(0..=1);
|
let coin_flip = rng.gen_range(0..=1);
|
||||||
if coin_flip == 1 {
|
if coin_flip == 1 {
|
||||||
@@ -141,7 +139,7 @@ impl Effect for TurnBasedDamage {
|
|||||||
fn is_persistent(&self) -> bool {
|
fn is_persistent(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
fn apply_to(&self, entity: &mut Entity) {
|
fn apply_to(&self, entity: &mut Entity, _floor: &mut Floor) {
|
||||||
if self.time > 0 {
|
if self.time > 0 {
|
||||||
entity.apply_damage(self.damage);
|
entity.apply_damage(self.damage);
|
||||||
entity.add_effect(Box::new(Self {
|
entity.add_effect(Box::new(Self {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
cell::Effect,
|
cell::Effect,
|
||||||
floor::{FloorPtr, FloorView},
|
floor::{Floor, FloorView},
|
||||||
};
|
};
|
||||||
use dyn_clone::{clone_trait_object, DynClone};
|
use dyn_clone::{clone_trait_object, DynClone};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -88,7 +88,6 @@ pub struct Entity {
|
|||||||
name: String,
|
name: String,
|
||||||
effects: VecDeque<Box<dyn Effect>>,
|
effects: VecDeque<Box<dyn Effect>>,
|
||||||
behavior: Box<dyn Behavior>,
|
behavior: Box<dyn Behavior>,
|
||||||
floor: FloorPtr,
|
|
||||||
pub buffer: Action,
|
pub buffer: Action,
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
@@ -107,14 +106,11 @@ impl Entity {
|
|||||||
health: i32,
|
health: i32,
|
||||||
attack: i32,
|
attack: i32,
|
||||||
behavior: Box<dyn Behavior>,
|
behavior: Box<dyn Behavior>,
|
||||||
mut floor: FloorPtr,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let position = floor.get().get_entrance();
|
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
floor,
|
|
||||||
behavior,
|
behavior,
|
||||||
position,
|
position: Position(0, 0),
|
||||||
attack,
|
attack,
|
||||||
health,
|
health,
|
||||||
health_max: health,
|
health_max: health,
|
||||||
@@ -158,51 +154,34 @@ impl Entity {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restituisce il piano in cui si trova l'entità in questo momento.
|
/// Permette all'entità di mostrare il piano in cui si trova e di fare una mossa.\
|
||||||
pub fn get_floor(&self) -> FloorPtr {
|
/// Il piano viene mostrato tramite il behavior dell'entità e successivamente viene chiesto di fare un'azione.\
|
||||||
self.floor.clone()
|
/// Dopodichè vengono calcolati tutti gli effetti che devono essere applicati all'entità.\
|
||||||
}
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
/// Nel caso in cui l'entità non sia più in vita questo metodo ritornerà false
|
||||||
/// e non permetterà all'entità di fare un update.\
|
/// e non permetterà all'entità di fare update.\
|
||||||
/// 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) -> bool {
|
pub fn update(&mut self, floor: &mut Floor) -> bool {
|
||||||
if self.is_alive() && matches!(self.compute_action(), Some(_)) {
|
self.behavior.update(floor.get_limited_view_floor(self));
|
||||||
self.compute_effects();
|
if self.is_alive() && matches!(self.compute_action(floor), Some(_)) {
|
||||||
|
self.compute_effects(floor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Permette all'entità di mostrare il piano in cui si trova e di fare una mossa.\
|
|
||||||
/// Ha la stessa funzionalità di update() ma prima mostra il piano dell'entità.\
|
|
||||||
/// Il piano viene mostrato tramite il behavior dell'entità.
|
|
||||||
pub fn update_display(&mut self, floor: FloorView) -> bool {
|
|
||||||
self.behavior.update(floor);
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// calcola gli effetti e li applica all'entità.
|
/// calcola gli effetti e li applica all'entità.
|
||||||
fn compute_effects(&mut self) {
|
fn compute_effects(&mut self, floor: &mut Floor) {
|
||||||
let total = self.effects.len(); // len could change
|
let total = self.effects.len(); // len could change
|
||||||
for _ in 0..total {
|
for _ in 0..total {
|
||||||
if let Some(effect) = self.effects.pop_front() {
|
if let Some(effect) = self.effects.pop_front() {
|
||||||
effect.apply_to(self);
|
effect.apply_to(self, floor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// prende una decisione e applica l'azione da fare
|
/// prende una decisione e applica l'azione da fare
|
||||||
/// L'azione compiuta viene restituita, altrimenti None
|
/// L'azione compiuta viene restituita, altrimenti None
|
||||||
fn compute_action(&mut self) -> Option<Action> {
|
fn compute_action(&mut self, floor: &mut Floor) -> Option<Action> {
|
||||||
let action = self.behavior.get_next_action()?;
|
let action = self.behavior.get_next_action()?;
|
||||||
let action = match self.buffer {
|
let action = match self.buffer {
|
||||||
Action::DoNothing => action,
|
Action::DoNothing => action,
|
||||||
@@ -210,7 +189,7 @@ impl Entity {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let result = Some(action.clone());
|
let result = Some(action.clone());
|
||||||
action.apply(self);
|
action.apply(self, floor);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,7 +227,7 @@ impl Action {
|
|||||||
/// \
|
/// \
|
||||||
/// Es. Move(Up) sposterà l'entità da una posizione (x,y) -> (x,y+1)\
|
/// 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
|
/// e applicherà qualunque effetto che si trovi sulla cella di destinazione
|
||||||
pub fn apply(self, entity: &mut Entity) {
|
pub fn apply(self, entity: &mut Entity, floor: &mut Floor) {
|
||||||
match self {
|
match self {
|
||||||
Action::DoNothing => {}
|
Action::DoNothing => {}
|
||||||
Action::Move(direction) => {
|
Action::Move(direction) => {
|
||||||
@@ -256,8 +235,6 @@ impl Action {
|
|||||||
entity.direction = direction;
|
entity.direction = direction;
|
||||||
entity.position = pos;
|
entity.position = pos;
|
||||||
|
|
||||||
let mut floor = entity.floor.clone();
|
|
||||||
let mut floor = floor.get();
|
|
||||||
let cell = floor.get_cell(pos);
|
let cell = floor.get_cell(pos);
|
||||||
cell.entity_over(entity);
|
cell.entity_over(entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,36 +4,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use rand_pcg::Pcg32;
|
use rand_pcg::Pcg32;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{collections::VecDeque, fmt::Display};
|
||||||
cell::{RefCell, RefMut},
|
|
||||||
fmt::Display,
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Tupla creata per poter implementare qualche metodo sulla struttura Rc<RefCell<Floor>>\
|
|
||||||
/// 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<RefCell<Floor>>);
|
|
||||||
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<Entity>, grid: Vec<Vec<Cell>>) -> Self {
|
|
||||||
Self(Rc::new(RefCell::new(Floor {
|
|
||||||
level,
|
|
||||||
rng,
|
|
||||||
players: vec![],
|
|
||||||
entities,
|
|
||||||
grid,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Permette di prendere il valore puntato al piano.
|
|
||||||
pub fn get(&mut self) -> RefMut<Floor> {
|
|
||||||
self.0.borrow_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Indica un piano del dungeon, in essa si possono trovare le celle in cui si
|
/// Indica un piano del dungeon, in essa si possono trovare le celle in cui si
|
||||||
/// cammina e le entità che abitano il piano.\
|
/// cammina e le entità che abitano il piano.\
|
||||||
@@ -42,12 +13,36 @@ impl FloorPtr {
|
|||||||
pub struct Floor {
|
pub struct Floor {
|
||||||
level: usize,
|
level: usize,
|
||||||
grid: Vec<Vec<Cell>>,
|
grid: Vec<Vec<Cell>>,
|
||||||
players: Vec<Entity>,
|
players: VecDeque<Entity>,
|
||||||
entities: Vec<Entity>,
|
entities: VecDeque<Entity>,
|
||||||
rng: Pcg32,
|
rng: Pcg32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Floor {
|
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<Entity>, grid: Vec<Vec<Cell>>) -> 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
|
/// Restituisce il livello di profondità del piano
|
||||||
pub fn get_level(&self) -> usize {
|
pub fn get_level(&self) -> usize {
|
||||||
self.level
|
self.level
|
||||||
@@ -83,78 +78,66 @@ impl Floor {
|
|||||||
.expect("Entrance of the floor should be inside the grid!")
|
.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<Entity> {
|
||||||
|
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
|
/// Fa l'update di tutte le entità e rimuove eventualmente quelle non più in vita
|
||||||
pub fn update_entities(&mut self) {
|
pub fn update_entities(&mut self) {
|
||||||
let to_remove: Vec<bool> = self
|
for _ in 0..self.entities.len() {
|
||||||
.entities
|
let mut entity = self.entities.pop_front().unwrap();
|
||||||
.iter_mut()
|
if entity.update(self) {
|
||||||
.map(|entity| entity.update())
|
self.entities.push_back(entity);
|
||||||
.collect();
|
}
|
||||||
let mut to_remove = to_remove.iter();
|
}
|
||||||
self.entities.retain(|_| *to_remove.next().unwrap());
|
}
|
||||||
|
|
||||||
|
/// Crea una view del piano.\
|
||||||
|
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.\
|
/// 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.\
|
/// 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.\
|
/// Infatti internamente ha solo alcuni pezzi del gioco per non far mostrare tutto.\
|
||||||
pub struct FloorView {
|
pub struct FloorView<'a> {
|
||||||
pub level: usize,
|
pub entity: &'a Entity,
|
||||||
pub entity: Entity,
|
pub floor: &'a Floor,
|
||||||
pub players: Vec<Entity>,
|
|
||||||
pub entities: Vec<Entity>,
|
|
||||||
pub grid: Vec<Vec<Cell>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloorView {
|
impl<'a> FloorView<'a> {
|
||||||
/// Crea una vista del gioco corrente secondo la visione dell entità passata in intput.\
|
/// 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
|
/// Il SimpleFloor risultante avrà il piano, entità, livello e giocatori che si trovano
|
||||||
/// in questo momento sul piano dell'entità passata in input.
|
/// in questo momento sul piano dell'entità passata in input.
|
||||||
pub fn new(entity: &Entity) -> Self {
|
pub fn new(floor: &'a Floor, entity: &'a Entity) -> Self {
|
||||||
let mut floor = entity.get_floor();
|
|
||||||
let floor = floor.get();
|
|
||||||
|
|
||||||
let level = floor.level;
|
|
||||||
let grid = floor.grid.clone();
|
|
||||||
let entities: Vec<Entity> = floor
|
|
||||||
.entities
|
|
||||||
.iter()
|
|
||||||
.filter_map(|e| (e.position != entity.position).then_some(e.clone()))
|
|
||||||
.collect();
|
|
||||||
let players: Vec<Entity> = floor
|
|
||||||
.players
|
|
||||||
.iter()
|
|
||||||
.filter_map(|p| (p.position != entity.position).then_some(p.clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
level,
|
entity: &entity,
|
||||||
entity: entity.clone(),
|
floor: &floor,
|
||||||
players,
|
|
||||||
entities,
|
|
||||||
grid,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rappresentazione del piano come matrice di char
|
/// Rappresentazione del piano come matrice di char
|
||||||
pub fn as_char_grid(&self) -> Vec<Vec<char>> {
|
pub fn as_char_grid(&self) -> Vec<Vec<char>> {
|
||||||
let size = self.grid.len();
|
let grid = &self.floor.grid;
|
||||||
|
let size = grid.len();
|
||||||
let mut grid: Vec<Vec<char>> = (0..size)
|
let mut grid: Vec<Vec<char>> = (0..size)
|
||||||
.map(|y| {
|
.map(|y| {
|
||||||
let row = (0..size).map(|x| Some(&self.grid[x][y]));
|
let row = (0..size).map(|x| Some(&grid[x][y]));
|
||||||
let mut row: Vec<_> = row
|
let mut row: Vec<_> = row
|
||||||
.clone()
|
.clone()
|
||||||
.zip(row.skip(1).chain(std::iter::once(None)))
|
.zip(row.skip(1).chain(std::iter::once(None)))
|
||||||
.flat_map(|(a, b)| {
|
.flat_map(Self::increase_x_dimension)
|
||||||
let a = a.unwrap();
|
|
||||||
if let Some(b) = b {
|
|
||||||
let one_is_wall = matches!(b, Cell::Wall) || matches!(a, Cell::Wall);
|
|
||||||
let c = if one_is_wall { Cell::Wall } else { Cell::Empty };
|
|
||||||
vec![a.as_char(), c.as_char()]
|
|
||||||
} else {
|
|
||||||
vec![a.as_char()]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
row.push('\n');
|
row.push('\n');
|
||||||
row
|
row
|
||||||
@@ -165,18 +148,27 @@ impl FloorView {
|
|||||||
grid[pos.1][pos.0 * 2] = self.entity.direction.as_char();
|
grid[pos.1][pos.0 * 2] = self.entity.direction.as_char();
|
||||||
grid
|
grid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn increase_x_dimension(tuple: (Option<&Cell>, Option<&Cell>)) -> Vec<char> {
|
||||||
|
let (a, b) = tuple;
|
||||||
|
let a = a.unwrap();
|
||||||
|
if let Some(b) = b {
|
||||||
|
let one_is_wall = matches!(b, Cell::Wall) || matches!(a, Cell::Wall);
|
||||||
|
let c = if one_is_wall { Cell::Wall } else { Cell::Empty };
|
||||||
|
vec![a.as_char(), c.as_char()]
|
||||||
|
} else {
|
||||||
|
vec![a.as_char()]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for FloorView {
|
impl<'a> Display for FloorView<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let grid: String = self
|
let grid: String = self
|
||||||
.as_char_grid()
|
.as_char_grid()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|row| {
|
.map(|row| row.iter().collect::<String>())
|
||||||
let a: String = row.iter().collect();
|
|
||||||
a
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
write!(f, "{}\n{}", grid, self.entity)
|
write!(f, "{}\n{}", grid, self.entity)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use super::{
|
use super::{
|
||||||
cell::Cell,
|
|
||||||
config::Config,
|
config::Config,
|
||||||
entities::{Behavior, Entity},
|
entities::{Behavior, Entity},
|
||||||
floor::{FloorPtr, FloorView},
|
floor::Floor,
|
||||||
generator::Generator,
|
generator::Generator,
|
||||||
};
|
};
|
||||||
use rand::{RngCore, SeedableRng};
|
use rand::{RngCore, SeedableRng};
|
||||||
@@ -14,10 +13,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
/// e dei giocatori che esplorano.
|
/// e dei giocatori che esplorano.
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct Dungeon {
|
pub struct Dungeon {
|
||||||
floors: Vec<FloorPtr>,
|
floors: Vec<Floor>,
|
||||||
rng: Pcg32,
|
|
||||||
config: Config,
|
config: Config,
|
||||||
players: Vec<Entity>,
|
rng: Pcg32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dungeon {
|
impl Dungeon {
|
||||||
@@ -31,7 +29,6 @@ impl Dungeon {
|
|||||||
let mut game = Self {
|
let mut game = Self {
|
||||||
rng: Pcg32::seed_from_u64(config.game_seed),
|
rng: Pcg32::seed_from_u64(config.game_seed),
|
||||||
floors: vec![],
|
floors: vec![],
|
||||||
players: vec![],
|
|
||||||
config,
|
config,
|
||||||
};
|
};
|
||||||
game.build_next_floor();
|
game.build_next_floor();
|
||||||
@@ -42,47 +39,52 @@ impl Dungeon {
|
|||||||
/// ad esso tramite la configurazione indicata nel costruttore.\
|
/// ad esso tramite la configurazione indicata nel costruttore.\
|
||||||
/// Il giocatore appena inserito si troverà al piano 0.
|
/// Il giocatore appena inserito si troverà al piano 0.
|
||||||
pub fn add_player(&mut self, name: String, decider: Box<dyn Behavior>) {
|
pub fn add_player(&mut self, name: String, decider: Box<dyn Behavior>) {
|
||||||
let floor = self.floors[0].clone();
|
|
||||||
let stats = &self.config.player_stats;
|
let stats = &self.config.player_stats;
|
||||||
let entity = Entity::new(name, stats.health, stats.attack, decider, floor);
|
let player = Entity::new(name, stats.health, stats.attack, decider);
|
||||||
self.players.push(entity);
|
self.floors[0].add_player(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indica se nel dungeon ci sono dei giocatori.\
|
/// Indica se nel dungeon ci sono dei giocatori.\
|
||||||
/// Metodo utile, dato che nel caso in cui non ci siano, il dungen non verrà modificato
|
/// Metodo utile, dato che nel caso in cui non ci siano, il dungen non verrà modificato
|
||||||
/// siccome per calcolare il turno successivo ho bisogno di giocatori.
|
/// siccome per calcolare il turno successivo ho bisogno di giocatori.
|
||||||
pub fn has_players(&self) -> bool {
|
pub fn has_players(&mut self) -> bool {
|
||||||
!self.get_players().is_empty()
|
self.floors.iter().any(|floor| floor.has_players())
|
||||||
}
|
|
||||||
|
|
||||||
/// Restiutuisce la lista dei giocatori che in questo momento stanno giocando.
|
|
||||||
pub fn get_players(&self) -> &Vec<Entity> {
|
|
||||||
&self.players
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restituisce il piano indicato dal livello di profondità.\
|
/// Restituisce il piano indicato dal livello di profondità.\
|
||||||
/// Nel caso il livello non esista, restituisce il piano con profondità maggiore.
|
/// Nel caso il livello non esista, restituisce il piano con profondità maggiore.
|
||||||
pub fn get_floor(&self, level: usize) -> FloorPtr {
|
pub fn get_floor(&self, level: usize) -> &Floor {
|
||||||
let floors = self.floors.len() - 1;
|
let floors = self.floors.len() - 1;
|
||||||
let index = level.min(floors);
|
let index = level.min(floors);
|
||||||
self.floors[index].clone()
|
&self.floors[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Funzione principale del dungeon.\
|
/// Funzione principale del dungeon.\
|
||||||
/// In essa viene fatto fare l'update ai giocatori e ad ogni piano.
|
/// In essa viene fatto fare l'update ai giocatori e ad ogni piano.
|
||||||
/// In generale l'algoritmo è il seguente:\
|
/// In generale l'algoritmo è il seguente per ogni piano in cui si trova un giocatore:\
|
||||||
/// - I giocatori fanno le loro mosse.\
|
/// - I giocatori fanno le loro mosse.\
|
||||||
/// - Se un giocatore non è più in vita o non può indicare l'azione da fare, viene rimosso
|
/// - 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
|
/// - 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) {
|
||||||
if !self.has_players() {
|
let moved: Option<Vec<Entity>> = self.floors.iter_mut().fold(None, |moved, floor| {
|
||||||
return;
|
let removed = floor.has_players().then(|| {
|
||||||
}
|
let removed = floor.update_players();
|
||||||
|
floor.update_entities();
|
||||||
|
removed
|
||||||
|
});
|
||||||
|
if let Some(mut moved) = moved {
|
||||||
|
moved.drain(..).for_each(|player| floor.add_player(player));
|
||||||
|
}
|
||||||
|
removed
|
||||||
|
});
|
||||||
|
|
||||||
let mut update = UpdateDungeon::compute(self);
|
if let Some(mut moved) = moved {
|
||||||
update.update_floors();
|
self.build_next_floor();
|
||||||
update.remove_eventual_players();
|
let len = self.floors.len();
|
||||||
|
let floor = &mut self.floors[len - 1];
|
||||||
|
moved.drain(..).for_each(|player| floor.add_player(player));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// permette di costruire il piano successivo
|
/// permette di costruire il piano successivo
|
||||||
@@ -93,94 +95,4 @@ impl Dungeon {
|
|||||||
let floor = generator.build_floor();
|
let floor = generator.build_floor();
|
||||||
self.floors.push(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() {
|
|
||||||
level = self.floors.len();
|
|
||||||
}
|
|
||||||
if level == self.floors.len() {
|
|
||||||
self.build_next_floor()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.get_floor(level)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serve al dungeon per fare tutti i vari update.\
|
|
||||||
/// È stata creata una struttura e una sua implementazione apposta dato che per gli update la logica
|
|
||||||
/// è sia complessa che contorta. In questo modo si riesce a capire meglio che cosa viene fatto
|
|
||||||
/// utilizzando delle funzioni apposta.
|
|
||||||
struct UpdateDungeon<'a> {
|
|
||||||
dungeon: &'a mut Dungeon,
|
|
||||||
players: Vec<bool>,
|
|
||||||
update_floors: Vec<bool>,
|
|
||||||
change_floors: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> UpdateDungeon<'a> {
|
|
||||||
/// Crea un update del dungeon.\
|
|
||||||
/// Con questo metodo si inizializza la struttura e per farlo viene chiamata la funzione
|
|
||||||
/// update_display per ogni player che il dungeon ha attivo.\
|
|
||||||
/// Dopo questo metodo, la struttura che ne risulta ha salvato alcuni parametri che possono
|
|
||||||
/// diventare obsoleti nel caso in cui i metodo vangano chiamati dopo troppo tempo.
|
|
||||||
fn compute(dungeon: &'a mut Dungeon) -> Self {
|
|
||||||
let mut update_floors = vec![false; dungeon.floors.len()];
|
|
||||||
let mut change_floors = vec![0; dungeon.players.len()];
|
|
||||||
|
|
||||||
let players: Vec<bool> = (0..dungeon.players.len())
|
|
||||||
.into_iter()
|
|
||||||
.map(|i| {
|
|
||||||
let player = &mut dungeon.players[i];
|
|
||||||
let floor = FloorView::new(&player);
|
|
||||||
let value = player.update_display(floor);
|
|
||||||
|
|
||||||
let mut floor = player.get_floor();
|
|
||||||
let mut floor = floor.get();
|
|
||||||
update_floors[floor.get_level()] = true;
|
|
||||||
if let Cell::Exit = floor.get_cell(player.position) {
|
|
||||||
change_floors[i] = floor.get_level() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Self {
|
|
||||||
dungeon,
|
|
||||||
players,
|
|
||||||
update_floors,
|
|
||||||
change_floors,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Permette di fare l'update dei tutti i piani che hanno giocatori attivi.\
|
|
||||||
/// I giocatori attivi vengono calcolati appena viene creata l'istanza, quindi
|
|
||||||
/// questo metodo può diventare obsoleto nel caso in cui venga chiamato dopo troppo tempo
|
|
||||||
/// dall'inizializzazione della struttura.
|
|
||||||
fn update_floors(&mut self) -> &mut Self {
|
|
||||||
self.update_floors
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, b)| (*b).then(|| i))
|
|
||||||
.for_each(|i| self.dungeon.floors[i].get().update_entities());
|
|
||||||
self.change_floors
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(_, f)| **f != 0)
|
|
||||||
.for_each(|(player, floor)| {
|
|
||||||
let floor = self.dungeon.get_floor_or_build(*floor);
|
|
||||||
let player = &mut self.dungeon.players[player];
|
|
||||||
player.set_floor(floor);
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Permette di rimuovare eventuali giocatori che non servono più.\
|
|
||||||
/// Questo metodo prende l'ownership della struttura dato che deve essere chiamato per ultimo
|
|
||||||
/// siccome può modificare la lunghezza del vettore di players, invalidando quindi
|
|
||||||
/// tutti i parametri creati precedentemente.
|
|
||||||
fn remove_eventual_players(self) {
|
|
||||||
let mut players = self.players.iter();
|
|
||||||
self.dungeon.players.retain(|_| *players.next().unwrap());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::{
|
use super::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
config::{Config, ConfigEffect, ConfigEntity},
|
config::{Config, ConfigEffect, ConfigEntity},
|
||||||
floor::{Floor, FloorPtr},
|
floor::Floor,
|
||||||
};
|
};
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
use rand_pcg::Pcg32;
|
use rand_pcg::Pcg32;
|
||||||
@@ -46,7 +46,7 @@ impl Generator {
|
|||||||
|
|
||||||
/// Questo metodo è il più semplice per la generazione del piano.\
|
/// Questo metodo è il più semplice per la generazione del piano.\
|
||||||
/// Crea una enorme stanza con dei muri attorno e piazza tutti gli effetti.
|
/// Crea una enorme stanza con dei muri attorno e piazza tutti gli effetti.
|
||||||
pub fn build_floor(mut self) -> FloorPtr {
|
pub fn build_floor(mut self) -> Floor {
|
||||||
for x in 1..(self.size - 1) {
|
for x in 1..(self.size - 1) {
|
||||||
for y in 1..(self.size - 1) {
|
for y in 1..(self.size - 1) {
|
||||||
self.grid[x][y] = Cell::Empty;
|
self.grid[x][y] = Cell::Empty;
|
||||||
@@ -55,7 +55,7 @@ impl Generator {
|
|||||||
|
|
||||||
self.rand_place_cell(Cell::Entance);
|
self.rand_place_cell(Cell::Entance);
|
||||||
self.rand_place_effects();
|
self.rand_place_effects();
|
||||||
FloorPtr::new(self.level, self.rng, vec![], self.grid)
|
Floor::new(self.level, self.rng, vec![], self.grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO
|
/// TODO
|
||||||
|
|||||||
Reference in New Issue
Block a user