This commit is contained in:
Michael Smith 2021-11-20 23:38:23 +01:00
parent e38e119562
commit f452ccebe0

View File

@ -1,11 +1,10 @@
// #![allow(unused_imports)] #![allow(unused_imports)]
// #![allow(unused_variables)] #![allow(unused_variables)]
// #![allow(unused_mut)] #![allow(unused_mut)]
// #![allow(dead_code)] #![allow(dead_code)]
use std::cmp;
use rand::Rng; use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp;
use std::error::Error; use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
@ -165,7 +164,7 @@ impl Object {
pub fn attack(&mut self, target: &mut Object, game: &mut Game) { pub fn attack(&mut self, target: &mut Object, game: &mut Game) {
// A simple formula for attack damage // 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 { if damage > 0 {
// Make the target take some damage // Make the target take some damage
game.messages.add( 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 { if let Some(ref mut fighter) = self.fighter {
fighter.hp += amount; fighter.hp += amount;
if fighter.hp > fighter.max_hp { if fighter.hp > max_hp {
fighter.hp = fighter.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<Equipment> {
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)] #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
@ -322,10 +366,10 @@ impl Rect {
// Combat related properties and methods (monster, player, NPC). // Combat related properties and methods (monster, player, NPC).
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
struct Fighter { struct Fighter {
max_hp: i32, base_max_hp: i32,
hp: i32, hp: i32,
defense: i32, base_defense: i32,
power: i32, base_power: i32,
xp: i32, xp: i32,
on_death: DeathCallback, on_death: DeathCallback,
} }
@ -361,6 +405,9 @@ struct Transition {
struct Equipment { struct Equipment {
slot: Slot, slot: Slot,
equipped: bool, equipped: bool,
power_bonus: i32,
defense_bonus: i32,
max_hp_bonus: i32,
} }
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
@ -419,7 +466,8 @@ enum Item {
Lightning, Lightning,
Confuse, Confuse,
Fireball, Fireball,
Equipment, Sword,
Shield,
} }
enum UseResult { enum UseResult {
@ -536,7 +584,12 @@ Experience to level up: {}
Maximum HP: {}, Maximum HP: {},
Attack: {}, Attack: {},
Defense: {}", 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); msgbox(&msg, CHARACTER_SCREEN_WIDTH, &mut tcod.root);
} }
@ -717,8 +770,18 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>, level: u32) {
item: Item::Confuse, item: Item::Confuse,
}, },
Weighted { Weighted {
weight: 1000, weight: from_dungeon_level(&[Transition { level: 4, value: 5 }], level),
item: Item::Equipment, 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<Object>, level: u32) {
// Create an orc // Create an orc
let mut orc = Object::new(x, y, 'o', "orc", DESATURATED_GREEN, true); let mut orc = Object::new(x, y, 'o', "orc", DESATURATED_GREEN, true);
orc.fighter = Some(Fighter { orc.fighter = Some(Fighter {
max_hp: 20, base_max_hp: 20,
hp: 20, hp: 20,
defense: 0, base_defense: 0,
power: 4, base_power: 4,
xp: 35, xp: 35,
on_death: DeathCallback::Monster, on_death: DeathCallback::Monster,
}); });
@ -749,10 +812,10 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>, level: u32) {
// Create a troll // Create a troll
let mut troll = Object::new(x, y, 'T', "troll", DARKER_GREEN, true); let mut troll = Object::new(x, y, 'T', "troll", DARKER_GREEN, true);
troll.fighter = Some(Fighter { troll.fighter = Some(Fighter {
max_hp: 30, base_max_hp: 30,
hp: 30, hp: 30,
defense: 2, base_defense: 2,
power: 8, base_power: 8,
xp: 100, xp: 100,
on_death: DeathCallback::Monster, on_death: DeathCallback::Monster,
}); });
@ -803,13 +866,29 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>, level: u32) {
object.item = Some(Item::Confuse); object.item = Some(Item::Confuse);
object object
} }
Item::Equipment => { Item::Sword => {
// Create a sword // Create a sword
let mut object = Object::new(x, y, '/', "sword", SKY, false); let mut object = Object::new(x, y, '/', "sword", SKY, false);
object.item = Some(Item::Equipment); object.item = Some(Item::Sword);
object.equipment = Some(Equipment { object.equipment = Some(Equipment {
equipped: false, equipped: false,
slot: Slot::RightHand, 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 object
} }
@ -1156,7 +1235,8 @@ fn use_item(inventory_id: usize, tcod: &mut Tcod, game: &mut Game, objects: &mut
Lightning => cast_lightning, Lightning => cast_lightning,
Confuse => cast_confuse, Confuse => cast_confuse,
Fireball => cast_fireball, Fireball => cast_fireball,
Equipment => toggle_equipment, Sword => toggle_equipment,
Shield => toggle_equipment,
}; };
match on_use(inventory_id, tcod, game, objects) { match on_use(inventory_id, tcod, game, objects) {
UseResult::UsedUp => { UseResult::UsedUp => {
@ -1183,8 +1263,9 @@ fn cast_heal(
objects: &mut [Object], objects: &mut [Object],
) -> UseResult { ) -> UseResult {
// Heal the player // Heal the player
if let Some(fighter) = objects[PLAYER].fighter { let player = &mut objects[PLAYER];
if fighter.hp == fighter.max_hp { if let Some(fighter) = player.fighter {
if fighter.hp == player.max_hp(game) {
game.messages.add("You are already at full health.", RED); game.messages.add("You are already at full health.", RED);
return UseResult::Cancelled; return UseResult::Cancelled;
} }
@ -1192,7 +1273,7 @@ fn cast_heal(
game game
.messages .messages
.add("Your wounds start to feel better!", LIGHT_VIOLET); .add("Your wounds start to feel better!", LIGHT_VIOLET);
objects[PLAYER].heal(HEAL_AMOUNT); player.heal(HEAL_AMOUNT, game);
return UseResult::UsedUp; return UseResult::UsedUp;
} }
UseResult::Cancelled UseResult::Cancelled
@ -1496,7 +1577,7 @@ fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recomput
// Show the player's stats // Show the player's stats
let hp = objects[PLAYER].fighter.map_or(0, |f| f.hp); 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( render_bar(
&mut tcod.panel, &mut tcod.panel,
1, 1,
@ -1572,10 +1653,10 @@ fn new_game(tcod: &mut Tcod) -> (Game, Vec<Object>) {
let mut player = Object::new(0, 0, '@', "player", WHITE, true); let mut player = Object::new(0, 0, '@', "player", WHITE, true);
player.alive = true; player.alive = true;
player.fighter = Some(Fighter { player.fighter = Some(Fighter {
max_hp: 100, base_max_hp: 100,
hp: 100, hp: 100,
defense: 1, base_defense: 1,
power: 4, base_power: 2,
xp: 0, xp: 0,
on_death: DeathCallback::Player, on_death: DeathCallback::Player,
}); });
@ -1591,6 +1672,18 @@ fn new_game(tcod: &mut Tcod) -> (Game, Vec<Object>) {
dungeon_level: 1, 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); initialise_fov(tcod, &game.map);
// A warm welcoming message! // A warm welcoming message!
@ -1729,8 +1822,8 @@ fn next_level(tcod: &mut Tcod, game: &mut Game, objects: &mut Vec<Object>) {
VIOLET, VIOLET,
); );
let heal_hp = objects[PLAYER].fighter.map_or(0, |f| f.max_hp / 2); let heal_hp = objects[PLAYER].max_hp(game) / 2;
objects[PLAYER].heal(heal_hp); objects[PLAYER].heal(heal_hp, game);
game.messages.add( game.messages.add(
"After a rare moment of peace, you descend deeper into \ "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( choice = menu(
"Level up! Choose a stat to raise:\n", "Level up! Choose a stat to raise:\n",
&[ &[
format!("Constitution (+ 20 HP, from {}", fighter.max_hp), format!("Constitution (+ 20 HP, from {}", fighter.base_max_hp),
format!("Strength (+1 attack, from {}", fighter.power), format!("Strength (+1 attack, from {}", fighter.base_power),
format!("Agility (+1 defense, from {}", fighter.defense), format!("Agility (+1 defense, from {}", fighter.base_defense),
], ],
LEVEL_SCREEN_WIDTH, LEVEL_SCREEN_WIDTH,
&mut tcod.root, &mut tcod.root,
@ -1776,16 +1869,16 @@ fn level_up(tcod: &mut Tcod, game: &mut Game, objects: &mut [Object]) {
fighter.xp -= level_up_xp; fighter.xp -= level_up_xp;
match choice.unwrap() { match choice.unwrap() {
0 => { 0 => {
fighter.max_hp += 20; fighter.base_max_hp += 20;
fighter.hp += 20; fighter.hp += 20;
} }
1 => { 1 => {
fighter.power += 1; fighter.base_power += 1;
} }
2 => { 2 => {
fighter.defense += 1; fighter.base_defense += 1;
} }
_ => unreachable!(), _ => unreachable!(),
} }