From 93b270c5f331e2561d6b421767cd178c49efb504 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 13 Nov 2021 00:44:35 +0100 Subject: [PATCH] Fireball constants --- src/main.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/main.rs b/src/main.rs index 6654bd0..ae93c08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,6 +53,8 @@ const LIGHTNING_DAMAGE: i32 = 40; const LIGHTNING_RANGE: i32 = 5; const CONFUSE_RANGE: i32 = 8; const CONFUSE_NUM_TURNS: i32 = 10; +const FIREBALL_RADIUS: i32 = 3; +const FIREBALL_DAMAGE: i32 = 12; struct Tcod { root: Root, @@ -164,6 +166,11 @@ impl Object { } } } + + // Return the distance to some coordinates + pub fn distance(&self, x: i32, y: i32) -> f32 { + (((x - self.x).pow(2) + (y - self.y).pow(2)) as f32).sqrt() + } } #[derive(Clone, Copy, Debug)] @@ -918,6 +925,44 @@ fn cast_confuse( } } +fn cast_fireball( + _inventory_id: usize, + tcod: &mut Tcod, + game: &mut Game, + objects: &mut [Object], +) -> UseResult { + // Ask the player for a target tile to throw a fireball at + game.messages.add( + "Left-click a target tile for the fireball, or right-click to cancel.", + LIGHT_CYAN, + ); + let (x, y) = match target_tile(tcod, game, objects, None) { + Some(tile_pos) => tile_pos, + None => return UseResult::Canceled, + }; + game.messages.add( + format!( + "The fireball explodes, burning everything within {} tiles!", + FIREBALL_RADIUS + ), + ORANGE, + ); + + for obj in objects { + if obj.distance(x, y) <= FIREBALL_RADIUS as f32 && obj.fighter.is_some() { + game.messages.add( + format!( + "The {} gets burned for {} hit points.", + obj.name, FIREBALL_DAMAGE + ), + ORANGE, + ); + obj.take_damage(FIREBALL_DAMAGE, game); + } + } + UseResult::UsedUp +} + // Find closest enemy, up to a maximum range, and in the player's FOV fn closest_monster(tcod: &Tcod, objects: &[Object], max_range: i32) -> Option { let mut closest_enemy = None; @@ -942,6 +987,44 @@ fn closest_monster(tcod: &Tcod, objects: &[Object], max_range: i32) -> Option, +) -> Option<(i32, i32)> { + use tcod::input::KeyCode::Escape; + + loop { + // Render the screen. This erases the inventory and shows the names of + // objects under the mouse. + tcod.root.flush(); + let event = input::check_for_event(input::KEY_PRESS | input::MOUSE).map(|e| e.1); + match event { + Some(Event::Mouse(m)) => tcod.mouse = m, + Some(Event::Key(k)) => tcod.key = k, + None => tcod.key = Default::default(), + } + render_all(tcod, game, objects, false); + + let (x, y) = (tcod.mouse.cx as i32, tcod.mouse.cy as i32); + + // Accept the target if the player clicked in FOV, and in case a range + // is specified, if it's in that range + let in_fov = (x < MAP_WIDTH) && (y < MAP_HEIGHT) && tcod.fov.is_in_fov(x, y); + let in_range = max_range.map_or(true, |range| objects[PLAYER].distance(x, y) <= range); + if tcod.mouse.lbutton_pressed && in_fov && in_range { + return Some((x, y)); + } + + if tcod.mouse.rbutton_pressed || tcod.key.code == Escape { + return None; // Cancel if the player right-clicked or pressed escape. + } + } +} + fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recompute: bool) { if fov_recompute { let player = &objects[PLAYER];