Status bar

This commit is contained in:
Michael Smith 2021-10-27 21:40:42 +02:00
parent b652bd512d
commit 5b4f28a7c8

View File

@ -93,6 +93,41 @@ impl Object {
let dy = other.y - self.y;
((dx.pow(2) + dy.pow(2)) as f32).sqrt()
}
pub fn take_damage(&mut self, damage: i32) {
// Apply damage if possible
if let Some(fighter) = self.fighter.as_mut() {
if damage > 0 {
fighter.hp -= damage;
}
}
// Check for death, call the death function
if let Some(fighter) = self.fighter {
if fighter.hp <= 0 {
self.alive = false;
fighter.on_death.callback(self);
}
}
}
pub fn attack(&mut self, target: &mut Object) {
// A simple formula for attack damage
let damage = self.fighter.map_or(0, |f| f.power) - target.fighter.map_or(0, |f| f.defense);
if damage > 0 {
// Make the target take some damage
println!(
"{} attacks {} for {} hit points.",
self.name, target.name, damage
);
target.take_damage(damage);
} else {
println!(
"{} attacks {} but it has no effect!",
self.name, target.name
)
}
}
}
#[derive(Clone, Copy, Debug)]
@ -163,6 +198,7 @@ struct Fighter {
hp: i32,
defense: i32,
power: i32,
on_death: DeathCallback,
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -177,6 +213,23 @@ enum Ai {
Basic,
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum DeathCallback {
Player,
Monster,
}
impl DeathCallback {
fn callback(self, object: &mut Object) {
use DeathCallback::*;
let callback: fn(&mut Object) = match self {
Player => player_death,
Monster => monster_death,
};
callback(object);
}
}
fn handle_keys(tcod: &mut Tcod, game: &Game, objects: &mut Vec<Object>) -> PlayerAction {
use tcod::input::Key;
use tcod::input::KeyCode::*;
@ -305,6 +358,7 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>) {
hp: 10,
defense: 0,
power: 3,
on_death: DeathCallback::Monster,
});
orc.ai = Some(Ai::Basic);
orc
@ -316,6 +370,7 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>) {
hp: 16,
defense: 1,
power: 4,
on_death: DeathCallback::Monster,
});
troll.ai = Some(Ai::Basic);
troll
@ -351,15 +406,15 @@ fn player_move_or_attack(dx: i32, dy: i32, game: &Game, objects: &mut [Object])
let y = objects[PLAYER].y + dy;
// Try to find an attackable object there
let target_id = objects.iter().position(|object| object.pos() == (x, y));
let target_id = objects
.iter()
.position(|object| object.fighter.is_some() && object.pos() == (x, y));
// Attack target if found, move otherwise
match target_id {
Some(target_id) => {
println!(
"The {} laughs at your puny efforts to attack him!",
objects[target_id].name
);
let (player, target) = mut_two(PLAYER, target_id, objects);
player.attack(target);
}
None => {
move_by(PLAYER, dx, dy, &game.map, objects);
@ -390,15 +445,44 @@ fn ai_take_turn(monster_id: usize, tcod: &Tcod, game: &Game, objects: &mut [Obje
move_towards(monster_id, player_x, player_y, &game.map, objects);
} else if objects[PLAYER].fighter.map_or(false, |f| f.hp > 0) {
// Close enough, attach! (if the player is still alive.)
let monster = &objects[monster_id];
println!(
"The attack of the {} bounces off your shiny metal armor!",
monster.name
);
let (monster, player) = mut_two(monster_id, PLAYER, objects);
monster.attack(player);
}
}
}
fn mut_two<T>(first_index: usize, second_index: usize, items: &mut [T]) -> (&mut T, &mut T) {
assert!(first_index != second_index);
let split_at_index = cmp::max(first_index, second_index);
let (first_slice, second_slice) = items.split_at_mut(split_at_index);
if first_index < second_index {
(&mut first_slice[first_index], &mut second_slice[0])
} else {
(&mut second_slice[0], &mut first_slice[second_index])
}
}
fn player_death(player: &mut Object) {
// The game ended!
println!("You died!");
// For added effect, transform the player into corpse!
player.char = '%';
player.color = DARK_RED;
}
fn monster_death(monster: &mut Object) {
// Transform it into a nasty corpse! It doesn't block, can't be
// attacked and doesn't move
println!("{} is dead!", monster.name);
monster.char = '%';
monster.color = DARK_RED;
monster.blocks = false;
monster.fighter = None;
monster.ai = None;
monster.name = format!("remains of {}", monster.name);
}
fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recompute: bool) {
if fov_recompute {
let player = &objects[PLAYER];
@ -407,11 +491,18 @@ fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recomput
.compute_fov(player.x, player.y, TORCH_RADIUS, FOV_LIGHT_WALLS, FOV_ALGO);
}
// Draw all objects in the list
for object in objects {
if tcod.fov.is_in_fov(object.x, object.y) {
object.draw(&mut tcod.con);
}
// Only render objects in fov
let mut to_draw: Vec<_> = objects
.iter()
.filter(|o| tcod.fov.is_in_fov(o.x, o.y))
.collect();
// Sort so that non-blocking objects are rendered behind blocking objects
to_draw.sort_by(|o1, o2| o1.blocks.cmp(&o2.blocks));
// Draw objects in the list
for object in &to_draw {
object.draw(&mut tcod.con);
}
// Go through all tiles and set their background color
@ -439,6 +530,18 @@ fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recomput
}
}
// Show the player's stats
tcod.root.set_default_foreground(WHITE);
if let Some(fighter) = objects[PLAYER].fighter {
tcod.root.print_ex(
1,
SCREEN_HEIGHT - 2,
BackgroundFlag::None,
TextAlignment::Left,
format!("HP: {}/{} ", fighter.hp, fighter.max_hp),
);
}
// Blit contents of "con" to the root console
blit(
&tcod.con,
@ -475,6 +578,7 @@ fn main() {
hp: 30,
defense: 2,
power: 5,
on_death: DeathCallback::Player,
});
// List of objects in the world