Entity attack

- implemented
- changed param of a fn of trait Behavior
This commit is contained in:
2024-05-30 21:09:08 +02:00
parent 492a0d5c28
commit 9df26d931b
5 changed files with 82 additions and 27 deletions

View File

@@ -104,7 +104,7 @@ impl Default for Config {
floors: 0..255, floors: 0..255,
name: "Basic enemy".to_string(), name: "Basic enemy".to_string(),
behavior: Box::new(RandomMovement::new()), behavior: Box::new(RandomMovement::new()),
health: 30, health: 10,
attack: 10, attack: 10,
priority: 1, priority: 1,
}], }],

View File

@@ -3,8 +3,7 @@ use super::{
floor::{Floor, FloorView}, floor::{Floor, FloorView},
}; };
use dyn_clone::{clone_trait_object, DynClone}; use dyn_clone::{clone_trait_object, DynClone};
use rand::{Rng, SeedableRng}; use rand::Rng;
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, mem};
@@ -57,7 +56,7 @@ impl Direction {
/// Restituisce una direzione casuale a partire da un generatore.\ /// Restituisce una direzione casuale a partire da un generatore.\
/// La direzione viene generata con una distribuzione uniforme, ovvero non /// La direzione viene generata con una distribuzione uniforme, ovvero non
/// c'è una direzione preferita o con più probabilità. /// c'è una direzione preferita o con più probabilità.
pub fn random(rng: &mut Pcg32) -> Self { pub fn random(rng: &mut impl Rng) -> Self {
match rng.gen_range(0..5) { match rng.gen_range(0..5) {
0 => Direction::Up, 0 => Direction::Up,
1 => Direction::Down, 1 => Direction::Down,
@@ -220,8 +219,12 @@ 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, behavior: &mut Box<dyn Behavior>, floor: &mut Floor) -> Option<Action> { fn compute_action(
let action = behavior.get_next_action()?; &mut self,
behavior: &mut Box<dyn Behavior>,
floor: &mut Floor,
) -> Option<Action> {
let action = behavior.get_next_action(self)?;
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),
@@ -255,7 +258,7 @@ impl Display for Entity {
#[derive(Clone, Default, Debug, Deserialize, Serialize)] #[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub enum Action { pub enum Action {
Move(Direction), Move(Direction),
//Attack(Direction), Attack(Direction),
#[default] #[default]
DoNothing, DoNothing,
} }
@@ -277,6 +280,14 @@ impl Action {
let cell = floor.get_cell_mut(&entity.position); let cell = floor.get_cell_mut(&entity.position);
cell.entity_over(entity); cell.entity_over(entity);
} }
Action::Attack(direction) => {
let mut pos = entity.position;
direction.move_from(&mut pos);
if let Some(other) = floor.get_entity_at(&pos) {
other.apply_damage(entity.attack);
}
}
} }
} }
} }
@@ -308,14 +319,14 @@ pub trait Behavior: DynClone + core::fmt::Debug {
/// in modo che possa eventualmente fare ulteriori calcoli.\ /// in modo che possa eventualmente fare ulteriori calcoli.\
/// Non è necessario implementarla. /// Non è necessario implementarla.
fn on_death(&mut self, _view: FloorView) {} fn on_death(&mut self, _view: FloorView) {}
/// Genera una azione che poi verrà usata per l'entità associata.\ /// Genera un'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.\
/// \ /// \
/// Nel caso in cui venga restituito None come valore, l'entità verrà rimossa dal gioco.\ /// Nel caso in cui venga restituito None come valore, l'entità verrà rimossa dal gioco.\
/// Questo viene fatto in modo che si possa avere una possibilità di rimozione del giocatore, /// Questo viene fatto in modo che si possa avere una possibilità di rimozione del giocatore,
/// ma anche una possibilità che alcune entità rare possano sparire. /// ma anche una possibilità che alcune entità rare possano sparire.
fn get_next_action(&mut self) -> Option<Action>; fn get_next_action(&mut self, entity: &Entity) -> Option<Action>;
} }
clone_trait_object!(Behavior); clone_trait_object!(Behavior);
@@ -325,7 +336,7 @@ clone_trait_object!(Behavior);
pub struct Immovable; pub struct Immovable;
#[typetag::serde] #[typetag::serde]
impl Behavior for Immovable { impl Behavior for Immovable {
fn get_next_action(&mut self) -> Option<Action> { fn get_next_action(&mut self, _entity: &Entity) -> Option<Action> {
Some(Action::DoNothing) Some(Action::DoNothing)
} }
} }
@@ -335,27 +346,26 @@ impl Behavior for Immovable {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RandomMovement { pub struct RandomMovement {
action: Action, action: Action,
rng: Pcg32,
} }
impl RandomMovement { impl RandomMovement {
pub fn new() -> Self { pub fn new() -> Self {
Self { let action = Action::default();
action: Action::DoNothing, Self { action }
rng: Pcg32::seed_from_u64(0),
}
} }
} }
#[typetag::serde] #[typetag::serde]
impl Behavior for RandomMovement { impl Behavior for RandomMovement {
fn update(&mut self, view: FloorView) { fn update(&mut self, view: FloorView) {
let dir = Direction::random(&mut self.rng);
let mut pos = view.entity.position.clone(); let mut pos = view.entity.position.clone();
let mut rng = rand::rngs::ThreadRng::default();
let dir = Direction::random(&mut rng);
dir.move_from(&mut pos); dir.move_from(&mut pos);
if let Cell::Empty = view.floor.get_cell(&pos) { if let Cell::Empty = view.floor.get_cell(&pos) {
self.action = Action::Move(dir); self.action = Action::Move(dir);
} }
} }
fn get_next_action(&mut self) -> Option<Action> { fn get_next_action(&mut self, _entity: &Entity) -> Option<Action> {
Some(mem::take(&mut self.action)) Some(mem::take(&mut self.action))
} }
} }

View File

@@ -105,6 +105,25 @@ impl Floor {
.expect("Entrance of the floor should be inside the grid!") .expect("Entrance of the floor should be inside the grid!")
} }
/// Permette di prendere l'entità o il giocatore che si trova alla posizione indicata.\
/// Nel caso in cui non ci sia nessuna entità nella posizione, allora
/// verrà ritornato None.
pub fn get_entity_at(&mut self, position: &Position) -> Option<&mut Entity> {
let entity = self
.entities
.iter_mut()
.filter(|e| e.position == *position)
.next();
if let None = entity {
self.players
.iter_mut()
.filter(|e| e.position == *position)
.next()
} else {
entity
}
}
/// Ritorna un eventuale giocatore che si trova sopra la cella di uscita del piano.\ /// 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. /// Nel caso in cui non ci siano giocatori sopra, questo metodo ritornerà None.
pub fn get_player_at_exit(&mut self) -> Option<Entity> { pub fn get_player_at_exit(&mut self) -> Option<Entity> {
@@ -132,7 +151,12 @@ impl Floor {
pub fn update_players(&mut self) { pub fn update_players(&mut self) {
for _ in 0..self.players.len() { for _ in 0..self.players.len() {
let player = self.players.pop_front().unwrap(); let player = self.players.pop_front().unwrap();
if let Some(player) = player.update(self) { let previous = player.position;
if let Some(mut player) = player.update(self) {
if self.collisions(&player.position) > 0 {
player.position = previous;
}
self.players.push_back(player); self.players.push_back(player);
} }
} }
@@ -142,7 +166,12 @@ impl Floor {
pub fn update_entities(&mut self) { pub fn update_entities(&mut self) {
for _ in 0..self.entities.len() { for _ in 0..self.entities.len() {
let entity = self.entities.pop_front().unwrap(); let entity = self.entities.pop_front().unwrap();
if let Some(entity) = entity.update(self) { let previous = entity.position;
if let Some(mut entity) = entity.update(self) {
if self.collisions(&entity.position) > 0 {
entity.position = previous;
}
self.entities.push_back(entity); self.entities.push_back(entity);
} }
} }

View File

@@ -159,7 +159,7 @@ where
} }
/// todo!() docs /// todo!() docs
pub fn vec_get_sample<'a, T>(vec: &Vec<(f32, &'a T)>, rng: &mut Pcg32) -> &'a T { pub fn vec_get_sample<'a, T>(vec: &Vec<(f32, &'a T)>, rng: &mut impl Rng) -> &'a T {
let sample = rng.gen_range(0.0..1.0); let sample = rng.gen_range(0.0..1.0);
vec.iter().filter(|(p, _)| *p >= sample).next().unwrap().1 vec.iter().filter(|(p, _)| *p >= sample).next().unwrap().1
} }

View File

@@ -164,18 +164,34 @@ impl Behavior for ConsoleInput {
fn on_death(&mut self, floor: FloorView) { fn on_death(&mut self, floor: FloorView) {
self.print_floor(floor, "YOU DIED!".to_string()); self.print_floor(floor, "YOU DIED!".to_string());
} }
fn get_next_action(&mut self) -> Option<Action> { fn get_next_action(&mut self, entity: &Entity) -> Option<Action> {
let prompt = "Insert your action [? for help]: ";
let mut term = console::Term::stdout(); let mut term = console::Term::stdout();
let _ = term.write("Insert your action [wasd or space for nothing]: ".as_bytes()); let _ = term.write(prompt.as_bytes());
loop { loop {
if let Ok(ch) = term.read_char() { if let Ok(ch) = term.read_char() {
match ch { match ch {
' ' => return Some(Action::DoNothing), ' ' => return Some(Action::Attack(entity.direction)),
'z' => return Some(Action::DoNothing),
'w' => return Some(Action::Move(Direction::Up)), 'w' => return Some(Action::Move(Direction::Up)),
'a' => return Some(Action::Move(Direction::Left)), 'a' => return Some(Action::Move(Direction::Left)),
's' => return Some(Action::Move(Direction::Down)), 's' => return Some(Action::Move(Direction::Down)),
'd' => return Some(Action::Move(Direction::Right)), 'd' => return Some(Action::Move(Direction::Right)),
'q' => return None,
'?' => {
let _ = term.write_line("");
let _ = term.write_line("[wasd] => for movement");
let _ = term.write_line("[space] => for attacking the enemy in front");
let _ = term.write_line("[z] => for doing nothing");
let _ = term.write_line("[q] => for exit the game");
let _ = term.write("Press ANY button to continue...".as_bytes());
let _ = term.read_char();
let _ = term.clear_line();
let _ = term.clear_last_lines(4); // this number is from the previous message (4 total lines of help)
let _ = term.move_cursor_up(1);
let _ = term.move_cursor_right(prompt.len());
}
_ => (), _ => (),
} }
} }