From f452ccebe0d702be701fe0d5f748c4f6b99b262e Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 20 Nov 2021 23:38:23 +0100 Subject: [PATCH] Done. --- src/main.rs | 175 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 134 insertions(+), 41 deletions(-) diff --git a/src/main.rs b/src/main.rs index 12482f0..52b8f69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,10 @@ -// #![allow(unused_imports)] -// #![allow(unused_variables)] -// #![allow(unused_mut)] -// #![allow(dead_code)] -use std::cmp; - +#![allow(unused_imports)] +#![allow(unused_variables)] +#![allow(unused_mut)] +#![allow(dead_code)] use rand::Rng; use serde::{Deserialize, Serialize}; +use std::cmp; use std::error::Error; use std::fs::File; use std::io::{Read, Write}; @@ -165,7 +164,7 @@ impl Object { pub fn attack(&mut self, target: &mut Object, game: &mut Game) { // A simple formula for attack damage - let damage = self.fighter.map_or(0, |f| f.power) - target.fighter.map_or(0, |f| f.defense); + let damage = self.power(game) - target.defense(game); if damage > 0 { // Make the target take some damage game.messages.add( @@ -191,11 +190,12 @@ impl Object { } } - pub fn heal(&mut self, amount: i32) { + pub fn heal(&mut self, amount: i32, game: &Game) { + let max_hp = self.max_hp(game); if let Some(ref mut fighter) = self.fighter { fighter.hp += amount; - if fighter.hp > fighter.max_hp { - fighter.hp = fighter.max_hp; + if fighter.hp > max_hp { + fighter.hp = max_hp; } } } @@ -254,6 +254,50 @@ impl Object { ); } } + + // Returns a list of equipped items + pub fn get_all_equipped(&self, game: &Game) -> Vec { + if self.name == "player" { + game + .inventory + .iter() + .filter(|item| item.equipment.map_or(false, |e| e.equipped)) + .map(|item| item.equipment.unwrap()) + .collect() + } else { + vec![] // other objects have no equipment + } + } + + pub fn power(&self, game: &Game) -> i32 { + let base_power = self.fighter.map_or(0, |f| f.base_power); + let bonus: i32 = self + .get_all_equipped(game) + .iter() + .map(|e| e.power_bonus) + .sum(); + base_power + bonus + } + + pub fn defense(&self, game: &Game) -> i32 { + let base_defense = self.fighter.map_or(0, |f| f.base_defense); + let bonus: i32 = self + .get_all_equipped(game) + .iter() + .map(|e| e.defense_bonus) + .sum(); + base_defense + bonus + } + + pub fn max_hp(&self, game: &Game) -> i32 { + let base_max_hp = self.fighter.map_or(0, |f| f.base_max_hp); + let bonus: i32 = self + .get_all_equipped(game) + .iter() + .map(|e| e.max_hp_bonus) + .sum(); + base_max_hp + bonus + } } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] @@ -322,10 +366,10 @@ impl Rect { // Combat related properties and methods (monster, player, NPC). #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] struct Fighter { - max_hp: i32, + base_max_hp: i32, hp: i32, - defense: i32, - power: i32, + base_defense: i32, + base_power: i32, xp: i32, on_death: DeathCallback, } @@ -361,6 +405,9 @@ struct Transition { struct Equipment { slot: Slot, equipped: bool, + power_bonus: i32, + defense_bonus: i32, + max_hp_bonus: i32, } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] @@ -419,7 +466,8 @@ enum Item { Lightning, Confuse, Fireball, - Equipment, + Sword, + Shield, } enum UseResult { @@ -536,7 +584,12 @@ Experience to level up: {} Maximum HP: {}, Attack: {}, Defense: {}", - level, fighter.xp, level_up_xp, fighter.max_hp, fighter.power, fighter.defense + level, + fighter.xp, + level_up_xp, + player.max_hp(game), + player.power(game), + player.defense(game), ); msgbox(&msg, CHARACTER_SCREEN_WIDTH, &mut tcod.root); } @@ -717,8 +770,18 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec, level: u32) { item: Item::Confuse, }, Weighted { - weight: 1000, - item: Item::Equipment, + weight: from_dungeon_level(&[Transition { level: 4, value: 5 }], level), + item: Item::Sword, + }, + Weighted { + weight: from_dungeon_level( + &[Transition { + level: 8, + value: 15, + }], + level, + ), + item: Item::Shield, }, ]; @@ -734,10 +797,10 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec, level: u32) { // Create an orc let mut orc = Object::new(x, y, 'o', "orc", DESATURATED_GREEN, true); orc.fighter = Some(Fighter { - max_hp: 20, + base_max_hp: 20, hp: 20, - defense: 0, - power: 4, + base_defense: 0, + base_power: 4, xp: 35, on_death: DeathCallback::Monster, }); @@ -749,10 +812,10 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec, level: u32) { // Create a troll let mut troll = Object::new(x, y, 'T', "troll", DARKER_GREEN, true); troll.fighter = Some(Fighter { - max_hp: 30, + base_max_hp: 30, hp: 30, - defense: 2, - power: 8, + base_defense: 2, + base_power: 8, xp: 100, on_death: DeathCallback::Monster, }); @@ -803,13 +866,29 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec, level: u32) { object.item = Some(Item::Confuse); object } - Item::Equipment => { + Item::Sword => { // Create a sword let mut object = Object::new(x, y, '/', "sword", SKY, false); - object.item = Some(Item::Equipment); + object.item = Some(Item::Sword); object.equipment = Some(Equipment { equipped: false, slot: Slot::RightHand, + max_hp_bonus: 0, + power_bonus: 3, + defense_bonus: 0, + }); + object + } + Item::Shield => { + // Create a shield + let mut object = Object::new(x, y, '[', "shield", DARKER_ORANGE, false); + object.item = Some(Item::Shield); + object.equipment = Some(Equipment { + equipped: false, + slot: Slot::LeftHand, + max_hp_bonus: 0, + defense_bonus: 1, + power_bonus: 0, }); object } @@ -1156,7 +1235,8 @@ fn use_item(inventory_id: usize, tcod: &mut Tcod, game: &mut Game, objects: &mut Lightning => cast_lightning, Confuse => cast_confuse, Fireball => cast_fireball, - Equipment => toggle_equipment, + Sword => toggle_equipment, + Shield => toggle_equipment, }; match on_use(inventory_id, tcod, game, objects) { UseResult::UsedUp => { @@ -1183,8 +1263,9 @@ fn cast_heal( objects: &mut [Object], ) -> UseResult { // Heal the player - if let Some(fighter) = objects[PLAYER].fighter { - if fighter.hp == fighter.max_hp { + let player = &mut objects[PLAYER]; + if let Some(fighter) = player.fighter { + if fighter.hp == player.max_hp(game) { game.messages.add("You are already at full health.", RED); return UseResult::Cancelled; } @@ -1192,7 +1273,7 @@ fn cast_heal( game .messages .add("Your wounds start to feel better!", LIGHT_VIOLET); - objects[PLAYER].heal(HEAL_AMOUNT); + player.heal(HEAL_AMOUNT, game); return UseResult::UsedUp; } UseResult::Cancelled @@ -1496,7 +1577,7 @@ fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recomput // Show the player's stats let hp = objects[PLAYER].fighter.map_or(0, |f| f.hp); - let max_hp = objects[PLAYER].fighter.map_or(0, |f| f.max_hp); + let max_hp = objects[PLAYER].max_hp(game); render_bar( &mut tcod.panel, 1, @@ -1572,10 +1653,10 @@ fn new_game(tcod: &mut Tcod) -> (Game, Vec) { let mut player = Object::new(0, 0, '@', "player", WHITE, true); player.alive = true; player.fighter = Some(Fighter { - max_hp: 100, + base_max_hp: 100, hp: 100, - defense: 1, - power: 4, + base_defense: 1, + base_power: 2, xp: 0, on_death: DeathCallback::Player, }); @@ -1591,6 +1672,18 @@ fn new_game(tcod: &mut Tcod) -> (Game, Vec) { dungeon_level: 1, }; + // Initial equipment: a dagger + let mut dagger = Object::new(0, 0, '-', "dagger", SKY, false); + dagger.item = Some(Item::Sword); + dagger.equipment = Some(Equipment { + equipped: true, + slot: Slot::LeftHand, + max_hp_bonus: 0, + defense_bonus: 0, + power_bonus: 2, + }); + game.inventory.push(dagger); + initialise_fov(tcod, &game.map); // A warm welcoming message! @@ -1729,8 +1822,8 @@ fn next_level(tcod: &mut Tcod, game: &mut Game, objects: &mut Vec) { VIOLET, ); - let heal_hp = objects[PLAYER].fighter.map_or(0, |f| f.max_hp / 2); - objects[PLAYER].heal(heal_hp); + let heal_hp = objects[PLAYER].max_hp(game) / 2; + objects[PLAYER].heal(heal_hp, game); game.messages.add( "After a rare moment of peace, you descend deeper into \ @@ -1765,9 +1858,9 @@ fn level_up(tcod: &mut Tcod, game: &mut Game, objects: &mut [Object]) { choice = menu( "Level up! Choose a stat to raise:\n", &[ - format!("Constitution (+ 20 HP, from {}", fighter.max_hp), - format!("Strength (+1 attack, from {}", fighter.power), - format!("Agility (+1 defense, from {}", fighter.defense), + format!("Constitution (+ 20 HP, from {}", fighter.base_max_hp), + format!("Strength (+1 attack, from {}", fighter.base_power), + format!("Agility (+1 defense, from {}", fighter.base_defense), ], LEVEL_SCREEN_WIDTH, &mut tcod.root, @@ -1776,16 +1869,16 @@ fn level_up(tcod: &mut Tcod, game: &mut Game, objects: &mut [Object]) { fighter.xp -= level_up_xp; match choice.unwrap() { 0 => { - fighter.max_hp += 20; + fighter.base_max_hp += 20; fighter.hp += 20; } 1 => { - fighter.power += 1; + fighter.base_power += 1; } 2 => { - fighter.defense += 1; + fighter.base_defense += 1; } _ => unreachable!(), }