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;
|
let dy = other.y - self.y;
|
||||||
((dx.pow(2) + dy.pow(2)) as f32).sqrt()
|
((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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@ -163,6 +198,7 @@ struct Fighter {
|
|||||||
hp: i32,
|
hp: i32,
|
||||||
defense: i32,
|
defense: i32,
|
||||||
power: i32,
|
power: i32,
|
||||||
|
on_death: DeathCallback,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
@ -177,6 +213,23 @@ enum Ai {
|
|||||||
Basic,
|
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 {
|
fn handle_keys(tcod: &mut Tcod, game: &Game, objects: &mut Vec<Object>) -> PlayerAction {
|
||||||
use tcod::input::Key;
|
use tcod::input::Key;
|
||||||
use tcod::input::KeyCode::*;
|
use tcod::input::KeyCode::*;
|
||||||
@ -305,6 +358,7 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>) {
|
|||||||
hp: 10,
|
hp: 10,
|
||||||
defense: 0,
|
defense: 0,
|
||||||
power: 3,
|
power: 3,
|
||||||
|
on_death: DeathCallback::Monster,
|
||||||
});
|
});
|
||||||
orc.ai = Some(Ai::Basic);
|
orc.ai = Some(Ai::Basic);
|
||||||
orc
|
orc
|
||||||
@ -316,6 +370,7 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>) {
|
|||||||
hp: 16,
|
hp: 16,
|
||||||
defense: 1,
|
defense: 1,
|
||||||
power: 4,
|
power: 4,
|
||||||
|
on_death: DeathCallback::Monster,
|
||||||
});
|
});
|
||||||
troll.ai = Some(Ai::Basic);
|
troll.ai = Some(Ai::Basic);
|
||||||
troll
|
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;
|
let y = objects[PLAYER].y + dy;
|
||||||
|
|
||||||
// Try to find an attackable object there
|
// 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
|
// Attack target if found, move otherwise
|
||||||
match target_id {
|
match target_id {
|
||||||
Some(target_id) => {
|
Some(target_id) => {
|
||||||
println!(
|
let (player, target) = mut_two(PLAYER, target_id, objects);
|
||||||
"The {} laughs at your puny efforts to attack him!",
|
player.attack(target);
|
||||||
objects[target_id].name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
move_by(PLAYER, dx, dy, &game.map, objects);
|
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);
|
move_towards(monster_id, player_x, player_y, &game.map, objects);
|
||||||
} else if objects[PLAYER].fighter.map_or(false, |f| f.hp > 0) {
|
} else if objects[PLAYER].fighter.map_or(false, |f| f.hp > 0) {
|
||||||
// Close enough, attach! (if the player is still alive.)
|
// Close enough, attach! (if the player is still alive.)
|
||||||
let monster = &objects[monster_id];
|
let (monster, player) = mut_two(monster_id, PLAYER, objects);
|
||||||
println!(
|
monster.attack(player);
|
||||||
"The attack of the {} bounces off your shiny metal armor!",
|
|
||||||
monster.name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recompute: bool) {
|
||||||
if fov_recompute {
|
if fov_recompute {
|
||||||
let player = &objects[PLAYER];
|
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);
|
.compute_fov(player.x, player.y, TORCH_RADIUS, FOV_LIGHT_WALLS, FOV_ALGO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw all objects in the list
|
// Only render objects in fov
|
||||||
for object in objects {
|
let mut to_draw: Vec<_> = objects
|
||||||
if tcod.fov.is_in_fov(object.x, object.y) {
|
.iter()
|
||||||
object.draw(&mut tcod.con);
|
.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
|
// 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 contents of "con" to the root console
|
||||||
blit(
|
blit(
|
||||||
&tcod.con,
|
&tcod.con,
|
||||||
@ -475,6 +578,7 @@ fn main() {
|
|||||||
hp: 30,
|
hp: 30,
|
||||||
defense: 2,
|
defense: 2,
|
||||||
power: 5,
|
power: 5,
|
||||||
|
on_death: DeathCallback::Player,
|
||||||
});
|
});
|
||||||
|
|
||||||
// List of objects in the world
|
// List of objects in the world
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user