diff --git a/src/constants.rs b/src/constants.rs index 2a90aec..fa6e024 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -13,3 +13,5 @@ pub const MAP_SIZE: usize = MAP_WIDTH * MAP_HEIGHT; pub const MAP_X: usize = 1; pub const MAP_Y: usize = 1; + +pub const BASE_WHIP_POWER: u32 = 2; diff --git a/src/graphics/sidebar.rs b/src/graphics/sidebar.rs index abb7634..765a64c 100644 --- a/src/graphics/sidebar.rs +++ b/src/graphics/sidebar.rs @@ -1,4 +1,4 @@ -use crate::constants::{SIDEBAR_POS_X, SIDEBAR_POS_Y}; +use crate::constants::*; use crate::graphics::vga_color as vga; use crate::resources::Resources; use bracket_lib::prelude::*; @@ -48,7 +48,15 @@ pub fn draw(resources: &Resources, bterm: &mut BTerm) { let stats = &resources.stats; bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 1, stats.score * 10); bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 7, stats.gems); - bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 10, stats.whips); + bterm.print_centered_at( + SIDEBAR_POS_X + 6, + SIDEBAR_POS_Y + 10, + if stats.whip_power <= BASE_WHIP_POWER { + stats.whips.to_string() + } else { + format!("{}+{}", stats.whips, stats.whip_power - BASE_WHIP_POWER) + }, + ); bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 13, stats.teleports); bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 16, stats.keys); diff --git a/src/input/mod.rs b/src/input/mod.rs index 54f2519..e3887d0 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -25,6 +25,9 @@ pub fn handle_intent(bterm: &mut BTerm, _world: &mut World, resources: &mut Reso FlashingMessageIntent::Save => todo!(), FlashingMessageIntent::Restore => todo!(), FlashingMessageIntent::Restart => todo!(), + &FlashingMessageIntent::NextLevel => { + resources.should_advance_level = true; + } } } } diff --git a/src/input/player.rs b/src/input/player.rs index e8785e4..cef23c5 100644 --- a/src/input/player.rs +++ b/src/input/player.rs @@ -5,7 +5,12 @@ use bracket_lib::prelude::*; use hecs::{Entity, With, Without, World}; use crate::{ - components::monster::*, components::*, constants::*, resources::*, systems, tile_data::TileType, + components::monster::*, + components::*, + constants::*, + resources::{flashing_message::FlashingMessageIntent, *}, + systems, + tile_data::TileType, }; pub fn try_move(delta_x: i32, delta_y: i32, world: &mut World, resources: &mut Resources) { @@ -60,6 +65,12 @@ pub fn try_move(delta_x: i32, delta_y: i32, world: &mut World, resources: &mut R } else if let (Some(sound_effects), Some(sound_output)) = (&mut resources.sound_effects, &mut resources.sound_output) { + if !resources.message_shown.touched_boundary { + resources.flashing_message = Some(FlashingMessage::from( + "An Electrified Wall blocks your way.", + )); + resources.message_shown.touched_boundary = true; + } let static_sound = sound_effects.get_new_static_effect(sound_output); sound_output.play_sound(static_sound); } @@ -78,16 +89,45 @@ fn try_step(point: Point, _world: &World, resources: &mut Resources) -> bool { crate::tile_data::TileType::Floor | crate::tile_data::TileType::Slow | crate::tile_data::TileType::Medium - | crate::tile_data::TileType::Fast => true, - crate::tile_data::TileType::Block | crate::tile_data::TileType::Wall => { + | crate::tile_data::TileType::Fast + | crate::tile_data::TileType::Player => true, + crate::tile_data::TileType::Block => { if let (Some(sound_effects), Some(sound_output)) = (&mut resources.sound_effects, &mut resources.sound_output) { sound_output.play_sound(sound_effects.blocked.clone()); } + + if !resources.message_shown.touched_boundary { + resources.flashing_message = Some(FlashingMessage::from( + "An Electrified Wall blocks your way.", + )); + resources.message_shown.touched_boundary = true; + } + if resources.stats.score > 2 { resources.stats.take_score(2); } + + false + } + crate::tile_data::TileType::Wall => { + if let (Some(sound_effects), Some(sound_output)) = + (&mut resources.sound_effects, &mut resources.sound_output) + { + sound_output.play_sound(sound_effects.blocked.clone()); + } + + if !resources.message_shown.touched_boundary { + resources.flashing_message = + Some(FlashingMessage::from("A Solid Wall blocks your way.")); + resources.message_shown.touched_boundary = true; + } + + if resources.stats.score > 2 { + resources.stats.take_score(2); + } + false } crate::tile_data::TileType::Whip => { @@ -96,13 +136,27 @@ fn try_step(point: Point, _world: &World, resources: &mut Resources) -> bool { { sound_output.play_sound(sound_effects.grab.clone()); } + + if !resources.message_shown.whip { + resources.flashing_message = Some(FlashingMessage::from("You found a Whip.")); + resources.message_shown.whip = true; + } + resources.stats.give_whips(1); resources.stats.add_score(1); resources.map.set_tile_at(point, TileType::Floor); true } crate::tile_data::TileType::Stairs => { - resources.should_advance_level = true; + if !resources.message_shown.stairs { + resources.flashing_message = Some(FlashingMessage::new( + "Stairs take you to the next lower level.", + Some(FlashingMessageIntent::NextLevel), + )); + resources.message_shown.stairs = true; + } else { + resources.should_advance_level = true; + } true } crate::tile_data::TileType::Chest => todo!(), @@ -130,6 +184,14 @@ fn try_step(point: Point, _world: &World, resources: &mut Resources) -> bool { { sound_output.play_sound(sound_effects.grab.clone()); } + + if !resources.message_shown.gem { + resources.flashing_message = Some(FlashingMessage::from( + "Gems give you both points and strength.", + )); + resources.message_shown.gem = true; + } + resources.stats.give_gems(1); resources.stats.add_score(1); resources.map.set_tile_at(point, TileType::Floor); @@ -142,6 +204,13 @@ fn try_step(point: Point, _world: &World, resources: &mut Resources) -> bool { { sound_output.play_sound(sound_effects.grab.clone()); } + + if !resources.message_shown.teleport_scroll { + resources.flashing_message = + Some(FlashingMessage::from("You found a Teleport scroll.")); + resources.message_shown.teleport_scroll = true; + } + resources.stats.give_teleports(1); resources.stats.add_score(1); resources.map.set_tile_at(point, TileType::Floor); @@ -199,7 +268,6 @@ fn try_step(point: Point, _world: &World, resources: &mut Resources) -> bool { crate::tile_data::TileType::Trap11 => todo!(), crate::tile_data::TileType::Trap12 => todo!(), crate::tile_data::TileType::Trap13 => todo!(), - crate::tile_data::TileType::Player => true, crate::tile_data::TileType::Punctuation => todo!(), crate::tile_data::TileType::Letter(_) => todo!(), } diff --git a/src/resources/difficulty.rs b/src/resources/difficulty.rs index 87add56..ad03e0f 100644 --- a/src/resources/difficulty.rs +++ b/src/resources/difficulty.rs @@ -1,5 +1,14 @@ +use crate::constants::*; + +pub enum Difficulty { + Novice, + Experienced, + Advanced, + Secret, +} + #[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] -pub struct Difficulty { +pub struct DifficultySettings { pub value: u32, pub starting_gems: u32, pub starting_whips: u32, @@ -8,38 +17,49 @@ pub struct Difficulty { pub starting_whip_power: u32, } -pub const SECRET: Difficulty = Difficulty { +impl From<&Difficulty> for DifficultySettings { + fn from(difficulty: &Difficulty) -> Self { + match difficulty { + Difficulty::Novice => NOVICE, + Difficulty::Experienced => EXPERIENCED, + Difficulty::Advanced => ADVANCED, + Difficulty::Secret => SECRET, + } + } +} + +const SECRET: DifficultySettings = DifficultySettings { value: 9, starting_gems: 250, starting_whips: 100, starting_teleports: 50, starting_keys: 1, - starting_whip_power: 3, + starting_whip_power: BASE_WHIP_POWER + 1, }; -pub const NOVICE: Difficulty = Difficulty { +const NOVICE: DifficultySettings = DifficultySettings { value: 8, starting_gems: 20, starting_whips: 10, starting_teleports: 0, starting_keys: 0, - starting_whip_power: 0, + starting_whip_power: BASE_WHIP_POWER, }; -pub const EXPERIENCED: Difficulty = Difficulty { +const EXPERIENCED: DifficultySettings = DifficultySettings { value: 5, starting_gems: 15, starting_whips: 0, starting_teleports: 0, starting_keys: 0, - starting_whip_power: 0, + starting_whip_power: BASE_WHIP_POWER, }; -pub const ADVANCED: Difficulty = Difficulty { +const ADVANCED: DifficultySettings = DifficultySettings { value: 2, starting_gems: 10, starting_whips: 0, starting_teleports: 0, starting_keys: 0, - starting_whip_power: 0, + starting_whip_power: BASE_WHIP_POWER, }; diff --git a/src/resources/flashing_message.rs b/src/resources/flashing_message.rs index 5c83a20..d188c53 100644 --- a/src/resources/flashing_message.rs +++ b/src/resources/flashing_message.rs @@ -9,6 +9,7 @@ pub enum FlashingMessageIntent { Save, Restore, Restart, + NextLevel, } pub struct FlashingMessage { diff --git a/src/resources/message_shown.rs b/src/resources/message_shown.rs index 2bd036f..29a8e48 100644 --- a/src/resources/message_shown.rs +++ b/src/resources/message_shown.rs @@ -1,5 +1,24 @@ #[derive(Debug, Default)] pub struct MessageShown { + pub touched_boundary: bool, pub slow_time: bool, pub speed_time: bool, + pub whip: bool, + pub gem: bool, + pub teleport_scroll: bool, + pub stairs: bool, +} + +impl MessageShown { + pub fn all_shown() -> Self { + MessageShown { + touched_boundary: true, + slow_time: true, + speed_time: true, + whip: true, + gem: true, + teleport_scroll: true, + stairs: true, + } + } } diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 819e479..f546bfd 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -9,10 +9,10 @@ pub mod stats; use bracket_lib::prelude::*; pub use clock::{Clock, StopClock}; -pub use difficulty::Difficulty; +pub use difficulty::DifficultySettings; pub use flashing_message::FlashingMessage; pub use map::Map; -use message_shown::MessageShown; +pub use message_shown::MessageShown; pub use sound_effects::SoundEffects; pub use sound_output::SoundOutput; pub use stats::Stats; @@ -25,7 +25,7 @@ pub struct Resources { pub clock: Clock, pub stop_clock: bool, pub map: Map, - pub selected_difficulty: Option, + pub difficulty: Option, pub sound_effects: Option, pub sound_output: Option, pub flashing_message: Option, diff --git a/src/resources/sound_effects.rs b/src/resources/sound_effects.rs index bcbae52..b0d7638 100644 --- a/src/resources/sound_effects.rs +++ b/src/resources/sound_effects.rs @@ -34,7 +34,8 @@ pub struct SoundEffects { pub blocked: SoundSamples, pub whipping: SoundSamples, pub whipping_hit: SoundSamples, - pub whipping_hit_end: SoundSamples, + pub whipping_hit_enemy: SoundSamples, + pub whipping_hit_block: SoundSamples, pub slow_hit: SoundSamples, pub medium_hit: SoundSamples, pub fast_hit: SoundSamples, @@ -121,23 +122,27 @@ impl SoundEffects { }], }), whipping_hit: sound_output.render_sound_effect(&SoundEffect { - sounds: vec![ - Sound { - sound_type: SoundType::Tone(400), - duration: Duration::from_millis(20), - }, - Sound { - sound_type: SoundType::Tone(90), - duration: Duration::from_secs(3), - }, - ], + sounds: vec![Sound { + sound_type: SoundType::Tone(90), + duration: Duration::from_secs(3), + }], }), - whipping_hit_end: sound_output.render_sound_effect(&SoundEffect { + whipping_hit_enemy: sound_output.render_sound_effect(&SoundEffect { sounds: vec![Sound { sound_type: SoundType::Tone(400), duration: Duration::from_millis(20), }], }), + whipping_hit_block: sound_output.render_sound_effect(&SoundEffect { + sounds: (20..=5700) + .rev() + .step_by(100) + .map(|x| Sound { + sound_type: SoundType::Noise(0, x), + duration: Duration::from_millis(18), + }) + .collect(), + }), slow_hit: sound_output.render_sound_effect(&SoundEffect { sounds: vec![Sound { sound_type: SoundType::Tone(400), diff --git a/src/state.rs b/src/state.rs index ea9186f..39906b3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,10 +1,10 @@ use instant::Instant; use crate::resources::clock::Clock; -use crate::resources::difficulty::*; use crate::resources::flashing_message::{FlashingMessage, FlashingMessageIntent}; use crate::resources::sound_effects::SoundEffects; use crate::resources::stats::Stats; +use crate::resources::{difficulty::*, MessageShown}; use crate::resources::{Map, Resources, SoundOutput}; use crate::{graphics, input, levels, systems}; use bracket_lib::prelude::*; @@ -43,7 +43,8 @@ impl State { } let starting_level = 0; - let selected_difficulty = SECRET; + let selected_difficulty = Difficulty::Secret; + let difficulty_settings = DifficultySettings::from(&selected_difficulty); let mut world = World::new(); @@ -56,11 +57,11 @@ impl State { player_input: None, stats: Stats { score: 0, - gems: selected_difficulty.starting_gems, - whips: selected_difficulty.starting_whips, - whip_power: selected_difficulty.starting_whip_power, - teleports: selected_difficulty.starting_teleports, - keys: selected_difficulty.starting_keys, + gems: difficulty_settings.starting_gems, + whips: difficulty_settings.starting_whips, + whip_power: difficulty_settings.starting_whip_power, + teleports: difficulty_settings.starting_teleports, + keys: difficulty_settings.starting_keys, }, clock: Clock { last_ticked: Instant::now(), @@ -71,12 +72,15 @@ impl State { map, sound_effects, sound_output, - selected_difficulty: Some(selected_difficulty), + difficulty: Some(difficulty_settings), flashing_message: Some(FlashingMessage::new( "Press any key to begin this level.", Some(FlashingMessageIntent::Start), )), - message_shown: Default::default(), + message_shown: match selected_difficulty { + Difficulty::Novice | Difficulty::Experienced => Default::default(), + _ => MessageShown::all_shown(), + }, should_advance_level: false, speed_time_ticks: 0, slow_time_ticks: 0, @@ -92,9 +96,6 @@ impl State { self.resources.map = Map::from(levels::get_level(self.resources.level_number)); self.resources.map.spawn_entities(&mut self.world); - self.resources.flashing_message = - Some(FlashingMessage::from("Press any key to begin this level.")); - self.resources.should_advance_level = false; } } diff --git a/src/systems/whip.rs b/src/systems/whip.rs index 7a4fc83..d9f3db1 100644 --- a/src/systems/whip.rs +++ b/src/systems/whip.rs @@ -7,6 +7,7 @@ use hecs::{Entity, World}; use crate::{ components::{monster::damage_for_kind, Monster, Position, WantsToWhip}, resources::Resources, + tile_data::TileType, }; pub fn run(world: &mut World, resources: &mut Resources) { @@ -73,23 +74,15 @@ pub fn run(world: &mut World, resources: &mut Resources) { resources.stats.add_score(damage_for_kind(monster.kind)); to_kill.push(e); resources.map.clear_tile_content_at(dest); - if let Some(sound) = &mut wants_to_whip.sound { - sound.control::, _>().stop(); - } - if wants_to_whip.frame == 7 { - wants_to_whip.sound = None; - if let (Some(sound_effects), Some(sound_output)) = - (&mut resources.sound_effects, &mut resources.sound_output) - { - sound_output.play_sound(sound_effects.whipping_hit_end.clone()); - } - } else if let (Some(sound_effects), Some(sound_output)) = + if let (Some(sound_effects), Some(sound_output)) = (&mut resources.sound_effects, &mut resources.sound_output) { - wants_to_whip.sound = - Some(sound_output.play_sound(sound_effects.whipping_hit.clone())); + sound_output.play_sound(sound_effects.whipping_hit_enemy.clone()); } + switch_to_hit_sound(resources, wants_to_whip); } + } else { + hit_tile(resources, wants_to_whip, dest); } } @@ -115,3 +108,36 @@ pub fn run(world: &mut World, resources: &mut Resources) { let _ = world.remove_one::(e); } } + +fn hit_tile(resources: &mut Resources, wants_to_whip: &mut WantsToWhip, location: Point) { + let tile = resources.map.get_tile_at(location); + match tile { + TileType::Block | TileType::Tree => { + let mut rng = RandomNumberGenerator::new(); + + if (rng.range(0, 7) as u32) < resources.stats.whip_power { + resources.map.set_tile_at(location, TileType::Floor); + if let (Some(sound_effects), Some(sound_output)) = + (&mut resources.sound_effects, &mut resources.sound_output) + { + sound_output.play_sound(sound_effects.whipping_hit_block.clone()); + } + switch_to_hit_sound(resources, wants_to_whip); + } + } + TileType::Forest => todo!(), + _ => (), + } +} + +fn switch_to_hit_sound(resources: &mut Resources, wants_to_whip: &mut WantsToWhip) { + if let Some(sound) = &mut wants_to_whip.sound { + sound.control::, _>().stop(); + } + + if let (Some(sound_effects), Some(sound_output)) = + (&mut resources.sound_effects, &mut resources.sound_output) + { + wants_to_whip.sound = Some(sound_output.play_sound(sound_effects.whipping_hit.clone())); + } +}