summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gates.rs185
-rw-r--r--src/main.rs24
-rw-r--r--src/player.rs4
-rw-r--r--src/state.rs246
4 files changed, 262 insertions, 197 deletions
diff --git a/src/gates.rs b/src/gates.rs
new file mode 100644
index 0000000..c79297a
--- /dev/null
+++ b/src/gates.rs
@@ -0,0 +1,185 @@
+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 {
+ 15
+ }
+}
+
+fn random_position() -> (f64, f64) {
+ (rand::random::<f64>() * (constants::X_DIM - 200) as f64 + 100., rand::random::<f64>() * 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<T: state::Enemy<GatesBullet>>(enemy: &T) -> GatesBullet {
+ let multiplier = 2. + 2.*(1. - enemy.health());
+ let theta: f64 = rand::random::<f64>()*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 state::Enemy<GatesBullet> 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<GatesBullet> {
+ 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 >= 0.8 {
+ num_bullets = 4;
+ } else if self.health >= 0.5 {
+ num_bullets = 5;
+ } else if self.health >= 0.3 {
+ num_bullets = 6;
+ } else if self.health >= 0.2 {
+ 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()
+ }
+ }
+ fn health(&self) -> f64 {
+ self.health
+ }
+ fn set_health(&mut self, health: f64) {
+ self.health = health;
+ }
+}
+
+impl state::Drawable for GatesBullet {
+ fn center() -> (u32, u32) {
+ (20, 20)
+ }
+}
+
+impl state::Drawable for Gates {
+ fn center() -> (u32, u32) {
+ (16, 23)
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index ca1bdd8..6e1322a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,9 +3,11 @@ extern crate sdl2;
mod state;
mod player;
mod constants;
+mod gates;
use state::Positioned;
use state::Drawable;
+use state::Bullet;
enum Result {
Win,
@@ -20,15 +22,7 @@ struct Textures<'a> {
ebullet: sdl2::render::Texture<'a>,
}
-fn spawn(count: u64) -> Vec<state::Enemy> {
- if count == 0 {
- vec![state::Enemy::new()]
- } else {
- Vec::new()
- }
-}
-
-fn frame(canvas: &mut sdl2::render::WindowCanvas, state: &mut state::State, events: &mut sdl2::event::EventPollIterator, textures: &Textures) -> Result {
+fn frame<T: state::Enemy<U>, U: state::Bullet>(canvas: &mut sdl2::render::WindowCanvas, state: &mut state::State<T, U>, events: &mut sdl2::event::EventPollIterator, textures: &Textures) -> Result {
let mut new_bullets = Vec::new();
let count = state.count;
for enemy in state.enemies.iter_mut() {
@@ -51,16 +45,16 @@ fn frame(canvas: &mut sdl2::render::WindowCanvas, state: &mut state::State, even
for bullet in state.player_bullets.iter_mut() {
if enemy.overlaps(bullet) {
bullet.delete();
- enemy.health -= 1;
+ let health = enemy.health();
+ enemy.set_health(health - state::PBullet::damage());
}
}
}
- state.enemies.retain(|enemy| state::Enemy::health(enemy) > 0);
- state.enemies.append(&mut spawn(count));
+ state.enemies.retain(|enemy| state::Enemy::health(enemy) > 0.);
if state.enemies.len() == 0 {
return Result::Win;
}
- state.bullets.retain(|bullet| !state::EBullet::outside(bullet));
+ state.bullets.retain(|bullet| !U::outside(bullet));
state.bullets.append(&mut new_bullets);
state.player_bullets.retain(|bullet| !state::PBullet::outside(bullet));
for bullet in state.player_bullets.iter() {
@@ -74,7 +68,7 @@ fn frame(canvas: &mut sdl2::render::WindowCanvas, state: &mut state::State, even
bullet.draw(canvas, &textures.ebullet);
}
canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 0));
- canvas.draw_rect(sdl2::rect::Rect::new(0, 0, (state.enemies[0].health as f64 / state::STARTING_HEALTH as f64 * constants::X_DIM as f64).round() as u32, 20)).unwrap();
+ canvas.draw_rect(sdl2::rect::Rect::new(0, 0, (state.enemies[0].health() * constants::X_DIM as f64).round() as u32, 20)).unwrap();
state.count += 1;
Result::Nothing
}
@@ -93,7 +87,7 @@ fn main() {
let start = std::time::Instant::now();
let frame_duration = std::time::Duration::new(0, 1_000_000_000u32 / 60);
let mut frame_count = 0;
- let mut state = state::State::new();
+ let mut state: state::State<gates::Gates, gates::GatesBullet> = state::State::new();
let mut won = false;
let player_surface: sdl2::surface::Surface = sdl2::image::LoadSurface::from_file("assets/stallman.png").unwrap();
let enemy_surface: sdl2::surface::Surface = sdl2::image::LoadSurface::from_file("assets/gates.png").unwrap();
diff --git a/src/player.rs b/src/player.rs
index 996e554..fa2abfa 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -112,13 +112,13 @@ impl state::Positioned for Player {
fn y(&self) -> i32 {
self.y
}
- fn radius(&self) -> u32 {
+ fn radius() -> u32 {
13
}
}
impl state::Drawable for Player {
- fn center(&self) -> (u32, u32) {
+ fn center() -> (u32, u32) {
(30, 19)
}
}
diff --git a/src/state.rs b/src/state.rs
index 5f622b5..d80c550 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -5,167 +5,89 @@ extern crate rand;
use super::player;
use super::constants;
-pub struct EBullet {
- x: i32,
- y: i32,
- radius: u32,
+pub trait StraightBullet: Positioned + Drawable {
+ fn real_x(&self) -> f64;
+ fn real_y(&self) -> f64;
+ fn set_real_x(&mut self, real_x: f64);
+ fn set_real_y(&mut self, real_y: f64);
+ fn d_x(&self) -> f64;
+ fn d_y(&self) -> f64;
+}
+
+pub struct PBullet {
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 }
+impl StraightBullet for PBullet {
+ fn real_x(&self) -> f64 {
+ self.real_x
}
-
- 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;
+ fn real_y(&self) -> f64 {
+ self.real_y
}
-}
-
-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) }
+ fn set_real_x(&mut self, real_x: f64) {
+ self.real_x = real_x;
}
- pub fn act(&mut self) {
- self.b.act()
+ fn set_real_y(&mut self, real_y: f64) {
+ self.real_y = real_y;
}
- pub fn delete(&mut self) {
- self.b.x = -10;
+ fn d_x(&self) -> f64 {
+ self.d_x
+ }
+ fn d_y(&self) -> f64 {
+ self.d_y
}
}
-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::<f64>()*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::<f64>() * (constants::X_DIM - 200) as f64 + 100., rand::random::<f64>() * 100.)
+pub trait Bullet: Positioned + Drawable {
+ fn act(&mut self);
+ fn delete(&mut self);
}
-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<T: StraightBullet> Bullet for T {
+ fn act(&mut self) {
+ let real_x = self.real_x();
+ let d_x = self.d_x();
+ self.set_real_x(real_x + d_x);
+ let real_y = self.real_y();
+ let d_y = self.d_y();
+ self.set_real_y(real_y + d_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 }
+ fn delete(&mut self) {
+ self.set_real_x(-10.);
}
- pub fn act(&mut self, count: u64) -> Vec<EBullet> {
- 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()
- }
+impl PBullet {
+ pub fn new(x: i32, d_x: f64, y: i32, d_y: f64) -> PBullet {
+ PBullet { real_x: x as f64, real_y: y as f64, d_x: d_x, d_y: d_y }
}
- pub fn health(&self) -> i64 {
- self.health
+ pub fn damage() -> f64 {
+ 0.01
}
}
-pub struct State {
+pub trait Enemy<T>: Drawable {
+ fn new() -> Self;
+ fn act(&mut self, count: u64) -> Vec<T>;
+ fn health(&self) -> f64;
+ fn set_health(&mut self, health: f64);
+}
+
+pub struct State<T, U> {
pub player: player::Player,
- pub enemies: Vec<Enemy>,
- pub bullets: Vec<EBullet>,
+ pub enemies: Vec<T>,
+ pub bullets: Vec<U>,
pub player_bullets: Vec<PBullet>,
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() }
+impl<T: Enemy<U>, U> State<T, U> {
+ pub fn new() -> State<T, U> {
+ State { player: player::Player::new(), enemies: vec![T::new()], bullets: Vec::new(), count: 0, player_bullets: Vec::new() }
}
}
@@ -178,17 +100,17 @@ fn dist(x1: i32, y1: i32, x2: i32, y2: i32) -> f64 {
pub trait Positioned {
fn x(&self) -> i32;
fn y(&self) -> i32;
- fn radius(&self) -> u32;
+ fn radius() -> u32;
fn contains(&self, x: i32, y: i32) -> bool {
let d = dist(self.x(), self.y(), x, y);
- d <= self.radius() as f64
+ d <= Self::radius() as f64
}
fn overlaps<T: Positioned>(&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();
+ let threshold = Self::radius() + T::radius();
if (sx - ox).abs() as u32 >= threshold || (sy - oy).abs() as u32 >= threshold {
return false;
}
@@ -202,68 +124,32 @@ pub trait Positioned {
}
}
-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()
+ self.real_x.round() as i32
}
fn y(&self) -> i32 {
- self.b.y()
+ self.real_y.round() as i32
}
- 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
+ fn radius() -> u32 {
+ 10
}
}
pub trait Drawable : Positioned {
- fn center(&self) -> (u32, u32);
+ fn center() -> (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 (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) {
+ fn center() -> (u32, u32) {
(16, 22)
}
}