172 lines
5.9 KiB
Rust
172 lines
5.9 KiB
Rust
use super::{
|
|
entities::{Action, Direction, Entity},
|
|
floor::Floor,
|
|
};
|
|
use dyn_clone::{clone_trait_object, DynClone};
|
|
use rand::Rng;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fmt::Display;
|
|
|
|
/// Rappresentazione di una cella di spazio.\
|
|
/// Essa ha diversi valori in base a cosa si può fare o meno su di essa.
|
|
/// Nel caso in cui passi sopra una entià esiste un metodo entity_over che
|
|
/// gestisce le varie casistiche.
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub enum Cell {
|
|
Entance,
|
|
Exit,
|
|
Special(Box<dyn Effect>),
|
|
Wall,
|
|
Empty,
|
|
}
|
|
|
|
impl Cell {
|
|
/// Data una entità che passa sopra questa cella di spazio
|
|
/// modifica la posizione e la fa tornare indietro nel caso sia un muro,
|
|
/// nel caso di una cella speciale, applica l'effetto all'entità,
|
|
/// e i tutti gli altri casi non fa nulla.\
|
|
/// Il movimento tra piani tramite Exit e Entrance non è gestito in questa funzione
|
|
/// data la complessità di muovere l'entità.
|
|
pub fn entity_over(&mut self, entity: &mut Entity) {
|
|
match self {
|
|
Cell::Special(effect) => {
|
|
entity.add_effect(effect.clone());
|
|
if !effect.is_persistent() {
|
|
*self = Cell::Empty
|
|
}
|
|
}
|
|
Cell::Wall => {
|
|
entity.direction.invert();
|
|
entity.direction.move_from(&mut entity.position);
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
/// 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(effect) => effect.as_char(),
|
|
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
|
|
/// cella di spazio.\
|
|
/// Il trait è taggato con typetag in modo che possa essere utilizzato
|
|
/// nella serializzazione e deserializzazione di serde.
|
|
/// Esso permette di trasformare le implementazioni di Effect in una
|
|
/// spiecie di Enum senza il bisogno di farlo manualmente.\
|
|
/// Quello che viene richiesto è che, nell'implementazione di una
|
|
/// struttura concreta di questo trait, venga messo sopra impl X for Effect:\
|
|
/// #\[typetag::serde\]\
|
|
/// \
|
|
/// In questo modo si possono creare molteplici effetti che implementano
|
|
/// questo trait senza il bisogno di avere un Enum con essi
|
|
#[typetag::serde(tag = "type")]
|
|
pub trait Effect: DynClone + core::fmt::Debug {
|
|
/// Indica se l'effetto rimane nel terreno dopo la sua applicazione ad una entità.\
|
|
/// Nel caso di true, l'effetto non verrà rimosso dal terreno,
|
|
/// eltrimenti la cella dove si trova questo effetto diventerà Empty
|
|
fn is_persistent(&self) -> bool;
|
|
/// Applica l'effetto ad una entità.\
|
|
/// L'effetto può essere di tutto a partire da un danno a qualcosa di più
|
|
/// elaborato come una trappola di nemici.\
|
|
/// Tramite l'entità si può anche accedere al piano dove si trova per
|
|
/// poter modificare eventualmente qualcosa.
|
|
fn apply_to(&self, entity: &mut Entity, floor: &mut Floor);
|
|
/// Ritorna un carattere che rappresenta l'effetto.
|
|
fn as_char(&self) -> char {
|
|
'?'
|
|
}
|
|
}
|
|
clone_trait_object!(Effect);
|
|
|
|
/// Permette di dare un danno istantaneo a qualunque entità ci passi sopra.\
|
|
/// Una volta utilizzato verrà rimosso dal piano.\
|
|
/// Nel caso in cui il danno sia negativo, l'entità verrà curata
|
|
/// (sempre che la sua vita sia un valore positivo e non negativo)
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct InstantDamage(pub i32);
|
|
#[typetag::serde]
|
|
impl Effect for InstantDamage {
|
|
fn is_persistent(&self) -> bool {
|
|
false
|
|
}
|
|
fn apply_to(&self, entity: &mut Entity, _floor: &mut Floor) {
|
|
entity.apply_damage(self.0);
|
|
}
|
|
fn as_char(&self) -> char {
|
|
if self.0 <= 0 {
|
|
'+'
|
|
} else {
|
|
'-'
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Permettere di infliggere lo stato di confuzione ad una entità.\
|
|
/// Esso ignora il successivo comando che verrà impartito all'entità
|
|
/// con una probabilità del 50% e inserirà un movimento in una direzione casuale.\
|
|
/// Come parametro si può passare per quanti turni l'effetto dura
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct Confusion(pub u8);
|
|
#[typetag::serde]
|
|
impl Effect for Confusion {
|
|
fn is_persistent(&self) -> bool {
|
|
true
|
|
}
|
|
fn apply_to(&self, entity: &mut Entity, floor: &mut Floor) {
|
|
if self.0 > 0 {
|
|
let rng = floor.get_rng();
|
|
if rng.gen_bool(0.5) {
|
|
let random_direction = Direction::random(rng);
|
|
entity.buffer = Action::Move(random_direction);
|
|
}
|
|
entity.add_effect(Box::new(Self(self.0 - 1)));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Permette di infliggere un danno nel tempo.\
|
|
/// Similmente a InstantDamage, se il danno è negativo allora il personaggio verrà curato,
|
|
/// sempre a patto che la sua vita sia un valore positivo.\
|
|
/// L'effetto dura un determinato numero di turni.
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct TurnBasedDamage {
|
|
time: u8,
|
|
damage: i32,
|
|
}
|
|
impl TurnBasedDamage {
|
|
pub fn new(time: u8, damage: i32) -> Self {
|
|
Self { time, damage }
|
|
}
|
|
}
|
|
#[typetag::serde]
|
|
impl Effect for TurnBasedDamage {
|
|
fn is_persistent(&self) -> bool {
|
|
false
|
|
}
|
|
fn apply_to(&self, entity: &mut Entity, _floor: &mut Floor) {
|
|
if self.time > 0 {
|
|
entity.apply_damage(self.damage);
|
|
if self.time > 1 {
|
|
entity.add_effect(Box::new(Self {
|
|
time: self.time - 1,
|
|
damage: self.damage,
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
}
|