feat: assemble weapon and potions into item file

This commit is contained in:
2026-01-13 22:33:31 +01:00
parent 4d8d2207b0
commit 42bff2595f
5 changed files with 119 additions and 93 deletions

View File

@@ -1,4 +1,6 @@
use crate::weapon::Weapon; use std::f64::consts::E;
use crate::items::Item;
/// # Character /// # Character
/// ///
@@ -24,67 +26,72 @@ use crate::weapon::Weapon;
/// for method chaining. /// for method chaining.
/// - `get_name` is only an accessor of `name` /// - `get_name` is only an accessor of `name`
pub struct Character { pub struct Character {
life: u8, pub life: u8,
name: &'static str, name: &'static str,
weapon: Option<Box<Weapon>>, item: Option<Box<dyn Item>>,
} }
impl Character { impl Character {
/// Creates a new `Character` with the given attributes. /// Creates a new `Character` with the given attributes.
/// Acts as a "static constructor" returning the /// Acts as a "static constructor" returning the
/// initialized struct. /// initialized struct.
pub fn new(life: u8, name: &'static str, weapon: Option<Box<Weapon>>) -> Self { pub fn new(name: &'static str, life: u8, item: Option<Box<dyn Item>>) -> Self {
Character { life, name, weapon } Character { life, name, item }
} }
pub fn new_no_weapon(life: u8, name: &'static str) -> Self { pub fn new_no_weapon(name: &'static str, life: u8) -> Self {
Character { Character {
life, life,
name, name,
weapon: None, item: None,
} }
} }
/// Attacks another character, reducing their `life` by pub fn use_item(&mut self, other: Option<&mut Character>) -> () {
/// self.damage. Returns `self` to allow method chaining. let mut item: Option<Box<dyn Item>> = self.item.take(); // pop item and replace to None
pub fn attack(&mut self, other: &mut Self) -> &mut Self { if let Some(i) = &mut item {
if let Some(w) = &mut self.weapon { i.apply(other.unwrap_or(self));
other.life = w
.if_not_broken(|damage: u8| other.life - damage)
.unwrap_or(0);
w.consume();
} }
self self.item = item;
} }
/// Returns the character's name. /// Returns the character's name.
pub fn get_name(&self) -> &str { pub fn get_name(&self) -> &str {
self.name self.name
} }
pub fn set_item(&mut self, item: Option<Box<dyn Item>>) -> &mut Self {
self.item = item;
self
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::items::{Potion, Weapon};
#[test] #[test]
/// Tests that a character is correctly initialized. /// Tests that a character is correctly initialized.
fn character_build() { fn character_build() {
let c = Character::new_no_weapon(100, "test"); let c = Character::new_no_weapon("test", 100);
assert_eq!(c.life, 100); assert_eq!(c.life, 100);
assert_eq!(c.name, c.get_name()); assert_eq!(c.name, c.get_name());
} }
#[test] #[test]
/// Tests that attacking reduces the target's life correctly. /// Tests that attacking reduces the target's life correctly.
fn character_attack() { fn character_item() {
let w = Box::new(Weapon::new("test", 10, 100)); let w = Box::new(Weapon::new("testw", 10, 100));
let mut c1 = Character::new(100, "test1", Option::Some(w)); let p = Box::new(Potion::new("testp", 50));
let mut c2 = Character::new_no_weapon(200, "test2");
c1.attack(&mut c2); let mut c: Character = Character::new_no_weapon("testc", 100);
assert_eq!(c2.life, 190); c.set_item(Some(p)).use_item(None);
Character::attack(&mut c2, &mut c1);
assert_eq!(c1.life, 100); assert_eq!(c.life, 150);
c.set_item(Some(w)).use_item(None);
assert_eq!(c.life, 140);
} }
} }

80
src/items.rs Normal file
View File

@@ -0,0 +1,80 @@
use crate::character::Character;
pub trait Item {
fn apply(&mut self, char: &mut Character) -> ();
fn get_name(&self) -> &'static str;
}
pub struct Weapon {
name: &'static str,
damage: u8,
durability: u8,
}
// #[warn(dead_code)]
impl Weapon {
pub fn new(name: &'static str, damage: u8, durability: u8) -> Self {
Weapon {
name,
damage,
durability,
}
}
fn reduce_durability(&mut self) -> () {
self.durability = self.durability.saturating_sub(self.damage / 2);
}
pub fn is_broken(&self) -> bool {
self.durability == 0
}
}
impl Item for Weapon {
fn apply(&mut self, char: &mut Character) -> () {
if !self.is_broken() {
char.life = char.life.saturating_sub(self.damage);
self.reduce_durability();
}
}
fn get_name(&self) -> &'static str {
self.name
}
}
pub struct Potion {
name: &'static str,
heal: u8,
}
impl Potion {
pub fn new(name: &'static str, heal: u8) -> Self {
Potion { name, heal }
}
}
impl Item for Potion {
fn apply(&mut self, char: &mut Character) -> () {
char.life += self.heal;
}
fn get_name(&self) -> &'static str {
self.name
}
}
#[cfg(test)]
mod tests {
mod weapon {
use super::super::*;
#[test]
fn build() {
let w = Weapon::new("test", 10, 100);
assert_eq!(w.damage, 10);
assert_eq!(w.durability, 100);
assert_eq!(w.get_name(), w.name);
}
}
}

View File

@@ -1,2 +1,2 @@
pub mod character; pub mod character;
pub mod weapon; pub mod items;

View File

@@ -1 +1,6 @@
fn main() {} use bakersadventure::items::{Item, Weapon};
fn main() {
let mut list: [Box<dyn Item>; 1] = [Box::new(Weapon::new("name", 1, 1))];
}

View File

@@ -1,66 +0,0 @@
pub struct Weapon {
name: &'static str,
damage: u8,
durability: u8,
}
impl Weapon {
pub fn new(name: &'static str, damage: u8, durability: u8) -> Self {
Weapon {
name,
damage,
durability,
}
}
pub fn consume(&mut self) -> &mut Self {
self.durability -= (self.damage / 2) as u8;
self
}
fn is_broken(&self) -> bool {
self.durability <= 0
}
pub fn if_not_broken<F, T>(&mut self, fun: F) -> Option<T>
where
F: FnOnce(u8) -> T,
{
if !self.is_broken() {
Some(fun(self.damage))
} else {
None
}
}
pub fn get_name(&self) -> &'static str {
self.name
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn weapon_build() {
let w = Weapon::new("test", 10, 100);
assert_eq!(w.damage, 10);
assert_eq!(w.durability, 100);
assert_eq!(w.get_name(), w.name);
}
#[test]
fn weapon_consumption() {
let mut w = Weapon::new("test", 10, 100);
w.consume();
assert_eq!(w.durability, 95);
}
#[test]
fn weapon_if_not_broken() {
let mut w = Weapon::new("test", 10, 100);
let val: u8 = 50;
assert_eq!(w.if_not_broken(|d: u8| { val - d }).unwrap(), 40);
}
}