extern crate rand; extern crate std; use super::state; use super::constants; pub struct Gates { x: f64, y: f64, v_x: f64, v_y: f64, a_x: f64, a_y: f64, target_x: f64, target_y: f64, health: f64, } pub struct GatesBullet { real_x: f64, real_y: f64, d_x: f64, d_y: f64, } impl GatesBullet { pub fn new(x: i32, d_x: f64, y: i32, d_y: f64) -> GatesBullet { GatesBullet { real_x: x as f64, real_y: y as f64, d_x: d_x, d_y: d_y } } } impl state::Positioned for GatesBullet { fn x(&self) -> i32 { self.real_x.round() as i32 } fn y(&self) -> i32 { self.real_y.round() as i32 } fn radius() -> u32 { 10 } } impl state::Positioned for Gates { fn x(&self) -> i32 { self.x.round() as i32 } fn y(&self) -> i32 { self.y.round() as i32 } fn radius() -> u32 { 30 } } fn random_position() -> (f64, f64) { (rand::random::() * (constants::X_DIM - 200) as f64 + 100., rand::random::() * 100.) } impl state::StraightBullet for GatesBullet { fn real_x(&self) -> f64 { self.real_x } fn real_y(&self) -> f64 { self.real_y } fn set_real_x(&mut self, real_x: f64) { self.real_x = real_x; } fn set_real_y(&mut self, real_y: f64) { self.real_y = real_y; } fn d_x(&self) -> f64 { self.d_x } fn d_y(&self) -> f64 { self.d_y } } fn random_bullet>(enemy: &T) -> GatesBullet { let multiplier = 2. + 3.*(1. - enemy.health()); let theta: f64 = rand::random::()*2.*std::f64::consts::PI/3. + std::f64::consts::PI/6.; let dx = f64::cos(theta) * multiplier; let dy = f64::sin(theta) * multiplier; GatesBullet::new(enemy.x(), dx, enemy.y(), dy) } fn shorten(x: f64, y: f64, maxlen: f64) -> (f64, f64) { let len = f64::sqrt(x*x + y*y); if len > maxlen { (x / len / 2., y / len / 2.) } else { (x, y) } } impl Gates { fn choose_position(&mut self) { let (x, y) = random_position(); self.target_x = x; self.target_y = y; } } impl state::Enemy for Gates { fn new() -> Gates { Gates { x: 200., y: 40., v_x: 0., v_y: 0., a_x: 0., a_y: 0., target_x: 200., target_y: 40., health: 1. } } fn act(&mut self, count: u64) -> Vec { let max_accel = (1. - self.health) * 0.75 + 0.25; let max_velocity = (1. - self.health) * 5. + 5.; let dx = self.target_x - self.x; let dy = self.target_y - self.y; /* If we've arrived (close and stopped) */ if (dx*dx + dy*dy).sqrt() < max_velocity / max_accel && (self.v_x*self.v_x + self.v_y*self.v_y).sqrt() < 0.01 { /* Then pick a new target */ self.choose_position(); } let dx = self.target_x - self.x; let dy = self.target_y - self.y; /* If we're close */ if (dx*dx + dy*dy).sqrt() < max_velocity / max_accel { /* Then slow down */ let (x, y) = shorten(-self.v_x, -self.v_y, max_accel); self.a_x = x; self.a_y = y; } else { /* Otherwise, speed up */ let (x, y) = shorten(dx, dy, max_accel); self.a_x = x; self.a_y = y; } /* If we're going too fast */ if (self.v_x*self.v_x + self.v_y*self.v_y).sqrt() > max_velocity { /* Then don't go faster */ if self.v_x.signum() == self.a_x.signum() { self.a_x = 0.; } if self.v_y.signum() == self.a_y.signum() { self.a_y = 0.; } } self.v_x += self.a_x; self.x += self.v_x; self.v_y += self.a_y; self.y += self.v_y; if count % 20 == 0 { let num_bullets = if self.health >= 0.8 { 5 } else if self.health >= 0.7 { 6 } else if self.health >= 0.5 { 7 } else if self.health >= 0.4 { 8 } else if self.health >= 0.3 { 9 } else if self.health >= 0.2 { 10 } else { 11 }; let mut result = Vec::new(); for _ in 0..num_bullets { result.push(random_bullet(&*self)); } result } else { Vec::new() } } fn health(&self) -> f64 { self.health } fn set_health(&mut self, health: f64) { self.health = health; } fn injure(&mut self, damage: f64) { self.health -= damage; } } impl state::Drawable for GatesBullet { fn center() -> (u32, u32) { (20, 20) } } impl state::Drawable for Gates { fn center() -> (u32, u32) { (16, 23) } }