Refactoring es 03
- game now should work - missing tests docs interaction
This commit is contained in:
@@ -8,4 +8,6 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rand_pcg = { version = "0.3.1", features = ["serde1"] }
|
rand_pcg = { version = "0.3.1", features = ["serde1"] }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
|
typetag = "0.2.16"
|
||||||
|
dyn-clone = "1.0.17"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#![allow(unused)]
|
|
||||||
|
|
||||||
/** Es.3
|
/** Es.3
|
||||||
* Implementare una libreria che permetta di realizzare il seguente gioco.
|
* Implementare una libreria che permetta di realizzare il seguente gioco.
|
||||||
@@ -40,3 +39,11 @@ pub mod generator;
|
|||||||
pub mod cell;
|
pub mod cell;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod entities;
|
pub mod entities;
|
||||||
|
|
||||||
|
pub fn start_game() {
|
||||||
|
let mut game = game::Rogue::new();
|
||||||
|
loop {
|
||||||
|
println!("{}", game);
|
||||||
|
game.compute_turn();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +1,93 @@
|
|||||||
|
use super::entities::{Action, Direction, Entity};
|
||||||
|
use dyn_clone::{clone_trait_object, DynClone};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{entities::{Direction, Entity}, game::Rogue};
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
|
|
||||||
#[derive(Clone, Copy, Deserialize, Serialize)]
|
|
||||||
pub enum Cell {
|
pub enum Cell {
|
||||||
Staricase,
|
Entance,
|
||||||
Special(Effect),
|
Exit,
|
||||||
|
Special(Box<dyn Effect>),
|
||||||
Wall,
|
Wall,
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
impl Cell {
|
||||||
pub enum Effect {
|
pub fn entity_over(&mut self, entity: &mut Entity) {
|
||||||
InstantDamage(i8),
|
match self {
|
||||||
TurnBasedDamage(u8, i8),
|
Cell::Special(effect) => {
|
||||||
//DeBuff(i8, i8),
|
entity.add_effect(effect.clone());
|
||||||
Confusion(u8),
|
if !effect.is_persistent() {
|
||||||
//Custom(i16),
|
*self = Cell::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Cell::Wall => {
|
||||||
|
entity.direction.invert();
|
||||||
|
entity.position = entity.direction.move_from(entity.position);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DELTA: i32 = 5;
|
#[typetag::serde(tag = "type")]
|
||||||
impl Effect {
|
pub trait Effect: DynClone {
|
||||||
pub fn effect(&self, game: &mut Rogue, entity: &mut Entity) {
|
fn is_persistent(&self) -> bool;
|
||||||
let floor = game.current_floor();
|
fn apply_to(&self, entity: &mut Entity);
|
||||||
|
}
|
||||||
|
clone_trait_object!(Effect);
|
||||||
|
|
||||||
|
#[derive(Clone, 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) {
|
||||||
|
entity.health += self.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, 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) {
|
||||||
|
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();
|
||||||
|
|
||||||
match *self {
|
|
||||||
Effect::InstantDamage(damage) => {
|
|
||||||
let damage = damage as i32;
|
|
||||||
let damage = damage + rng.gen_range(-DELTA..DELTA);
|
|
||||||
entity.health += damage;
|
|
||||||
}
|
|
||||||
Effect::Confusion(time) => {
|
|
||||||
if time > 0 {
|
|
||||||
entity.add_effect(Effect::Confusion(time - 1));
|
|
||||||
let coin_flip = rng.gen_range(0..=1);
|
let coin_flip = rng.gen_range(0..=1);
|
||||||
if coin_flip == 1 {
|
if coin_flip == 1 {
|
||||||
let random_direction = Direction::generate_random(rng);
|
let random_direction = Direction::random(rng);
|
||||||
entity.direction = random_direction;
|
entity.buffer = Action::Move(random_direction);
|
||||||
}
|
}
|
||||||
}
|
entity.add_effect(Box::new(Self(self.0 - 1)));
|
||||||
}
|
|
||||||
_ => todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const POISON: Effect = Effect::InstantDamage(20);
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub const FOOD: Effect = Effect::InstantDamage(-20);
|
pub struct TurnBasedDamage {
|
||||||
pub const CONFUSION: Effect = Effect::Confusion(u8::MAX);
|
time: u8,
|
||||||
|
damage: i32,
|
||||||
|
}
|
||||||
|
#[typetag::serde]
|
||||||
|
impl Effect for TurnBasedDamage {
|
||||||
|
fn is_persistent(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn apply_to(&self, entity: &mut Entity) {
|
||||||
|
if self.time > 0 {
|
||||||
|
entity.health += self.damage;
|
||||||
|
entity.add_effect(Box::new(Self {
|
||||||
|
time: self.time - 1,
|
||||||
|
damage: self.damage,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
use super::{
|
||||||
use super::cell::{self, Effect};
|
cell::{Confusion, Effect, InstantDamage},
|
||||||
|
entities::Decider,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struttura Config usata per definire il gioco, ha alcune cose utili
|
* Struttura Config usata per definire il gioco, ha alcune cose utili
|
||||||
@@ -10,26 +13,56 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub game_seed: u64,
|
pub game_seed: u64,
|
||||||
pub floor_size_range: (usize, usize),
|
pub room_size: Range<usize>,
|
||||||
pub effects: Vec<ConfigEffect>,
|
pub floor_size: Range<usize>,
|
||||||
pub effects_total: usize,
|
pub effects_total: usize,
|
||||||
|
pub effects: Vec<ConfigEffect>,
|
||||||
|
pub entities_total: usize,
|
||||||
|
pub entities: Vec<ConfigEntity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct ConfigEffect {
|
pub struct ConfigEffect {
|
||||||
pub effect: Effect,
|
pub floors: Range<usize>,
|
||||||
pub first_floor: usize,
|
pub effect: Box<dyn Effect>,
|
||||||
pub last_floor: usize,
|
|
||||||
pub priority: usize,
|
pub priority: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
|
pub struct ConfigEntity {
|
||||||
|
pub floors: Range<usize>,
|
||||||
|
pub name: String,
|
||||||
|
pub decider: Box<dyn Decider>,
|
||||||
|
pub health: i32,
|
||||||
|
pub attack: i32,
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
game_seed: 0,
|
game_seed: 0,
|
||||||
floor_size_range: (20, 30),
|
room_size: 5..10,
|
||||||
effects: vec![ConfigEffect { effect: cell::POISON, first_floor: 0, last_floor: 255, priority: 1 }],
|
floor_size: 30..40,
|
||||||
|
effects: vec![
|
||||||
|
ConfigEffect {
|
||||||
|
effect: Box::new(InstantDamage(20)),
|
||||||
|
floors: 0..255,
|
||||||
|
priority: 1,
|
||||||
|
},
|
||||||
|
ConfigEffect {
|
||||||
|
effect: Box::new(InstantDamage(-20)),
|
||||||
|
floors: 0..255,
|
||||||
|
priority: 1,
|
||||||
|
},
|
||||||
|
ConfigEffect {
|
||||||
|
effect: Box::new(Confusion(10)),
|
||||||
|
floors: 0..255,
|
||||||
|
priority: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
effects_total: 45,
|
effects_total: 45,
|
||||||
|
entities: vec![],
|
||||||
|
entities_total: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
use std::{collections::VecDeque, fmt::Display};
|
use super::{cell::Effect, floor::FloorPtr};
|
||||||
|
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};
|
||||||
use super::{
|
|
||||||
cell::{Cell, Effect},
|
|
||||||
game::Rogue,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Deserialize, Serialize)]
|
||||||
|
pub struct Position(pub usize, pub usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
UP,
|
UP,
|
||||||
DOWN,
|
DOWN,
|
||||||
@@ -19,25 +18,25 @@ pub enum Direction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Direction {
|
impl Direction {
|
||||||
pub fn invert(&self) -> Self {
|
pub fn invert(&mut self) {
|
||||||
match *self {
|
*self = match *self {
|
||||||
Direction::UP => Direction::DOWN,
|
Direction::UP => Direction::DOWN,
|
||||||
Direction::DOWN => Direction::UP,
|
Direction::DOWN => Direction::UP,
|
||||||
Direction::RIGHT => Direction::LEFT,
|
Direction::RIGHT => Direction::LEFT,
|
||||||
Direction::LEFT => Direction::RIGHT,
|
Direction::LEFT => Direction::RIGHT,
|
||||||
_ => Direction::NONE,
|
_ => Direction::NONE,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
pub fn move_from(&self, pos: Position) -> Position {
|
||||||
pub fn move_from(&self, pos: (usize, usize)) -> (usize, usize) {
|
|
||||||
match *self {
|
match *self {
|
||||||
Direction::UP => (pos.0, pos.1 + 1),
|
Direction::UP => Position(pos.0, pos.1 + 1),
|
||||||
Direction::DOWN => (pos.0, pos.1 - 1),
|
Direction::DOWN => Position(pos.0, pos.1 - 1),
|
||||||
Direction::RIGHT => (pos.0 + 1, pos.1),
|
Direction::RIGHT => Position(pos.0 + 1, pos.1),
|
||||||
Direction::LEFT => (pos.0 - 1, pos.1),
|
Direction::LEFT => Position(pos.0 - 1, pos.1),
|
||||||
Direction::NONE => (pos.0, pos.1),
|
Direction::NONE => pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn generate_random(rng: &mut Pcg32) -> Self {
|
pub fn random(rng: &mut Pcg32) -> Self {
|
||||||
match rng.gen_range(0..4) {
|
match rng.gen_range(0..4) {
|
||||||
0 => Direction::UP,
|
0 => Direction::UP,
|
||||||
1 => Direction::DOWN,
|
1 => Direction::DOWN,
|
||||||
@@ -61,83 +60,103 @@ impl Display for Direction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
|
pub struct Entity {
|
||||||
|
name: String,
|
||||||
|
effects: VecDeque<Box<dyn Effect>>,
|
||||||
|
decider: Box<dyn Decider>,
|
||||||
|
floor: FloorPtr,
|
||||||
|
pub buffer: Action,
|
||||||
|
pub position: Position,
|
||||||
|
pub direction: Direction,
|
||||||
|
pub health: i32,
|
||||||
|
attack: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity {
|
||||||
|
pub fn new(name: String, decider: Box<dyn Decider>, mut floor: FloorPtr) -> Self {
|
||||||
|
let position = floor.get().get_entrance();
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
floor,
|
||||||
|
decider,
|
||||||
|
position,
|
||||||
|
buffer: Action::DoNothing,
|
||||||
|
effects: VecDeque::new(),
|
||||||
|
direction: Direction::NONE,
|
||||||
|
attack: 100,
|
||||||
|
health: 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_effect(&mut self, effect: Box<dyn Effect>) {
|
||||||
|
self.effects.push_back(effect);
|
||||||
|
}
|
||||||
|
pub fn get_floor(&self) -> FloorPtr {
|
||||||
|
self.floor.clone()
|
||||||
|
}
|
||||||
|
pub fn set_floor(&mut self, floor: FloorPtr) {
|
||||||
|
self.floor = floor;
|
||||||
|
self.position = self.floor.get().get_entrance();
|
||||||
|
}
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
self.compute_action();
|
||||||
|
self.compute_effects();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_effects(&mut self) {
|
||||||
|
let total = self.effects.len(); // len could change
|
||||||
|
for _ in 0..total {
|
||||||
|
if let Some(effect) = self.effects.pop_front() {
|
||||||
|
effect.apply_to(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn compute_action(&mut self) {
|
||||||
|
let action = self.decider.get_next_action();
|
||||||
|
match self.buffer {
|
||||||
|
Action::DoNothing => action.apply(self),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
self.buffer = Action::DoNothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Move(Direction),
|
Move(Direction),
|
||||||
// attack
|
// attack
|
||||||
DoNothing,
|
DoNothing,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
impl Action {
|
||||||
pub struct Entity {
|
pub fn apply(self, entity: &mut Entity) {
|
||||||
name: String,
|
match self {
|
||||||
floor: usize,
|
Action::DoNothing => {}
|
||||||
effected_by: VecDeque<Effect>,
|
|
||||||
player: bool,
|
|
||||||
pub position: (usize, usize),
|
|
||||||
pub direction: Direction,
|
|
||||||
pub health: i32,
|
|
||||||
pub attack: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entity {
|
|
||||||
pub fn new(name: String) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
floor: 0,
|
|
||||||
effected_by: VecDeque::new(),
|
|
||||||
position: (0, 0),
|
|
||||||
player: false,
|
|
||||||
direction: Direction::UP,
|
|
||||||
attack: 100,
|
|
||||||
health: 100,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn new_player(name: String) -> Self {
|
|
||||||
let mut player = Self::new(name);
|
|
||||||
player.player = true;
|
|
||||||
player
|
|
||||||
}
|
|
||||||
pub fn add_effect(&mut self, effect: Effect) {
|
|
||||||
self.effected_by.push_back(effect);
|
|
||||||
}
|
|
||||||
pub fn compute_effects(&mut self, game: &mut Rogue) {
|
|
||||||
let total = self.effected_by.len(); // len could change
|
|
||||||
for _ in 0..total {
|
|
||||||
if let Some(effect) = self.effected_by.pop_front() {
|
|
||||||
effect.effect(game, self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn do_action(&mut self, game: &mut Rogue, action: Action) {
|
|
||||||
match action {
|
|
||||||
Action::Move(direction) => {
|
Action::Move(direction) => {
|
||||||
self.direction = direction;
|
let pos = direction.move_from(entity.position);
|
||||||
self.do_action_move(game);
|
entity.direction = direction;
|
||||||
|
entity.position = pos;
|
||||||
|
|
||||||
|
let mut floor = entity.floor.clone();
|
||||||
|
let mut floor = floor.get();
|
||||||
|
let cell = floor.get_cell(pos);
|
||||||
|
cell.entity_over(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_action_move(&mut self, game: &mut Rogue) {
|
clone_trait_object!(Decider);
|
||||||
let direction = self.direction;
|
#[typetag::serde(tag = "type")]
|
||||||
let pos = direction.move_from(self.position);
|
pub trait Decider: DynClone {
|
||||||
let floor = game.current_floor();
|
fn get_next_action(&self) -> Action;
|
||||||
|
}
|
||||||
|
|
||||||
match floor.get_cell(pos) {
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
Cell::Empty => {
|
pub struct Immovable;
|
||||||
self.position = pos;
|
#[typetag::serde]
|
||||||
}
|
impl Decider for Immovable {
|
||||||
Cell::Special(effect) => {
|
fn get_next_action(&self) -> Action {
|
||||||
self.position = pos;
|
Action::DoNothing
|
||||||
self.add_effect(effect)
|
|
||||||
}
|
|
||||||
Cell::Staricase => {
|
|
||||||
if self.player {
|
|
||||||
game.build_new_floor()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Cell::Wall => self.direction = direction.invert(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,28 @@
|
|||||||
|
use super::{
|
||||||
|
cell::Cell,
|
||||||
|
entities::{Entity, Position},
|
||||||
|
};
|
||||||
use rand_pcg::Pcg32;
|
use rand_pcg::Pcg32;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
cell::{RefCell, RefMut},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use super::cell::Cell;
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
use super::entities::Entity;
|
pub struct FloorPtr(Rc<RefCell<Floor>>);
|
||||||
use super::game::Rogue;
|
impl FloorPtr {
|
||||||
use super::generator::Generator;
|
pub fn new(level: usize, rng: Pcg32, entities: Vec<Entity>, grid: Vec<Vec<Cell>>) -> Self {
|
||||||
|
Self(Rc::new(RefCell::new(Floor::new(
|
||||||
|
level, rng, entities, grid,
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
pub fn get(&mut self) -> RefMut<Floor> {
|
||||||
|
self.0.borrow_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct Floor {
|
pub struct Floor {
|
||||||
level: usize,
|
level: usize,
|
||||||
grid: Vec<Vec<Cell>>,
|
grid: Vec<Vec<Cell>>,
|
||||||
@@ -15,28 +31,43 @@ pub struct Floor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Floor {
|
impl Floor {
|
||||||
pub fn new(generator: Generator) -> Self {
|
fn new(level: usize, rng: Pcg32, entities: Vec<Entity>, grid: Vec<Vec<Cell>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entities: vec![],
|
level,
|
||||||
level: generator.level,
|
grid,
|
||||||
grid: generator.build_empty_matrix(),
|
entities,
|
||||||
rng: generator.rng,
|
rng,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_level(&self) -> usize {
|
||||||
|
self.level
|
||||||
|
}
|
||||||
pub fn get_rng(&mut self) -> &mut Pcg32 {
|
pub fn get_rng(&mut self) -> &mut Pcg32 {
|
||||||
&mut self.rng
|
&mut self.rng
|
||||||
}
|
}
|
||||||
pub fn get_level(&self) -> usize {
|
pub fn get_cell(&mut self, pos: Position) -> &mut Cell {
|
||||||
self.level
|
&mut self.grid[pos.0][pos.1]
|
||||||
}
|
}
|
||||||
pub fn get_cell(&self, pos: (usize, usize)) -> Cell {
|
pub fn get_entrance(&mut self) -> Position {
|
||||||
self.grid[pos.0][pos.1]
|
self.grid
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(x, vec)| {
|
||||||
|
vec.iter().enumerate().find_map(|(y, cell)| {
|
||||||
|
if let Cell::Entance = cell {
|
||||||
|
Some(Position(x, y))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.expect("Entrance of the floor should be inside the grid!")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_entities(&mut self, game: &mut Rogue) {
|
pub fn update_entities(&mut self) {
|
||||||
for entity in &mut self.entities {
|
for entity in &mut self.entities {
|
||||||
entity.compute_effects(game);
|
entity.update();
|
||||||
entity.do_action(game, game.input_action(entity));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
use std::fmt::Display;
|
use super::{
|
||||||
|
cell::Cell,
|
||||||
|
config::Config,
|
||||||
|
entities::{Entity, Immovable},
|
||||||
|
floor::FloorPtr,
|
||||||
|
generator::Generator,
|
||||||
|
};
|
||||||
use rand::{RngCore, SeedableRng};
|
use rand::{RngCore, SeedableRng};
|
||||||
use rand_pcg::Pcg32;
|
use rand_pcg::Pcg32;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Display;
|
||||||
use super::{config::Config, entities::{Action, Entity}, floor::Floor, generator::Generator};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struttura del gioco generico che implementa un RogueLike.
|
* Struttura del gioco generico che implementa un RogueLike.
|
||||||
*/
|
*/
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct Rogue {
|
pub struct Rogue {
|
||||||
floor: Floor,
|
floors: Vec<FloorPtr>,
|
||||||
rng: Pcg32,
|
rng: Pcg32,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
players: Vec<Entity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Rogue {
|
impl Display for Rogue {
|
||||||
@@ -24,30 +29,75 @@ impl Display for Rogue {
|
|||||||
|
|
||||||
impl Rogue {
|
impl Rogue {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let config = Config::default();
|
Self::new_with(Config::default())
|
||||||
let mut rng = Pcg32::seed_from_u64(config.game_seed);
|
|
||||||
let floor = Floor::new(Generator::new(rng.next_u64(), 0, &config));
|
|
||||||
|
|
||||||
Self { rng, config, floor }
|
|
||||||
}
|
}
|
||||||
|
pub fn new_with(config: Config) -> Self {
|
||||||
pub fn current_floor(&mut self) -> &mut Floor {
|
let mut game = Self {
|
||||||
&mut self.floor
|
rng: Pcg32::seed_from_u64(config.game_seed),
|
||||||
|
floors: vec![],
|
||||||
|
players: vec![],
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
game.build_next_floor();
|
||||||
|
game
|
||||||
}
|
}
|
||||||
|
pub fn add_player(&mut self, name: String) {
|
||||||
pub fn build_new_floor(&mut self) {
|
let floor = self.floors[0].clone();
|
||||||
let level = self.floor.get_level();
|
let decider = Box::new(Immovable);
|
||||||
|
let entity = Entity::new(name, decider, floor);
|
||||||
|
self.players.push(entity);
|
||||||
|
}
|
||||||
|
pub fn get_floor(&self, level: usize) -> FloorPtr {
|
||||||
|
let floors = self.floors.len() - 1;
|
||||||
|
let index = level.min(floors);
|
||||||
|
self.floors[index].clone()
|
||||||
|
}
|
||||||
|
pub fn build_next_floor(&mut self) {
|
||||||
let floor_seed = self.rng.next_u64();
|
let floor_seed = self.rng.next_u64();
|
||||||
|
let floor_level = self.floors.len();
|
||||||
let generator = Generator::new(floor_seed, level + 1, &self.config);
|
let generator = Generator::new(floor_seed, floor_level, &self.config);
|
||||||
self.floor = Floor::new(generator);
|
let floor = generator.build_floor();
|
||||||
|
self.floors.push(floor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_action(&self, entity: &Entity) -> Action {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compute_turn(&mut self) {
|
pub fn compute_turn(&mut self) {
|
||||||
todo!();
|
let mut update_floors = vec![false; self.floors.len()];
|
||||||
|
let mut change_floors = vec![0; self.players.len()];
|
||||||
|
|
||||||
|
self.players.iter_mut().enumerate().for_each(|(i, player)| {
|
||||||
|
let mut floor = player.get_floor();
|
||||||
|
let mut floor = floor.get();
|
||||||
|
|
||||||
|
player.update();
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,113 @@
|
|||||||
use rand::Rng;
|
use super::{
|
||||||
use rand::SeedableRng;
|
cell::Cell,
|
||||||
|
config::{Config, ConfigEffect, ConfigEntity},
|
||||||
|
floor::{Floor, FloorPtr},
|
||||||
|
};
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
use rand_pcg::Pcg32;
|
use rand_pcg::Pcg32;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
use super::cell::Cell;
|
pub struct Generator {
|
||||||
|
|
||||||
use super::config::Config;
|
|
||||||
use super::config::ConfigEffect;
|
|
||||||
|
|
||||||
pub struct Generator<'a> {
|
|
||||||
pub rng: Pcg32,
|
pub rng: Pcg32,
|
||||||
pub level: usize,
|
pub level: usize,
|
||||||
size: usize,
|
size_rooms: Range<usize>,
|
||||||
effects: Vec<&'a ConfigEffect>,
|
|
||||||
effects_total: usize,
|
effects_total: usize,
|
||||||
// enemies vec configuration?
|
effects: Vec<ConfigEffect>,
|
||||||
|
entities_total: usize,
|
||||||
|
entities: Vec<ConfigEntity>,
|
||||||
|
size: usize,
|
||||||
|
grid: Vec<Vec<Cell>>, // enemies vec configuration?
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Generator<'a> {
|
impl Generator {
|
||||||
pub fn new(floor_seed: u64, floor_level: usize, config: &'a Config) -> Self {
|
pub fn new(floor_seed: u64, floor_level: usize, config: &Config) -> Self {
|
||||||
let mut rand_pcg = Pcg32::seed_from_u64(floor_seed);
|
let mut rand_pcg = Pcg32::seed_from_u64(floor_seed);
|
||||||
let range = config.floor_size_range.0..config.floor_size_range.1;
|
let floor_size = rand_pcg.gen_range(config.floor_size.clone());
|
||||||
let floor_size = rand_pcg.gen_range(range);
|
|
||||||
|
|
||||||
let effects_list = &config.effects;
|
|
||||||
let effects_list = effects_list.into_iter();
|
|
||||||
let effects_list = effects_list.filter_map(|val| {
|
|
||||||
if floor_level >= val.first_floor && floor_level <= val.last_floor {
|
|
||||||
Some(val)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let effects_list = effects_list.collect();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
rng: rand_pcg,
|
rng: rand_pcg,
|
||||||
level: floor_level,
|
level: floor_level,
|
||||||
size: floor_size,
|
size: floor_size,
|
||||||
|
size_rooms: config.room_size.clone(),
|
||||||
effects_total: config.effects_total,
|
effects_total: config.effects_total,
|
||||||
effects: effects_list,
|
effects: Self::clone_vec_filter(&config.effects, |val| {
|
||||||
|
val.floors.contains(&floor_level)
|
||||||
|
}),
|
||||||
|
entities_total: config.entities_total,
|
||||||
|
entities: Self::clone_vec_filter(&config.entities, |val| {
|
||||||
|
val.floors.contains(&floor_level)
|
||||||
|
}),
|
||||||
|
grid: Self::grid_with_only(floor_size, Cell::Wall),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_empty_matrix(&self) -> Vec<Vec<Cell>> {
|
pub fn build_floor(mut self) -> FloorPtr {
|
||||||
self.build_matrix_with(Cell::Empty)
|
for x in 1..(self.size - 1) {
|
||||||
|
for y in 1..(self.size - 1) {
|
||||||
|
self.grid[x][y] = Cell::Empty;
|
||||||
}
|
}
|
||||||
pub fn build_labyrinth(&mut self) -> Vec<Vec<Cell>> {
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
pub fn build_rooms(&mut self) -> Vec<Vec<Cell>> {
|
|
||||||
|
self.rand_place_cell(Cell::Entance);
|
||||||
|
self.rand_place_effects();
|
||||||
|
FloorPtr::new(self.level, self.rng, vec![], self.grid)
|
||||||
|
}
|
||||||
|
pub fn build_floor_catacomb(mut self) -> Floor {
|
||||||
|
// init to WALLS
|
||||||
|
// reserve some cells for rooms ??
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn place_staircase(&mut self, grid: Vec<Vec<Cell>>) -> Vec<Vec<Cell>> {
|
fn build_rooms(&mut self) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn build_matrix_with(&self, cell: Cell) -> Vec<Vec<Cell>> {
|
fn rand_build_room(&mut self) -> (usize, usize, usize, usize) {
|
||||||
vec![vec![cell; self.size]; self.size]
|
loop {
|
||||||
|
let (x, y) = self.rand_2d(0..self.size);
|
||||||
|
let size = self.rand_2d(self.size_rooms.clone());
|
||||||
|
let (x_up, y_up) = (x + size.0, y + size.1);
|
||||||
|
|
||||||
|
if x_up < self.size && y_up < self.size {
|
||||||
|
for x in x..x_up {
|
||||||
|
for y in y..y_up {
|
||||||
|
self.grid[x][y] = Cell::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (x, y, x_up, y_up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rand_place_effects(&mut self) {
|
||||||
|
for _ in 0..self.effects_total {
|
||||||
|
let index = self.rng.gen_range(0..self.effects.len());
|
||||||
|
let effect = self.effects[index].effect.clone();
|
||||||
|
let cell = Cell::Special(effect);
|
||||||
|
self.rand_place_cell(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn rand_place_cell(&mut self, cell: Cell) -> (usize, usize) {
|
||||||
|
loop {
|
||||||
|
let (x, y) = self.rand_2d(0..self.size);
|
||||||
|
if let Cell::Empty = self.grid[x][y] {
|
||||||
|
self.grid[x][y] = cell;
|
||||||
|
return (x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn rand_2d(&mut self, range: Range<usize>) -> (usize, usize) {
|
||||||
|
let x = self.rng.gen_range(range.clone());
|
||||||
|
let y = self.rng.gen_range(range);
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_vec_filter<T: Clone>(original: &Vec<T>, filter: impl Fn(&T) -> bool) -> Vec<T> {
|
||||||
|
original
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|val| if filter(&val) { Some(val) } else { None })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
fn grid_with_only(size: usize, cell: Cell) -> Vec<Vec<Cell>> {
|
||||||
|
vec![vec![cell; size]; size]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user