use macros to create monsters
This commit is contained in:
parent
8267ad083f
commit
e490011b4e
76
Cargo.lock
generated
76
Cargo.lock
generated
@ -104,6 +104,41 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
@ -115,6 +150,7 @@ name = "el_diabolo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossterm",
|
||||
"macros",
|
||||
"petgraph",
|
||||
"rand",
|
||||
"ratatui",
|
||||
@ -143,6 +179,12 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
@ -176,6 +218,12 @@ version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
@ -257,6 +305,16 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.2"
|
||||
@ -323,18 +381,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -481,6 +539,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
@ -505,9 +569,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.38"
|
||||
version = "2.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1,3 +1,6 @@
|
||||
[workspace]
|
||||
members = ["macros"]
|
||||
|
||||
[package]
|
||||
name = "el_diabolo"
|
||||
version = "0.1.0"
|
||||
@ -11,6 +14,7 @@ crossterm = "0.28.1"
|
||||
rand = "0.8.5"
|
||||
petgraph = "0.6.5"
|
||||
whoami = "1.5.2"
|
||||
macros = { path = "./macros" }
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "Joachim Lusiardi <joachim@lusiardi.de>"
|
||||
@ -20,5 +24,5 @@ depends = "$auto"
|
||||
section = "game"
|
||||
priority = "optional"
|
||||
assets = [
|
||||
["target/release/el_diabolo", "/usr/bin/", "755"],
|
||||
["target/release/el_diabolo", "/usr/bin/", "755"],
|
||||
]
|
17
macros/Cargo.toml
Normal file
17
macros/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
name = "macros"
|
||||
path = "src/lib.rs"
|
||||
proc-macro = true
|
||||
|
||||
|
||||
[dependencies]
|
||||
syn = "2.0.85"
|
||||
quote = "1.0.37"
|
||||
proc-macro2 = "1.0.89"
|
||||
darling= "0.20.10"
|
58
macros/src/lib.rs
Normal file
58
macros/src/lib.rs
Normal file
@ -0,0 +1,58 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
|
||||
#[proc_macro_derive(CreateMonsters)]
|
||||
pub fn create_monsters(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as syn::File);
|
||||
let (enum_item, _) = match &input.items[0] {
|
||||
syn::Item::Enum(enum_item) => (enum_item, &enum_item.attrs),
|
||||
_ => panic!("must be an enum"),
|
||||
};
|
||||
|
||||
let structs = enum_item.variants.iter().map(|variant| {
|
||||
let variant_name = &variant.ident;
|
||||
|
||||
let t = quote! {
|
||||
pub struct #variant_name {
|
||||
name: String,
|
||||
life: usize,
|
||||
position: Position,
|
||||
symbol: String,
|
||||
color: Color,
|
||||
experience_gain: usize,
|
||||
ticks_between_steps: u128,
|
||||
damage_range: RangeInclusive<usize>,
|
||||
}
|
||||
impl Monster for #variant_name {
|
||||
fn get_name(&self) -> &str { &self.name }
|
||||
fn is_dead(&self) -> bool { self.life <= 0 }
|
||||
fn get_experience_gain(&self) -> usize { self.experience_gain }
|
||||
fn get_representation(&self) -> (&str, Color) { (&self.symbol, self.color) }
|
||||
fn decrease_life(&mut self, by: usize) {
|
||||
self.life = self.life.saturating_sub(by);
|
||||
}
|
||||
fn get_ticks_between_steps(&self) -> u128 { self.ticks_between_steps }
|
||||
fn damage(&self) -> usize { rand::thread_rng().gen_range(self.damage_range.clone()) }
|
||||
|
||||
#[cfg(test)]
|
||||
fn get_life(&self) -> usize { self.life }
|
||||
}
|
||||
impl HasPosition for #variant_name {
|
||||
fn get_position(&mut self) -> &mut Position {
|
||||
&mut self.position
|
||||
}
|
||||
fn get_immutable_position(&self) -> &Position {
|
||||
&self.position
|
||||
}
|
||||
}
|
||||
};
|
||||
t
|
||||
});
|
||||
let output = quote::quote! {
|
||||
#(#structs)*
|
||||
};
|
||||
output.into()
|
||||
}
|
@ -12,7 +12,7 @@ use rand::Rng;
|
||||
|
||||
use crate::artifacts::{Artifact, Chest, Potion};
|
||||
use crate::level::{Level, StructureElement};
|
||||
use crate::monster::{create_monster_by_type, Monster, Orc, Rat, Snake};
|
||||
use crate::monster::{create_monster_by_type, Monster, MonsterTypes, Orc, Rat, Snake};
|
||||
use crate::position::Position;
|
||||
|
||||
pub const ROOMS_VERTICAL: usize = 7;
|
||||
@ -268,11 +268,12 @@ impl LevelGenerator {
|
||||
|
||||
let t = [
|
||||
// level 0
|
||||
HashMap::from([("Rat", 1..=100)]),
|
||||
HashMap::from([(MonsterTypes::Rat, 1..=100)]),
|
||||
// level 1
|
||||
// HashMap::from([("Rat", 1..=50), ("Snake", 51..=100)]),
|
||||
// // level 2
|
||||
// HashMap::from([("Rat", 1..=40)]),
|
||||
HashMap::from([(MonsterTypes::Rat, 1..=50), (MonsterTypes::Snake, 51..=100)]),
|
||||
// level 2
|
||||
HashMap::from([(MonsterTypes::Orc, 1..=100)]),
|
||||
HashMap::from([(MonsterTypes::Skeleton, 1..=100)]),
|
||||
];
|
||||
if level < t.len() {
|
||||
for (mtype, range) in &t[level] {
|
||||
|
152
src/monster.rs
152
src/monster.rs
@ -1,10 +1,10 @@
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use crate::position::{HasPosition, Position};
|
||||
use macros::CreateMonsters;
|
||||
use rand::Rng;
|
||||
use ratatui::prelude::Color;
|
||||
|
||||
use crate::position::{HasPosition, Position};
|
||||
|
||||
pub trait Monster: HasPosition {
|
||||
fn get_name(&self) -> &str;
|
||||
fn is_dead(&self) -> bool;
|
||||
@ -18,99 +18,77 @@ pub trait Monster: HasPosition {
|
||||
fn damage(&self) -> usize;
|
||||
}
|
||||
|
||||
#[derive(CreateMonsters, PartialEq, Eq, Hash)]
|
||||
pub enum MonsterTypes {
|
||||
Rat,
|
||||
Orc,
|
||||
Snake,
|
||||
Skeleton,
|
||||
}
|
||||
|
||||
macro_rules! create_monster {
|
||||
($($t:ident),+ $(,)?) => ($(
|
||||
pub struct $t {
|
||||
name: String,
|
||||
life: usize,
|
||||
position: Position,
|
||||
symbol: String,
|
||||
color: Color,
|
||||
experience_gain: usize,
|
||||
ticks_between_steps: u128,
|
||||
damage_range: RangeInclusive<usize>,
|
||||
}
|
||||
impl Monster for $t {
|
||||
fn get_name(&self) -> &str { &self.name }
|
||||
fn is_dead(&self) -> bool { self.life <= 0 }
|
||||
fn get_experience_gain(&self) -> usize { self.experience_gain }
|
||||
fn get_representation(&self) -> (&str, Color) { (&self.symbol, self.color) }
|
||||
fn decrease_life(&mut self, by: usize) {
|
||||
self.life = self.life.saturating_sub(by);
|
||||
}
|
||||
fn get_ticks_between_steps(&self) -> u128 { self.ticks_between_steps }
|
||||
fn damage(&self) -> usize { rand::thread_rng().gen_range(self.damage_range.clone()) }
|
||||
|
||||
#[cfg(test)]
|
||||
fn get_life(&self) -> usize { self.life }
|
||||
}
|
||||
impl HasPosition for $t {
|
||||
fn get_position(&mut self) -> &mut Position {
|
||||
&mut self.position
|
||||
}
|
||||
fn get_immutable_position(&self) -> &Position {
|
||||
&self.position
|
||||
($t:ident $(, $k:ident : $v:expr)*$(,)?) => (
|
||||
impl $t {
|
||||
pub fn new_with_position(position: Position) -> Self {
|
||||
Self {
|
||||
position,
|
||||
$($k: $v,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
)+)
|
||||
)
|
||||
}
|
||||
|
||||
impl Rat {
|
||||
pub fn new_with_position(position: Position) -> Self {
|
||||
Self {
|
||||
name: "rat".to_string(),
|
||||
life: 2,
|
||||
position,
|
||||
symbol: String::from("R"),
|
||||
color: Color::Black,
|
||||
experience_gain: 5,
|
||||
ticks_between_steps: 5,
|
||||
damage_range: 1..=2,
|
||||
}
|
||||
}
|
||||
}
|
||||
create_monster!(Rat);
|
||||
create_monster!(
|
||||
Rat,
|
||||
name:"rat".to_string(),
|
||||
life: 2,
|
||||
symbol: String::from("R"),
|
||||
color: Color::Black,
|
||||
experience_gain: 5,
|
||||
ticks_between_steps: 5,
|
||||
damage_range: 1..=2,
|
||||
);
|
||||
|
||||
impl Orc {
|
||||
pub fn new_with_position(position: Position) -> Self {
|
||||
Self {
|
||||
name: "orc".to_string(),
|
||||
life: 4,
|
||||
position,
|
||||
symbol: String::from("O"),
|
||||
color: Color::DarkGray,
|
||||
experience_gain: 10,
|
||||
ticks_between_steps: 10,
|
||||
damage_range: 2..=3,
|
||||
}
|
||||
}
|
||||
}
|
||||
create_monster!(Orc);
|
||||
create_monster!(
|
||||
Orc,
|
||||
name: "orc".to_string(),
|
||||
life: 4,
|
||||
symbol: String::from("O"),
|
||||
color: Color::DarkGray,
|
||||
experience_gain: 10,
|
||||
ticks_between_steps: 10,
|
||||
damage_range: 2..=3,
|
||||
);
|
||||
|
||||
impl Snake {
|
||||
pub fn new_with_position(position: Position) -> Self {
|
||||
Self {
|
||||
name: "snake".to_string(),
|
||||
life: 3,
|
||||
position,
|
||||
symbol: String::from("S"),
|
||||
color: Color::DarkGray,
|
||||
experience_gain: 10,
|
||||
ticks_between_steps: 20,
|
||||
damage_range: 1..=4,
|
||||
}
|
||||
}
|
||||
}
|
||||
create_monster!(Snake);
|
||||
create_monster!(
|
||||
Snake,
|
||||
name: "snake".to_string(),
|
||||
life: 3,
|
||||
symbol: String::from("s"),
|
||||
color: Color::Black,
|
||||
experience_gain: 10,
|
||||
ticks_between_steps: 20,
|
||||
damage_range: 1..=4,
|
||||
);
|
||||
|
||||
pub fn create_monster_by_type(mtype: &str, position: Position) -> Box<dyn Monster> {
|
||||
match mtype {
|
||||
"Rat" => Box::new(Rat::new_with_position(position)),
|
||||
"Orc" => Box::new(Orc::new_with_position(position)),
|
||||
"Snake" => Box::new(Snake::new_with_position(position)),
|
||||
&_ => {
|
||||
panic!("Unknown species: {}", mtype)
|
||||
}
|
||||
create_monster!(
|
||||
Skeleton,
|
||||
name: "skeleton".to_string(),
|
||||
life: 3,
|
||||
symbol: String::from("S"),
|
||||
color: Color::DarkGray,
|
||||
experience_gain: 20,
|
||||
ticks_between_steps: 10,
|
||||
damage_range: 1..=5,
|
||||
);
|
||||
|
||||
pub fn create_monster_by_type(monster_type: &MonsterTypes, position: Position) -> Box<dyn Monster> {
|
||||
match monster_type {
|
||||
MonsterTypes::Rat => Box::new(Rat::new_with_position(position)),
|
||||
MonsterTypes::Orc => Box::new(Orc::new_with_position(position)),
|
||||
MonsterTypes::Snake => Box::new(Snake::new_with_position(position)),
|
||||
MonsterTypes::Skeleton => Box::new(Skeleton::new_with_position(position)),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user