mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2026-02-04 19:12:07 -08:00
implemented torpedoes!
This commit is contained in:
@@ -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<Pos>, 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<String>) {
|
||||
}
|
||||
|
||||
pub fn gather_dir_and_launch_torpedo(galaxy: &mut Galaxy, provided: Vec<String>) {
|
||||
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<String>)
|
||||
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<Pos> {
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<u8> 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 {
|
||||
|
||||
@@ -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!!")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user