extern crate sdl2; extern crate std; extern crate rand; use super::player; use super::constants; pub struct EBullet { x: i32, y: i32, radius: u32, real_x: f64, real_y: f64, d_x: f64, d_y: f64, } pub struct Enemy { pub x: f64, pub y: f64, v_x: f64, v_y: f64, a_x: f64, a_y: f64, target_x: f64, target_y: f64, radius: u32, pub health: i64, } pub const STARTING_HEALTH: i64 = 100; impl EBullet { pub fn new(x: i32, d_x: f64, y: i32, d_y: f64) -> EBullet { EBullet { x: x, real_x: x as f64, y: y, real_y: y as f64, radius: 10, d_x: d_x, d_y: d_y } } pub fn act(&mut self) { self.real_x += self.d_x; self.real_y += self.d_y; self.x = self.real_x.round() as i32; self.y = self.real_y.round() as i32; } } pub struct PBullet { b: EBullet, } impl PBullet { pub fn new(x: i32, d_x: f64, y: i32, d_y: f64) -> PBullet { PBullet { b: EBullet::new(x, d_x, y, d_y) } } pub fn act(&mut self) { self.b.act() } pub fn delete(&mut self) { self.b.x = -10; } } fn random_bullet(enemy: &Enemy) -> EBullet { let multiplier = 2. + (STARTING_HEALTH as i64 - enemy.health) as f64 / STARTING_HEALTH as f64 * 2.; 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; EBullet::new(enemy.x.round() as i32, dx, enemy.y.round() as i32, dy) } fn random_position() -> (f64, f64) { (rand::random::() * (constants::X_DIM - 200) as f64 + 100., rand::random::() * 100.) } 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 Enemy { pub fn new() -> Enemy { let (x, y) = random_position(); Enemy { x: 200., y: 40., v_x: 0., v_y: 0., a_x: 0., a_y: 0., radius: 30, health: STARTING_HEALTH, target_x: x, target_y: y } } pub fn act(&mut self, count: u64) -> Vec { let max_accel = 0.25; let max_velocity = 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 */ let (x, y) = random_position(); self.target_x = x; self.target_y = y; } 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 >= 80 { num_bullets = 4; } else if self.health >= 50 { num_bullets = 5; } else if self.health >= 30 { num_bullets = 6; } else if self.health >= 20 { num_bullets = 7; } else { num_bullets = 8; } let mut result = Vec::new(); for _ in 0..num_bullets { result.push(random_bullet(&*self)); } result } else { Vec::new() } } pub fn health(&self) -> i64 { self.health } } pub struct State { pub player: player::Player, pub enemies: Vec, pub bullets: Vec, pub player_bullets: Vec, pub count: u64, } impl State { pub fn new() -> State { State { player: player::Player::new(), enemies: Vec::new(), bullets: Vec::new(), count: 0, player_bullets: Vec::new() } } } fn dist(x1: i32, y1: i32, x2: i32, y2: i32) -> f64 { let xdist = x1 - x2; let ydist = y1 - y2; f64::sqrt((xdist*xdist + ydist*ydist) as f64) } pub trait Positioned { fn x(&self) -> i32; fn y(&self) -> i32; fn radius(&self) -> u32; fn contains(&self, x: i32, y: i32) -> bool { let d = dist(self.x(), self.y(), x, y); d <= self.radius() as f64 } fn overlaps(&self, other: &T) -> bool { let sx = self.x(); let ox = other.x(); let sy = self.y(); let oy = other.y(); let threshold = self.radius() + other.radius(); if (sx - ox).abs() as u32 >= threshold || (sy - oy).abs() as u32 >= threshold { return false; } let dist = dist(sx, sy, ox, oy); dist < threshold as f64 } fn outside(&self) -> bool { let x = self.x(); let y = self.y(); x < 0 || x > constants::X_DIM as i32 || y < 0 || y > constants::Y_DIM as i32 } } impl Positioned for EBullet { fn x(&self) -> i32 { self.x } fn y(&self) -> i32 { self.y } fn radius(&self) -> u32 { self.radius } } impl Positioned for PBullet { fn x(&self) -> i32 { self.b.x() } fn y(&self) -> i32 { self.b.y() } fn radius(&self) -> u32 { self.b.radius() } } impl Positioned for Enemy { fn x(&self) -> i32 { self.x.round() as i32 } fn y(&self) -> i32 { self.y.round() as i32 } fn radius(&self) -> u32 { self.radius } } pub trait Drawable : Positioned { fn center(&self) -> (u32, u32); fn draw(&self, canvas: &mut sdl2::render::WindowCanvas, texture: &sdl2::render::Texture) { let tw = texture.query().width; let th = texture.query().height; let (cx, cy) = self.center(); let x0 = self.x() - cx as i32; let y0 = self.y() - cy as i32; canvas.copy(&texture, None, Some(sdl2::rect::Rect::new(x0, y0, tw, th))).unwrap(); } } impl Drawable for Enemy { fn center(&self) -> (u32, u32) { (0, 0) } } impl Drawable for EBullet { fn center(&self) -> (u32, u32) { (20, 20) } } impl Drawable for PBullet { fn center(&self) -> (u32, u32) { (16, 22) } }