Status bar
This commit is contained in:
parent
b652bd512d
commit
5b4f28a7c8
134
src/main.rs
134
src/main.rs
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user