Done.
This commit is contained in:
parent
e38e119562
commit
f452ccebe0
175
src/main.rs
175
src/main.rs
@ -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!(),
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user