From 781d0566f8d351e38ba273854ba0fb1c624df180 Mon Sep 17 00:00:00 2001 From: Christopher Date: Sat, 4 Mar 2023 15:24:05 +1300 Subject: [PATCH] implemented torpedoes! --- 84_Super_Star_Trek/rust/src/commands.rs | 127 ++++++++++++++++++------ 84_Super_Star_Trek/rust/src/main.rs | 2 +- 84_Super_Star_Trek/rust/src/model.rs | 29 ++++-- 84_Super_Star_Trek/rust/src/view.rs | 39 ++++++-- 84_Super_Star_Trek/rust/tasks.md | 4 +- 5 files changed, 148 insertions(+), 53 deletions(-) diff --git a/84_Super_Star_Trek/rust/src/commands.rs b/84_Super_Star_Trek/rust/src/commands.rs index b3ca531e..cc79dffb 100644 --- a/84_Super_Star_Trek/rust/src/commands.rs +++ b/84_Super_Star_Trek/rust/src/commands.rs @@ -132,22 +132,24 @@ fn move_enterprise(course: f32, warp_speed: f32, galaxy: &mut Galaxy) { // todo account for being blocked - let path = find_path(ship.quadrant, ship.sector, course, warp_speed); + let (path, hit_edge) = find_nav_path(ship.quadrant, ship.sector, course, warp_speed); + let energy_cost = path.len() as u16 + 10; - if path.energy_cost > ship.total_energy { + if energy_cost > ship.total_energy { view::insuffient_warp_energy(warp_speed); return } - if path.hit_edge { - view::hit_edge(&path); + let (end_quadrant, end_sector) = path[path.len() - 1].to_local_quadrant_sector(); + if hit_edge { + view::hit_edge(end_quadrant, end_sector); } - if ship.quadrant != path.quadrant { - view::enter_quadrant(&path.quadrant); - galaxy.scanned.insert(path.quadrant); + if ship.quadrant != end_quadrant { + view::enter_quadrant(end_quadrant); + galaxy.scanned.insert(end_quadrant); - if galaxy.quadrants[path.quadrant.as_index()].klingons.len() > 0 { + if galaxy.quadrants[end_quadrant.as_index()].klingons.len() > 0 { view::condition_red(); if ship.shields <= 200 { view::danger_shields(); @@ -155,16 +157,16 @@ fn move_enterprise(course: f32, warp_speed: f32, galaxy: &mut Galaxy) { } } - ship.quadrant = path.quadrant; - ship.sector = path.sector; + ship.quadrant = end_quadrant; + ship.sector = end_sector; - let quadrant = &galaxy.quadrants[path.quadrant.as_index()]; + let quadrant = &galaxy.quadrants[end_quadrant.as_index()]; if quadrant.docked_at_starbase(ship.sector) { ship.shields = 0; ship.photon_torpedoes = MAX_PHOTON_TORPEDOES; ship.total_energy = MAX_ENERGY; } else { - ship.total_energy = (ship.total_energy - path.energy_cost).max(0); + ship.total_energy = ship.total_energy - energy_cost; if ship.shields > ship.total_energy { view::divert_energy_from_shields(); ship.shields = ship.total_energy; @@ -174,23 +176,16 @@ fn move_enterprise(course: f32, warp_speed: f32, galaxy: &mut Galaxy) { view::short_range_scan(&galaxy) } -fn find_path(start_quadrant: Pos, start_sector: Pos, course: f32, warp_speed: f32) -> EndPosition { +fn find_nav_path(start_quadrant: Pos, start_sector: Pos, course: f32, warp_speed: f32) -> (Vec, bool) { - // this course delta stuff is a translation (of a translation, of a translation...) of the original basic calcs - let dir = (course - 1.0) % 8.0; - let (dx1, dy1) = COURSES[dir as usize]; - let (dx2, dy2) = COURSES[(dir + 1.0) as usize]; - let frac = dir - (dir as i32) as f32; - - let dx = dx1 + (dx2 - dx1) * frac; - let dy = dy1 + (dy2 - dy1) * frac; + let (dx, dy) = calculate_delta(course); let mut distance = (warp_speed * 8.0) as i8; if distance == 0 { distance = 1; } - let mut last_sector = start_quadrant * 8 + start_sector; + let mut last_sector = start_sector.as_galactic_sector(start_quadrant); let mut path = Vec::new(); let mut hit_edge; @@ -210,11 +205,20 @@ fn find_path(start_quadrant: Pos, start_sector: Pos, course: f32, warp_speed: f3 } } - let quadrant = Pos((last_sector.0 / 8) as u8, (last_sector.1 / 8) as u8); - let sector = Pos((last_sector.0 % 8) as u8, (last_sector.1 % 8) as u8); - let energy_cost = path.len() as u16 + 10; + (path, hit_edge) +} - EndPosition { quadrant, sector, hit_edge, energy_cost } +fn calculate_delta(course: f32) -> (f32, f32) { + // this course delta stuff is a translation (of a translation, of a translation...) of the original basic calcs + let dir = (course - 1.0) % 8.0; + let (dx1, dy1) = COURSES[dir as usize]; + let (dx2, dy2) = COURSES[(dir + 1.0) as usize]; + let frac = dir - (dir as i32) as f32; + + let dx = dx1 + (dx2 - dx1) * frac; + let dy = dy1 + (dy2 - dy1) * frac; + + (dx, dy) } fn klingons_move(galaxy: &mut Galaxy) { @@ -371,12 +375,15 @@ pub fn get_power_and_fire_phasers(galaxy: &mut Galaxy, provided: Vec) { } pub fn gather_dir_and_launch_torpedo(galaxy: &mut Galaxy, provided: Vec) { - if galaxy.enterprise.damaged.contains_key(systems::TORPEDOES) { + let star_bases = galaxy.remaining_starbases(); + let ship = &mut galaxy.enterprise; + + if ship.damaged.contains_key(systems::TORPEDOES) { view::inoperable(&systems::name_for(systems::TORPEDOES)); return; } - if galaxy.enterprise.photon_torpedoes == 0 { + if ship.photon_torpedoes == 0 { view::no_torpedoes_remaining(); return; } @@ -387,12 +394,68 @@ pub fn gather_dir_and_launch_torpedo(galaxy: &mut Galaxy, provided: Vec) return; } - galaxy.enterprise.photon_torpedoes -= 1; + ship.photon_torpedoes -= 1; view::torpedo_track(); - // calculate direction - // step through sectors - // test for hits or final miss + let path = find_torpedo_path(ship.sector, course.unwrap()); + let quadrant = &mut galaxy.quadrants[ship.quadrant.as_index()]; + let mut hit = false; + for p in path { + view::torpedo_path(p); + match quadrant.sector_status(p) { + SectorStatus::Empty => continue, + SectorStatus::Star => { + hit = true; + view::star_absorbed_torpedo(p); + break; + }, + SectorStatus::Klingon => { + hit = true; + quadrant.get_klingon(p).unwrap().energy = 0.0; + quadrant.klingons.retain(|k| k.energy > 0.0); + view::klingon_destroyed(); + break; + }, + SectorStatus::StarBase => { + hit = true; + quadrant.star_base = None; + let remaining = star_bases - 1; + view::destroyed_starbase(remaining > 0); + if remaining == 0 { + ship.destroyed = true; + } + break; + } + } + } + + if ship.destroyed { // if you wiped out the last starbase, trigger game over + return; + } + + if !hit { + view::torpedo_missed(); + } klingons_fire(galaxy); +} + +fn find_torpedo_path(start_sector: Pos, course: f32) -> Vec { + + let (dx, dy) = calculate_delta(course); + + let mut last_sector = start_sector; + let mut path = Vec::new(); + + loop { + let nx = (last_sector.0 as f32 + dx) as i8; + let ny = (last_sector.1 as f32 + dy) as i8; + if nx < 0 || ny < 0 || nx >= 8 || ny >= 8 { + break; + } + last_sector = Pos(nx as u8, ny as u8); + path.push(last_sector); + } + + path } \ No newline at end of file diff --git a/84_Super_Star_Trek/rust/src/main.rs b/84_Super_Star_Trek/rust/src/main.rs index 58be1f18..78e59531 100644 --- a/84_Super_Star_Trek/rust/src/main.rs +++ b/84_Super_Star_Trek/rust/src/main.rs @@ -20,7 +20,7 @@ fn main() { view::intro(&galaxy); let _ = input::prompt(view::prompts::WHEN_READY); - view::starting_quadrant(&galaxy.enterprise.quadrant); + view::starting_quadrant(galaxy.enterprise.quadrant); view::short_range_scan(&galaxy); loop { diff --git a/84_Super_Star_Trek/rust/src/model.rs b/84_Super_Star_Trek/rust/src/model.rs index a8919b5e..9b2f1118 100644 --- a/84_Super_Star_Trek/rust/src/model.rs +++ b/84_Super_Star_Trek/rust/src/model.rs @@ -60,7 +60,8 @@ impl Enterprise { if self.shields <= hit_strength { view::enterprise_destroyed(); - self.destroyed = true + self.destroyed = true; + return; } self.shields -= hit_strength; @@ -141,13 +142,6 @@ pub mod systems { } } -pub struct EndPosition { - pub quadrant: Pos, - pub sector: Pos, - pub hit_edge: bool, - pub energy_cost: u16, -} - #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq)] pub struct Pos(pub u8, pub u8); @@ -159,6 +153,14 @@ impl Pos { pub fn abs_diff(&self, other: Pos) -> u8 { self.0.abs_diff(other.0) + self.1.abs_diff(other.1) } + + pub fn as_galactic_sector(&self, containing_quadrant: Pos) -> Self { + Pos(containing_quadrant.0 * 8 + self.0, containing_quadrant.1 * 8 + self.1) + } + + pub fn to_local_quadrant_sector(&self) -> (Self, Self) { + (Pos(self.0 / 8, self.1 / 8), Pos(self.0 % 8, self.1 % 8)) + } } impl Mul for Pos { @@ -286,7 +288,7 @@ impl Quadrant { SectorStatus::Star } else if self.is_starbase(sector) { SectorStatus::StarBase - } else if self.has_klingon(§or) { + } else if self.has_klingon(sector) { SectorStatus::Klingon } else { SectorStatus::Empty @@ -300,9 +302,14 @@ impl Quadrant { } } - fn has_klingon(&self, sector: &Pos) -> bool { + fn has_klingon(&self, sector: Pos) -> bool { let klingons = &self.klingons; - klingons.into_iter().find(|k| &k.sector == sector).is_some() + klingons.into_iter().find(|k| k.sector == sector).is_some() + } + + pub fn get_klingon(&mut self, sector: Pos) -> Option<&mut Klingon> { + let klingons = &mut self.klingons; + klingons.into_iter().find(|k| k.sector == sector) } pub fn find_empty_sector(&self) -> Pos { diff --git a/84_Super_Star_Trek/rust/src/view.rs b/84_Super_Star_Trek/rust/src/view.rs index 267fc91b..f7bac4db 100644 --- a/84_Super_Star_Trek/rust/src/view.rs +++ b/84_Super_Star_Trek/rust/src/view.rs @@ -1,4 +1,4 @@ -use crate::model::{Galaxy, Pos, EndPosition, SectorStatus, Enterprise, systems}; +use crate::model::{Galaxy, Pos, SectorStatus, Enterprise, systems}; pub mod prompts { pub const COURSE: &str = "Course (1-9)?"; @@ -187,19 +187,19 @@ const REGION_NAMES: [&str; 16] = [ const SUB_REGION_NAMES: [&str; 4] = ["I", "II", "III", "IV"]; -fn quadrant_name(quadrant: &Pos) -> String { +fn quadrant_name(quadrant: Pos) -> String { format!("{} {}", REGION_NAMES[((quadrant.1 << 1) + (quadrant.0 >> 2)) as usize], SUB_REGION_NAMES[(quadrant.1 % 4) as usize]) } -pub fn starting_quadrant(quadrant: &Pos) { +pub fn starting_quadrant(quadrant: Pos) { println!( "\nYour mission begins with your starship located in the galactic quadrant, '{}'.\n", quadrant_name(quadrant)) } -pub fn enter_quadrant(quadrant: &Pos) { +pub fn enter_quadrant(quadrant: Pos) { println!("\nNow entering {} quadrant . . .\n", quadrant_name(quadrant)) } @@ -285,13 +285,13 @@ pub fn enterprise_hit(hit_strength: &u16, from_sector: &Pos) { println!("{hit_strength} unit hit on Enterprise from sector {from_sector}"); } -pub fn hit_edge(end: &EndPosition) { +pub fn hit_edge(quadrant: Pos, sector: Pos) { println!( "Lt. Uhura report message from Starfleet Command: 'Permission to attempt crossing of galactic perimeter is hereby *Denied*. Shut down your engines.' Chief Engineer Scott reports, 'Warp engines shut down - at sector {} of quadrant {}.'", end.sector, end.quadrant); + at sector {} of quadrant {}.'", sector, quadrant); } pub fn condition_red() { @@ -494,7 +494,7 @@ pub fn klingon_remaining_energy(energy: f32) { } pub fn klingon_destroyed() { - println!(" Target Destroyed!") // not standard for game but feedback is good. Sorry Mr. Roddenberry + println!("*** Klingon destroyed ***") } pub fn congratulations(efficiency: f32) { @@ -519,4 +519,29 @@ pub fn no_torpedoes_remaining() { pub fn torpedo_track() { println!("Torpedo track:") +} + +pub fn torpedo_path(sector: Pos) { + println!("{:<16}{}", "", sector) +} + +pub fn torpedo_missed() { + println!("Torpedo missed!") +} + +pub fn star_absorbed_torpedo(sector: Pos) { + println!("Star at {sector} absorbed torpedo energy.") +} + +pub fn destroyed_starbase(not_the_last_starbase: bool) { + println!("*** Starbase destroyed ***"); + if not_the_last_starbase { + println!(" +Starfleet Command reviewing your record to consider +court martial!") + } else { + println!(" +That does it, Captain!! You are hereby relieved of command +and sentenced to 99 stardates at hard labor on Cygnus 12!!") + } } \ No newline at end of file diff --git a/84_Super_Star_Trek/rust/tasks.md b/84_Super_Star_Trek/rust/tasks.md index 7a663304..2ddea3df 100644 --- a/84_Super_Star_Trek/rust/tasks.md +++ b/84_Super_Star_Trek/rust/tasks.md @@ -25,9 +25,9 @@ Started after movement and display of stats was finished (no energy management o - [x] proximity detection for docking - [x] repair on damage control - [x] protection from shots -- [ ] weapons +- [x] weapons - [x] phasers - - [ ] torpedoes + - [x] torpedoes - [ ] computer - [x] 0 - output of all short and long range scans (requires tracking if a system has been scanned) - [ ] 1 - klingons, starbases, stardate and damage control