From b652bd512d7359152608d485fba9d6131e25814e Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Wed, 27 Oct 2021 00:02:09 +0200 Subject: [PATCH] Sword-fighting --- src/main.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index f725d40..e8b794e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,6 +54,8 @@ struct Object { name: String, blocks: bool, alive: bool, + fighter: Option, + ai: Option, } impl Object { @@ -66,6 +68,8 @@ impl Object { name: name.into(), blocks: blocks, alive: false, + fighter: None, + ai: None, } } @@ -83,6 +87,12 @@ impl Object { self.x = x; self.y = y; } + + pub fn distance_to(&self, other: &Object) -> f32 { + let dx = other.x - self.x; + let dy = other.y - self.y; + ((dx.pow(2) + dy.pow(2)) as f32).sqrt() + } } #[derive(Clone, Copy, Debug)] @@ -146,6 +156,15 @@ impl Rect { } } +// Combat related properties and methods (monster, player, NPC). +#[derive(Clone, Copy, Debug, PartialEq)] +struct Fighter { + max_hp: i32, + hp: i32, + defense: i32, + power: i32, +} + #[derive(Clone, Copy, Debug, PartialEq)] enum PlayerAction { TookTurn, @@ -153,6 +172,11 @@ enum PlayerAction { Exit, } +#[derive(Clone, Debug, PartialEq)] +enum Ai { + Basic, +} + fn handle_keys(tcod: &mut Tcod, game: &Game, objects: &mut Vec) -> PlayerAction { use tcod::input::Key; use tcod::input::KeyCode::*; @@ -275,10 +299,26 @@ fn place_objects(room: Rect, map: &Map, objects: &mut Vec) { if !is_blocked(x, y, map, objects) { let mut monster = if rand::random::() < 0.8 { // 80% chance of spawning an 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 { + max_hp: 10, + hp: 10, + defense: 0, + power: 3, + }); + orc.ai = Some(Ai::Basic); + orc } else { // 20% chance of spawning a 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 { + max_hp: 16, + hp: 16, + defense: 1, + power: 4, + }); + troll.ai = Some(Ai::Basic); + troll }; monster.alive = true; @@ -327,6 +367,38 @@ fn player_move_or_attack(dx: i32, dy: i32, game: &Game, objects: &mut [Object]) } } +fn move_towards(id: usize, target_x: i32, target_y: i32, map: &Map, objects: &mut [Object]) { + // Vector from this object to the target, and distance + let dx = target_x - objects[id].x; + let dy = target_y - objects[id].y; + let distance = ((dx.pow(2) + dy.pow(2)) as f32).sqrt(); + + // Normalize it to length 1 (preserving direction), then round it and + // convert it to integer so the movement is restricted to the map grid + let dx = (dx as f32 / distance).round() as i32; + let dy = (dy as f32 / distance).round() as i32; + move_by(id, dx, dy, map, objects); +} + +fn ai_take_turn(monster_id: usize, tcod: &Tcod, game: &Game, objects: &mut [Object]) { + // A basic monster takes its turn. If you can see it, it can see you. + let (monster_x, monster_y) = objects[monster_id].pos(); + if tcod.fov.is_in_fov(monster_x, monster_y) { + if objects[monster_id].distance_to(&objects[PLAYER]) >= 2.0 { + // Move towards player if far away + let (player_x, player_y) = objects[PLAYER].pos(); + 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 + ); + } + } +} + fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recompute: bool) { if fov_recompute { let player = &objects[PLAYER]; @@ -398,6 +470,12 @@ fn main() { // Create player object let mut player = Object::new(0, 0, '@', "player", WHITE, true); player.alive = true; + player.fighter = Some(Fighter { + max_hp: 30, + hp: 30, + defense: 2, + power: 5, + }); // List of objects in the world let mut objects = vec![player]; @@ -437,10 +515,9 @@ fn main() { // Let monsters take their turn if objects[PLAYER].alive && player_action != PlayerAction::DidntTakeTurn { - for object in &objects { - // ONly if object is not player - if (object as *const _) != (&objects[PLAYER] as *const _) { - println!("The {} growls!", object.name); + for id in 0..objects.len() { + if objects[id].ai.is_some() { + ai_take_turn(id, &tcod, &game, &mut objects); } } }