mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2026-01-10 12:14:41 -08:00
302 lines
8.5 KiB
Rust
302 lines
8.5 KiB
Rust
use rand::Rng;
|
|
|
|
use crate::{model::{Galaxy, Pos, COURSES, EndPosition, self, Enterprise, systems}, view, input::{self, prompt_value, param_or_prompt_value}};
|
|
|
|
pub fn perform_short_range_scan(galaxy: &Galaxy) {
|
|
if galaxy.enterprise.damaged.contains_key(systems::SHORT_RANGE_SCAN) {
|
|
view::scanners_out();
|
|
return;
|
|
}
|
|
|
|
view::short_range_scan(&galaxy)
|
|
}
|
|
|
|
pub fn get_amount_and_set_shields(galaxy: &mut Galaxy, provided: Vec<String>) {
|
|
|
|
if galaxy.enterprise.damaged.contains_key(systems::SHIELD_CONTROL) {
|
|
view::inoperable(&systems::name_for(systems::SHIELD_CONTROL));
|
|
return;
|
|
}
|
|
|
|
view::energy_available(galaxy.enterprise.total_energy);
|
|
let value = input::param_or_prompt_value(&provided, 0, "Number of units to shields", 0, i32::MAX);
|
|
if value.is_none() {
|
|
view::shields_unchanged();
|
|
return;
|
|
}
|
|
|
|
let value = value.unwrap() as u16;
|
|
if value > galaxy.enterprise.total_energy {
|
|
view::ridiculous();
|
|
view::shields_unchanged();
|
|
return;
|
|
}
|
|
|
|
galaxy.enterprise.shields = value;
|
|
view::shields_set(value);
|
|
}
|
|
|
|
pub fn gather_dir_and_speed_then_move(galaxy: &mut Galaxy, provided: Vec<String>) {
|
|
|
|
let course = input::param_or_prompt_value(&provided, 0, "Course (1-9)?", 1, 9);
|
|
if course.is_none() {
|
|
view::bad_nav();
|
|
return;
|
|
}
|
|
|
|
let course = course.unwrap();
|
|
|
|
let mut max_warp = 8.0;
|
|
if galaxy.enterprise.damaged.contains_key(systems::WARP_ENGINES) {
|
|
max_warp = 0.2;
|
|
}
|
|
|
|
let speed = input::param_or_prompt_value(&provided, 1, format!("Warp Factor (0-{})?", max_warp).as_str(), 0.0, 8.0);
|
|
if speed.is_none() {
|
|
view::bad_nav();
|
|
return;
|
|
}
|
|
|
|
let speed = speed.unwrap();
|
|
|
|
if speed > max_warp {
|
|
view::damaged_engines(max_warp, speed);
|
|
return;
|
|
}
|
|
|
|
move_klingons_and_fire(galaxy);
|
|
if galaxy.enterprise.destroyed {
|
|
return;
|
|
}
|
|
|
|
repair_systems(&mut galaxy.enterprise, speed);
|
|
repair_or_damage_random_system(&mut galaxy.enterprise);
|
|
|
|
move_enterprise(course, speed, galaxy);
|
|
}
|
|
|
|
fn repair_systems(enterprise: &mut Enterprise, amount: f32) {
|
|
|
|
let keys: Vec<String> = enterprise.damaged.keys().map(|k| k.to_string()).collect();
|
|
let mut repaired = Vec::new();
|
|
for key in keys {
|
|
let fully_fixed = enterprise.repair_system(&key, amount);
|
|
if fully_fixed {
|
|
repaired.push(systems::name_for(&key));
|
|
}
|
|
}
|
|
|
|
if repaired.len() <= 0 {
|
|
return;
|
|
}
|
|
|
|
view::damage_control_report();
|
|
for name in repaired {
|
|
view::system_repair_completed(name);
|
|
}
|
|
}
|
|
|
|
fn repair_or_damage_random_system(enterprise: &mut Enterprise) {
|
|
let mut rng = rand::thread_rng();
|
|
|
|
if rng.gen::<f32>() > 0.2 {
|
|
return;
|
|
}
|
|
|
|
if rng.gen::<f32>() >= 0.6 {
|
|
if enterprise.damaged.len() == 0 {
|
|
return;
|
|
}
|
|
|
|
let damaged: Vec<String> = enterprise.damaged.keys().map(|k| k.to_string()).collect();
|
|
let system = damaged[rng.gen_range(0..damaged.len())].to_string();
|
|
let system_name = &systems::name_for(&system);
|
|
|
|
enterprise.repair_system(&system, rng.gen::<f32>() * 3.0 + 1.0);
|
|
view::random_repair_report_for(system_name, false);
|
|
return;
|
|
}
|
|
|
|
let system = systems::KEYS[rng.gen_range(0..systems::KEYS.len())].to_string();
|
|
let system_name = &systems::name_for(&system);
|
|
|
|
enterprise.damage_system(&system, rng.gen::<f32>() * 5.0 + 1.0);
|
|
view::random_repair_report_for(system_name, true);
|
|
}
|
|
|
|
fn move_enterprise(course: u8, warp_speed: f32, galaxy: &mut Galaxy) {
|
|
|
|
let ship = &mut galaxy.enterprise;
|
|
|
|
// todo account for being blocked
|
|
|
|
let end = find_end_quadrant_sector(ship.quadrant, ship.sector, course, warp_speed);
|
|
|
|
if end.energy_cost > ship.total_energy {
|
|
view::insuffient_warp_energy(warp_speed);
|
|
return
|
|
}
|
|
|
|
if end.hit_edge {
|
|
view::hit_edge(&end);
|
|
}
|
|
|
|
if ship.quadrant != end.quadrant {
|
|
view::enter_quadrant(&end.quadrant);
|
|
galaxy.scanned.insert(end.quadrant);
|
|
|
|
if galaxy.quadrants[end.quadrant.as_index()].klingons.len() > 0 {
|
|
view::condition_red();
|
|
if ship.shields <= 200 {
|
|
view::danger_shields();
|
|
}
|
|
}
|
|
}
|
|
|
|
ship.quadrant = end.quadrant;
|
|
ship.sector = end.sector;
|
|
|
|
ship.total_energy = (ship.total_energy - end.energy_cost).max(0);
|
|
if ship.shields > ship.total_energy {
|
|
view::divert_energy_from_shields();
|
|
ship.shields = ship.total_energy;
|
|
}
|
|
|
|
view::short_range_scan(&galaxy)
|
|
}
|
|
|
|
fn find_end_quadrant_sector(start_quadrant: Pos, start_sector: Pos, course: u8, warp_speed: f32) -> EndPosition {
|
|
let (dx, dy): (i8, i8) = COURSES[(course - 1) as usize];
|
|
|
|
let mut distance = (warp_speed * 8.0) as i8;
|
|
if distance == 0 {
|
|
distance = 1;
|
|
}
|
|
|
|
let galaxy_pos = start_quadrant * 8u8 + start_sector;
|
|
|
|
let mut nx = (galaxy_pos.0 as i8) + dx * distance;
|
|
let mut ny = (galaxy_pos.1 as i8) + dy * distance;
|
|
|
|
let hit_edge = nx < 0 || ny < 0 || nx >= 64 || ny >= 64;
|
|
nx = nx.min(63).max(0);
|
|
ny = ny.min(63).max(0);
|
|
|
|
let quadrant = Pos((nx / 8) as u8, (ny / 8) as u8);
|
|
let sector = Pos((nx % 8) as u8, (ny % 8) as u8);
|
|
let energy_cost = distance as u16 + 10;
|
|
|
|
EndPosition { quadrant, sector, hit_edge, energy_cost }
|
|
}
|
|
|
|
fn move_klingons_and_fire(galaxy: &mut Galaxy) {
|
|
let quadrant = &mut galaxy.quadrants[galaxy.enterprise.quadrant.as_index()];
|
|
for k in 0..quadrant.klingons.len() {
|
|
let new_sector = quadrant.find_empty_sector();
|
|
quadrant.klingons[k].sector = new_sector;
|
|
}
|
|
|
|
// todo: check if enterprise is protected by a starbase
|
|
|
|
for k in 0..quadrant.klingons.len() {
|
|
quadrant.klingons[k].fire_on(&mut galaxy.enterprise);
|
|
}
|
|
}
|
|
|
|
pub fn display_damage_control(enterprise: &Enterprise) {
|
|
if enterprise.damaged.contains_key(systems::DAMAGE_CONTROL) {
|
|
view::inoperable(&systems::name_for(systems::DAMAGE_CONTROL));
|
|
return;
|
|
}
|
|
|
|
println!("Device State of Repair");
|
|
for key in systems::KEYS {
|
|
let damage = enterprise.damaged.get(key).unwrap_or(&0.0);
|
|
println!("{:<25}{}", systems::name_for(key), damage)
|
|
}
|
|
println!();
|
|
}
|
|
|
|
pub fn perform_long_range_scan(galaxy: &mut Galaxy) {
|
|
if galaxy.enterprise.damaged.contains_key(systems::LONG_RANGE_SCAN) {
|
|
view::inoperable(&systems::name_for(systems::LONG_RANGE_SCAN));
|
|
return;
|
|
}
|
|
|
|
let seen = view::long_range_scan(galaxy);
|
|
for pos in seen {
|
|
galaxy.scanned.insert(pos);
|
|
}
|
|
}
|
|
|
|
pub fn access_computer(galaxy: &Galaxy, provided: Vec<String>) {
|
|
if galaxy.enterprise.damaged.contains_key(systems::COMPUTER) {
|
|
view::inoperable(&systems::name_for(systems::COMPUTER));
|
|
return;
|
|
}
|
|
|
|
let operation : i32;
|
|
loop {
|
|
let entered = input::param_or_prompt_value(&provided, 0, "Computer active and waiting command?", 0, 5);
|
|
if entered.is_none() {
|
|
view::computer_options();
|
|
} else {
|
|
operation = entered.unwrap();
|
|
break;
|
|
}
|
|
}
|
|
|
|
match operation {
|
|
0 => view::galaxy_scanned_map(galaxy),
|
|
5 => view::galaxy_region_map(),
|
|
_ => todo!() // todo implement others
|
|
}
|
|
}
|
|
|
|
pub fn get_power_and_fire_phasers(galaxy: &mut Galaxy, provided: Vec<String>) {
|
|
if galaxy.enterprise.damaged.contains_key(systems::PHASERS) {
|
|
view::inoperable(&systems::name_for(systems::PHASERS));
|
|
return;
|
|
}
|
|
|
|
let quadrant = &mut galaxy.quadrants[galaxy.enterprise.quadrant.as_index()];
|
|
if quadrant.klingons.len() == 0 {
|
|
view::no_local_enemies();
|
|
return;
|
|
}
|
|
|
|
let computer_damaged = galaxy.enterprise.damaged.contains_key(systems::COMPUTER);
|
|
if computer_damaged {
|
|
view::computer_accuracy_issue();
|
|
}
|
|
|
|
let available_energy = galaxy.enterprise.total_energy - galaxy.enterprise.shields;
|
|
view::phasers_locked(available_energy);
|
|
let mut power: f32;
|
|
loop {
|
|
let setting = param_or_prompt_value(&provided, 0, "Number of units to fire", 0, available_energy);
|
|
if setting.is_some() {
|
|
power = setting.unwrap() as f32;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if power == 0.0 {
|
|
return;
|
|
}
|
|
|
|
galaxy.enterprise.total_energy -= power as u16;
|
|
|
|
let mut rng = rand::thread_rng();
|
|
if computer_damaged {
|
|
power *= rng.gen::<f32>();
|
|
}
|
|
|
|
let per_enemy = power / quadrant.klingons.len() as f32;
|
|
|
|
// fire on each klingon
|
|
|
|
for klingon in &mut quadrant.klingons {
|
|
klingon.fire_on(&mut galaxy.enterprise)
|
|
}
|
|
} |