From 9df26d931b786c3c48e7e79c4bd13d3030b9272c Mon Sep 17 00:00:00 2001 From: Berack96 Date: Thu, 30 May 2024 21:09:08 +0200 Subject: [PATCH] Entity attack - implemented - changed param of a fn of trait Behavior --- rogue_lib/src/config.rs | 2 +- rogue_lib/src/entities.rs | 44 +++++++++++++++++++++++--------------- rogue_lib/src/floor.rs | 33 ++++++++++++++++++++++++++-- rogue_lib/src/generator.rs | 2 +- rogue_lib/src/lib.rs | 28 ++++++++++++++++++------ 5 files changed, 82 insertions(+), 27 deletions(-) diff --git a/rogue_lib/src/config.rs b/rogue_lib/src/config.rs index 914ba7a..7aebaaf 100644 --- a/rogue_lib/src/config.rs +++ b/rogue_lib/src/config.rs @@ -104,7 +104,7 @@ impl Default for Config { floors: 0..255, name: "Basic enemy".to_string(), behavior: Box::new(RandomMovement::new()), - health: 30, + health: 10, attack: 10, priority: 1, }], diff --git a/rogue_lib/src/entities.rs b/rogue_lib/src/entities.rs index 9ad9a13..8747fea 100644 --- a/rogue_lib/src/entities.rs +++ b/rogue_lib/src/entities.rs @@ -3,8 +3,7 @@ use super::{ floor::{Floor, FloorView}, }; use dyn_clone::{clone_trait_object, DynClone}; -use rand::{Rng, SeedableRng}; -use rand_pcg::Pcg32; +use rand::Rng; use serde::{Deserialize, Serialize}; use std::{collections::VecDeque, fmt::Display, mem}; @@ -57,7 +56,7 @@ impl Direction { /// Restituisce una direzione casuale a partire da un generatore.\ /// La direzione viene generata con una distribuzione uniforme, ovvero non /// 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) { 0 => Direction::Up, 1 => Direction::Down, @@ -130,7 +129,7 @@ impl Entity { /// Permette di vedere tutti gli effetti che in questo momento sono applicati all'entità.\ /// Gli effetti qui elencati sono in uno stato di attesa prima di essere effettivamente /// applicati tremite la funzione update. - pub fn get_effects(& self) -> impl Iterator> { + pub fn get_effects(&self) -> impl Iterator> { self.effects.iter() } @@ -220,8 +219,12 @@ impl Entity { } /// prende una decisione e applica l'azione da fare /// L'azione compiuta viene restituita, altrimenti None - fn compute_action(&mut self, behavior: &mut Box, floor: &mut Floor) -> Option { - let action = behavior.get_next_action()?; + fn compute_action( + &mut self, + behavior: &mut Box, + floor: &mut Floor, + ) -> Option { + let action = behavior.get_next_action(self)?; let action = match self.buffer { Action::DoNothing => action, _ => mem::replace(&mut self.buffer, Action::DoNothing), @@ -255,7 +258,7 @@ impl Display for Entity { #[derive(Clone, Default, Debug, Deserialize, Serialize)] pub enum Action { Move(Direction), - //Attack(Direction), + Attack(Direction), #[default] DoNothing, } @@ -277,6 +280,14 @@ impl Action { let cell = floor.get_cell_mut(&entity.position); 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.\ /// Non è necessario implementarla. 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, /// 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.\ /// 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. - fn get_next_action(&mut self) -> Option; + fn get_next_action(&mut self, entity: &Entity) -> Option; } clone_trait_object!(Behavior); @@ -325,7 +336,7 @@ clone_trait_object!(Behavior); pub struct Immovable; #[typetag::serde] impl Behavior for Immovable { - fn get_next_action(&mut self) -> Option { + fn get_next_action(&mut self, _entity: &Entity) -> Option { Some(Action::DoNothing) } } @@ -335,27 +346,26 @@ impl Behavior for Immovable { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RandomMovement { action: Action, - rng: Pcg32, } impl RandomMovement { pub fn new() -> Self { - Self { - action: Action::DoNothing, - rng: Pcg32::seed_from_u64(0), - } + let action = Action::default(); + Self { action } } } #[typetag::serde] impl Behavior for RandomMovement { fn update(&mut self, view: FloorView) { - let dir = Direction::random(&mut self.rng); 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); if let Cell::Empty = view.floor.get_cell(&pos) { self.action = Action::Move(dir); } } - fn get_next_action(&mut self) -> Option { + fn get_next_action(&mut self, _entity: &Entity) -> Option { Some(mem::take(&mut self.action)) } } diff --git a/rogue_lib/src/floor.rs b/rogue_lib/src/floor.rs index 73ed1b6..a46ed2f 100644 --- a/rogue_lib/src/floor.rs +++ b/rogue_lib/src/floor.rs @@ -105,6 +105,25 @@ impl Floor { .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.\ /// Nel caso in cui non ci siano giocatori sopra, questo metodo ritornerà None. pub fn get_player_at_exit(&mut self) -> Option { @@ -132,7 +151,12 @@ impl Floor { pub fn update_players(&mut self) { for _ in 0..self.players.len() { 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); } } @@ -142,7 +166,12 @@ impl Floor { pub fn update_entities(&mut self) { for _ in 0..self.entities.len() { 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); } } diff --git a/rogue_lib/src/generator.rs b/rogue_lib/src/generator.rs index 52dce27..061a9b0 100644 --- a/rogue_lib/src/generator.rs +++ b/rogue_lib/src/generator.rs @@ -159,7 +159,7 @@ where } /// 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); vec.iter().filter(|(p, _)| *p >= sample).next().unwrap().1 } diff --git a/rogue_lib/src/lib.rs b/rogue_lib/src/lib.rs index c14a8e8..0d1e4c7 100644 --- a/rogue_lib/src/lib.rs +++ b/rogue_lib/src/lib.rs @@ -76,9 +76,9 @@ pub fn box_of( let correction = if 2 * len + title.len() < size { 1 } else { 0 }; std::iter::once("╔".to_string()) - .chain(std::iter::repeat("═".to_string()).take(len + 1)) - .chain(std::iter::once(title)) - .chain(std::iter::repeat("═".to_string()).take(len + 1 + correction)) + .chain(std::iter::repeat("═".to_string()).take(len + 1)) + .chain(std::iter::once(title)) + .chain(std::iter::repeat("═".to_string()).take(len + 1 + correction)) .chain(std::iter::once("╗\n".to_string())) .chain(iter.map(|string| { std::iter::once("║ ".to_string()) @@ -164,18 +164,34 @@ impl Behavior for ConsoleInput { fn on_death(&mut self, floor: FloorView) { self.print_floor(floor, "YOU DIED!".to_string()); } - fn get_next_action(&mut self) -> Option { + fn get_next_action(&mut self, entity: &Entity) -> Option { + let prompt = "Insert your action [? for help]: "; 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 { if let Ok(ch) = term.read_char() { match ch { - ' ' => return Some(Action::DoNothing), + ' ' => return Some(Action::Attack(entity.direction)), + 'z' => 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)), + '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()); + } _ => (), } }