Game playable
- game with only one big room
This commit is contained in:
@@ -11,3 +11,4 @@ rand_pcg = { version = "0.3.1", features = ["serde1"] }
|
|||||||
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
typetag = "0.2.16"
|
typetag = "0.2.16"
|
||||||
dyn-clone = "1.0.17"
|
dyn-clone = "1.0.17"
|
||||||
|
console = "0.15.8"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use super::entities::{Action, Direction, Entity};
|
|||||||
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};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
/// Rappresentazione di una cella di spazio.\
|
/// Rappresentazione di una cella di spazio.\
|
||||||
/// Essa ha diversi valori in base a cosa si può fare o meno su di essa.
|
/// Essa ha diversi valori in base a cosa si può fare o meno su di essa.
|
||||||
@@ -38,6 +39,23 @@ impl Cell {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Restituisce la rappresentazione della cella in formato char, in questo modo
|
||||||
|
/// può essere utilizzata per vedere il valore e mostrarlo a terminale.
|
||||||
|
pub fn as_char(&self) -> char {
|
||||||
|
match self {
|
||||||
|
Cell::Entance => ' ',
|
||||||
|
Cell::Exit => '¤',
|
||||||
|
Cell::Special(_) => '§',
|
||||||
|
Cell::Wall => '█',
|
||||||
|
Cell::Empty => ' ',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Cell {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.as_char())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait che permette di implementare un effetto speciale di una
|
/// Trait che permette di implementare un effetto speciale di una
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
cell::{Confusion, Effect, InstantDamage},
|
cell::{Confusion, Effect, InstantDamage},
|
||||||
entities::Decider,
|
entities::Behavior,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
@@ -52,7 +52,7 @@ pub struct ConfigPlayer {
|
|||||||
pub struct ConfigEntity {
|
pub struct ConfigEntity {
|
||||||
pub floors: Range<usize>,
|
pub floors: Range<usize>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub decider: Box<dyn Decider>,
|
pub decider: Box<dyn Behavior>,
|
||||||
pub health: i32,
|
pub health: i32,
|
||||||
pub attack: i32,
|
pub attack: i32,
|
||||||
pub priority: usize,
|
pub priority: usize,
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
use super::{cell::Effect, floor::FloorPtr};
|
use super::{
|
||||||
|
cell::Effect,
|
||||||
|
floor::{FloorPtr, FloorView},
|
||||||
|
};
|
||||||
use dyn_clone::{clone_trait_object, DynClone};
|
use dyn_clone::{clone_trait_object, DynClone};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rand_pcg::Pcg32;
|
use rand_pcg::Pcg32;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::VecDeque, fmt::Display, mem};
|
use std::{collections::VecDeque, fmt::Display, io::Write, mem};
|
||||||
|
|
||||||
/// Tupla nominata Position in modo che nel codice sia più chiaro a cosa serve.\
|
/// Tupla nominata Position in modo che nel codice sia più chiaro a cosa serve.\
|
||||||
/// È molto più facile capire a colpo d'occhio Position rispetto a (usize, usize)\
|
/// È molto più facile capire a colpo d'occhio Position rispetto a (usize, usize)\
|
||||||
/// I due valori sono la posizione sull'asse X e sull'asse Y\
|
/// I due valori sono la posizione sull'asse X e sull'asse Y\
|
||||||
/// Il punto (0,0) si trova in basso a sinista.
|
/// Il punto (0,0) si trova in basso a sinista.
|
||||||
#[derive(Clone, Copy, Deserialize, Serialize)]
|
#[derive(PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
|
||||||
pub struct Position(pub usize, pub usize);
|
pub struct Position(pub usize, pub usize);
|
||||||
|
|
||||||
/// Indica la direzione dove una entità sta guardando.\
|
/// Indica la direzione dove una entità sta guardando.\
|
||||||
/// È possibile anche non guardare in nessuna direzione tramite None.
|
/// È possibile anche non guardare in nessuna direzione tramite None.
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
@@ -60,19 +63,22 @@ impl Direction {
|
|||||||
_ => Direction::None,
|
_ => Direction::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
/// Restituisce la rappresentazione della direzione in formato char, in questo modo
|
||||||
|
/// può essere utilizzata per vedere il valore e mostrarlo a terminale.
|
||||||
impl Display for Direction {
|
pub fn as_char(&self) -> char {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
match self {
|
||||||
let c = match self {
|
|
||||||
Self::Up => '▲',
|
Self::Up => '▲',
|
||||||
Self::Down => '▼',
|
Self::Down => '▼',
|
||||||
Self::Left => '◄',
|
Self::Left => '◄',
|
||||||
Self::Right => '►',
|
Self::Right => '►',
|
||||||
Self::None => '■',
|
Self::None => '■',
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
write!(f, "{}", c)
|
impl Display for Direction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.as_char())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +87,7 @@ impl Display for Direction {
|
|||||||
pub struct Entity {
|
pub struct Entity {
|
||||||
name: String,
|
name: String,
|
||||||
effects: VecDeque<Box<dyn Effect>>,
|
effects: VecDeque<Box<dyn Effect>>,
|
||||||
decider: Box<dyn Decider>,
|
behavior: Box<dyn Behavior>,
|
||||||
floor: FloorPtr,
|
floor: FloorPtr,
|
||||||
pub buffer: Action,
|
pub buffer: Action,
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
@@ -100,14 +106,14 @@ impl Entity {
|
|||||||
name: String,
|
name: String,
|
||||||
health: i32,
|
health: i32,
|
||||||
attack: i32,
|
attack: i32,
|
||||||
decider: Box<dyn Decider>,
|
behavior: Box<dyn Behavior>,
|
||||||
mut floor: FloorPtr,
|
mut floor: FloorPtr,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let position = floor.get().get_entrance();
|
let position = floor.get().get_entrance();
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
floor,
|
floor,
|
||||||
decider,
|
behavior,
|
||||||
position,
|
position,
|
||||||
attack,
|
attack,
|
||||||
health,
|
health,
|
||||||
@@ -177,6 +183,14 @@ impl Entity {
|
|||||||
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) {
|
||||||
let total = self.effects.len(); // len could change
|
let total = self.effects.len(); // len could change
|
||||||
@@ -189,7 +203,7 @@ impl Entity {
|
|||||||
/// 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) -> Option<Action> {
|
||||||
let action = self.decider.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,
|
||||||
_ => mem::replace(&mut self.buffer, Action::DoNothing),
|
_ => mem::replace(&mut self.buffer, Action::DoNothing),
|
||||||
@@ -199,20 +213,22 @@ impl Entity {
|
|||||||
action.apply(self);
|
action.apply(self);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Metodo statico per l'update e l'eventuale eliminazione di entità da un vettore.
|
impl Display for Entity {
|
||||||
/// Le entità rimosse sono quelle che non riescono a fare l'update o che eventualmente
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
/// non sono più in vita
|
let times = 20;
|
||||||
pub fn update_from_vec(entities: &mut Vec<Entity>) {
|
let health_bar = (self.health * times) / self.health_max;
|
||||||
let to_remove: Vec<usize> = entities
|
|
||||||
.iter_mut()
|
let filled = "█".repeat(health_bar as usize);
|
||||||
.enumerate()
|
let empty = " ".repeat((times - health_bar) as usize);
|
||||||
.filter_map(|(i, entity)| if entity.update() { Some(i) } else { None })
|
let health_bar = format!("[{}{}]", filled, empty);
|
||||||
.rev()
|
|
||||||
.collect();
|
write!(
|
||||||
to_remove.iter().for_each(|i| {
|
f,
|
||||||
entities.remove(*i);
|
"{}: {} {}{:4}/{:4}",
|
||||||
});
|
self.name, self.direction, health_bar, self.health, self.health_max
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,17 +271,22 @@ impl Action {
|
|||||||
/// \
|
/// \
|
||||||
/// Il trait è taggato con typetag in modo che possa essere utilizzato
|
/// Il trait è taggato con typetag in modo che possa essere utilizzato
|
||||||
/// nella serializzazione e deserializzazione di serde.
|
/// nella serializzazione e deserializzazione di serde.
|
||||||
/// Esso permette di trasformare le implementazioni di Decider in una
|
/// Esso permette di trasformare le implementazioni di questo trait in una
|
||||||
/// spiecie di Enum senza il bisogno di farlo manualmente.\
|
/// spiecie di Enum senza il bisogno di farlo manualmente.\
|
||||||
/// Quello che viene richiesto è che, nell'implementazione di una
|
/// Quello che viene richiesto è che, nell'implementazione di una
|
||||||
/// struttura concreta di questo trait, venga messo sopra impl X for Decider:\
|
/// struttura concreta di questo trait, venga messo sopra impl X for Behavior:\
|
||||||
/// #\[typetag::serde\]\
|
/// #\[typetag::serde\]\
|
||||||
/// \
|
/// \
|
||||||
/// In questo modo si possono creare molteplici comoprtamenti che implementano
|
/// In questo modo si possono creare molteplici comoprtamenti che implementano
|
||||||
/// questo trait senza il bisogno di avere un Enum con essi
|
/// questo trait senza il bisogno di avere un Enum con essi
|
||||||
#[typetag::serde(tag = "type")]
|
#[typetag::serde(tag = "type")]
|
||||||
pub trait Decider: DynClone {
|
pub trait Behavior: DynClone {
|
||||||
/// Genera una azione che poi verrà usata per l'entità associata a questo Decider.\
|
/// In questo metodo viene passata una struttura che contiene una rappresentazione del
|
||||||
|
/// piano semplice, avente solo delle informazioni parziali.\
|
||||||
|
/// Questo serve a mostrare eventualmente delle possibili informazioni all'utente
|
||||||
|
/// o di registrare dei valori per l'algoritmo di generazione delle azioni.
|
||||||
|
fn update(&self, floor: FloorView);
|
||||||
|
/// Genera una azione che poi verrà usata per l'entità associata.\
|
||||||
/// L'azione può essere generata in qualunque modo: casuale, sempre la stessa,
|
/// L'azione può essere generata in qualunque modo: casuale, sempre la stessa,
|
||||||
/// tramite interazione con console, o tramite una connessione ad un client.\
|
/// tramite interazione con console, o tramite una connessione ad un client.\
|
||||||
/// \
|
/// \
|
||||||
@@ -274,15 +295,45 @@ pub trait Decider: DynClone {
|
|||||||
/// ma anche una possibilità che alcune entità rare possano sparire.
|
/// ma anche una possibilità che alcune entità rare possano sparire.
|
||||||
fn get_next_action(&self) -> Option<Action>;
|
fn get_next_action(&self) -> Option<Action>;
|
||||||
}
|
}
|
||||||
clone_trait_object!(Decider);
|
clone_trait_object!(Behavior);
|
||||||
|
|
||||||
/// Semplice implementazione di un possibile comportamento di una entità.\
|
/// Semplice implementazione di un possibile comportamento di una entità.\
|
||||||
/// In questo caso l'entità resterà immobile nel punto in cui si trova per sempre.
|
/// In questo caso l'entità resterà immobile nel punto in cui si trova per sempre.
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Immovable;
|
pub struct Immovable;
|
||||||
#[typetag::serde]
|
#[typetag::serde]
|
||||||
impl Decider for Immovable {
|
impl Behavior for Immovable {
|
||||||
|
fn update(&self, _floor: FloorView) {}
|
||||||
fn get_next_action(&self) -> Option<Action> {
|
fn get_next_action(&self) -> Option<Action> {
|
||||||
Some(Action::DoNothing)
|
Some(Action::DoNothing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Semplice implementazione di una possibile interfaccia console.
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ConsoleInput;
|
||||||
|
#[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", floor));
|
||||||
|
}
|
||||||
|
fn get_next_action(&self) -> Option<Action> {
|
||||||
|
let mut term = console::Term::stdout();
|
||||||
|
let _ = term.write("Insert your action [wasd or enter for nothing]: ".as_bytes());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Ok(ch) = term.read_char() {
|
||||||
|
match ch {
|
||||||
|
'\n' => return Some(Action::DoNothing),
|
||||||
|
'w' => return Some(Action::Move(Direction::Up)),
|
||||||
|
'a' => return Some(Action::Move(Direction::Left)),
|
||||||
|
's' => return Some(Action::Move(Direction::Down)),
|
||||||
|
'd' => return Some(Action::Move(Direction::Right)),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use rand_pcg::Pcg32;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
cell::{RefCell, RefMut},
|
cell::{RefCell, RefMut},
|
||||||
|
fmt::Display,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19,9 +20,13 @@ impl FloorPtr {
|
|||||||
/// Il piano viene creato a partire dai parametri passati in input, che sono tutte cose
|
/// Il piano viene creato a partire dai parametri passati in input, che sono tutte cose
|
||||||
/// necessarie ad esso.
|
/// necessarie ad esso.
|
||||||
pub fn new(level: usize, rng: Pcg32, entities: Vec<Entity>, grid: Vec<Vec<Cell>>) -> Self {
|
pub fn new(level: usize, rng: Pcg32, entities: Vec<Entity>, grid: Vec<Vec<Cell>>) -> Self {
|
||||||
Self(Rc::new(RefCell::new(Floor::new(
|
Self(Rc::new(RefCell::new(Floor {
|
||||||
level, rng, entities, grid,
|
level,
|
||||||
))))
|
rng,
|
||||||
|
players: vec![],
|
||||||
|
entities,
|
||||||
|
grid,
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Permette di prendere il valore puntato al piano.
|
/// Permette di prendere il valore puntato al piano.
|
||||||
@@ -37,21 +42,12 @@ impl FloorPtr {
|
|||||||
pub struct Floor {
|
pub struct Floor {
|
||||||
level: usize,
|
level: usize,
|
||||||
grid: Vec<Vec<Cell>>,
|
grid: Vec<Vec<Cell>>,
|
||||||
|
players: Vec<Entity>,
|
||||||
entities: Vec<Entity>,
|
entities: Vec<Entity>,
|
||||||
rng: Pcg32,
|
rng: Pcg32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Floor {
|
impl Floor {
|
||||||
/// Crea un piano secondo i parametri indicati
|
|
||||||
fn new(level: usize, rng: Pcg32, entities: Vec<Entity>, grid: Vec<Vec<Cell>>) -> Self {
|
|
||||||
Self {
|
|
||||||
level,
|
|
||||||
grid,
|
|
||||||
entities,
|
|
||||||
rng,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -89,6 +85,100 @@ impl Floor {
|
|||||||
|
|
||||||
/// 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) {
|
||||||
Entity::update_from_vec(&mut self.entities);
|
let to_remove: Vec<bool> = self
|
||||||
|
.entities
|
||||||
|
.iter_mut()
|
||||||
|
.map(|entity| entity.update())
|
||||||
|
.collect();
|
||||||
|
let mut to_remove = to_remove.iter();
|
||||||
|
self.entities.retain(|_| *to_remove.next().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
pub level: usize,
|
||||||
|
pub entity: Entity,
|
||||||
|
pub players: Vec<Entity>,
|
||||||
|
pub entities: Vec<Entity>,
|
||||||
|
pub grid: Vec<Vec<Cell>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloorView {
|
||||||
|
/// 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(entity: &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 {
|
||||||
|
level,
|
||||||
|
entity: entity.clone(),
|
||||||
|
players,
|
||||||
|
entities,
|
||||||
|
grid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rappresentazione del piano come matrice di char
|
||||||
|
pub fn as_char_grid(&self) -> Vec<Vec<char>> {
|
||||||
|
let size = self.grid.len();
|
||||||
|
let mut grid: Vec<Vec<char>> = (0..size)
|
||||||
|
.map(|y| {
|
||||||
|
let row = (0..size).map(|x| Some(&self.grid[x][y]));
|
||||||
|
let mut row: Vec<_> = row
|
||||||
|
.clone()
|
||||||
|
.zip(row.skip(1).chain(std::iter::once(None)))
|
||||||
|
.flat_map(|(a, b)| {
|
||||||
|
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();
|
||||||
|
row.push('\n');
|
||||||
|
row
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let pos = &self.entity.position;
|
||||||
|
grid[pos.1][pos.0 * 2] = self.entity.direction.as_char();
|
||||||
|
grid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FloorView {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let grid: String = self
|
||||||
|
.as_char_grid()
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|row| {
|
||||||
|
let a: String = row.iter().collect();
|
||||||
|
a
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
write!(f, "{}\n{}", grid, self.entity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use super::{
|
use super::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
config::Config,
|
config::Config,
|
||||||
entities::{Entity, Immovable},
|
entities::{Behavior, Entity},
|
||||||
floor::FloorPtr,
|
floor::{FloorPtr, FloorView},
|
||||||
generator::Generator,
|
generator::Generator,
|
||||||
};
|
};
|
||||||
use rand::{RngCore, SeedableRng};
|
use rand::{RngCore, SeedableRng};
|
||||||
@@ -41,14 +41,25 @@ impl Dungeon {
|
|||||||
/// Aggiunge un giocatore al Dungeon, esso avrà le statistiche di base assegnate
|
/// Aggiunge un giocatore al Dungeon, esso avrà le statistiche di base assegnate
|
||||||
/// 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) {
|
pub fn add_player(&mut self, name: String, decider: Box<dyn Behavior>) {
|
||||||
let floor = self.floors[0].clone();
|
let floor = self.floors[0].clone();
|
||||||
let decider = Box::new(Immovable);
|
|
||||||
let stats = &self.config.player_stats;
|
let stats = &self.config.player_stats;
|
||||||
let entity = Entity::new(name, stats.health, stats.attack, decider, floor);
|
let entity = Entity::new(name, stats.health, stats.attack, decider, floor);
|
||||||
self.players.push(entity);
|
self.players.push(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Indica se nel dungeon ci sono dei giocatori.\
|
||||||
|
/// 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.
|
||||||
|
pub fn has_players(&self) -> bool {
|
||||||
|
!self.get_players().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) -> FloorPtr {
|
||||||
@@ -65,33 +76,13 @@ impl Dungeon {
|
|||||||
/// - Update di tutti i piani in cui c'è almeno un giocatore
|
/// - Update di tutti i piani in cui c'è almeno un giocatore
|
||||||
/// - 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 mut update_floors = vec![false; self.floors.len()];
|
if !self.has_players() {
|
||||||
let mut change_floors = vec![0; self.players.len()];
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Entity::update_from_vec(&mut self.players);
|
let mut update = UpdateDungeon::compute(self);
|
||||||
self.players.iter_mut().enumerate().for_each(|(i, player)| {
|
update.update_floors();
|
||||||
let mut floor = player.get_floor();
|
update.remove_eventual_players();
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
update_floors
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, b)| if *b { Some(i) } else { None })
|
|
||||||
.for_each(|i| self.floors[i].get().update_entities());
|
|
||||||
change_floors
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(_, f)| **f != 0)
|
|
||||||
.for_each(|(player, floor)| {
|
|
||||||
let floor = self.get_floor_or_build(*floor);
|
|
||||||
let player = &mut self.players[player];
|
|
||||||
player.set_floor(floor);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// permette di costruire il piano successivo
|
/// permette di costruire il piano successivo
|
||||||
@@ -115,3 +106,81 @@ impl Dungeon {
|
|||||||
self.get_floor(level)
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,13 +37,9 @@ impl Generator {
|
|||||||
size: floor_size,
|
size: floor_size,
|
||||||
size_rooms: config.room_size.clone(),
|
size_rooms: config.room_size.clone(),
|
||||||
effects_total: config.effects_total,
|
effects_total: config.effects_total,
|
||||||
effects: Self::vec_filter(&config.effects, |val| {
|
effects: Self::vec_filter(&config.effects, |val| val.floors.contains(&floor_level)),
|
||||||
val.floors.contains(&floor_level)
|
|
||||||
}),
|
|
||||||
entities_total: config.entities_total,
|
entities_total: config.entities_total,
|
||||||
entities: Self::vec_filter(&config.entities, |val| {
|
entities: Self::vec_filter(&config.entities, |val| val.floors.contains(&floor_level)),
|
||||||
val.floors.contains(&floor_level)
|
|
||||||
}),
|
|
||||||
grid: Self::grid_with_only(floor_size, Cell::Wall),
|
grid: Self::grid_with_only(floor_size, Cell::Wall),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +118,7 @@ impl Generator {
|
|||||||
original
|
original
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|val| if filter(&val) { Some(val) } else { None })
|
.filter_map(|val| filter(&val).then(|| val))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
/// crea un campo con solamente la cella specificata clonata su tutto di esso
|
/// crea un campo con solamente la cella specificata clonata su tutto di esso
|
||||||
|
|||||||
Reference in New Issue
Block a user