diff --git a/src/game/game.rs b/src/game/game.rs index 23e1369..14be1b4 100644 --- a/src/game/game.rs +++ b/src/game/game.rs @@ -1,9 +1,33 @@ -use cgmath::Deg; +use cgmath::{Deg, Point2, Point3, Vector2}; use std::time::Instant; use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; use super::{Camera, InputStatus, Ship, System}; -use crate::{consts, content::Content, render::Sprite, render::Spriteable}; +use crate::{ + consts, + content::Content, + render::Spriteable, + render::{Sprite, SpriteTexture}, +}; + +pub struct Projectile { + pub position: Point2, + pub velocity: Vector2, + pub sprite: SpriteTexture, + pub angle: Deg, + pub lifetime: f32, +} + +impl Projectile { + pub fn tick(&mut self, t: f32) { + self.position += self.velocity * t; + self.lifetime -= t; + } + + pub fn is_expired(&self) -> bool { + return self.lifetime < 0.0; + } +} pub struct Game { pub input: InputStatus, @@ -13,6 +37,7 @@ pub struct Game { pub system: System, pub camera: Camera, paused: bool, + pub projectiles: Vec, pub time_scale: f32, } @@ -21,12 +46,14 @@ impl Game { Game { last_update: Instant::now(), input: InputStatus::new(), + projectiles: Vec::new(), player: Ship::new(&ct.ships[0], (0.0, 0.0).into()), camera: Camera { pos: (0.0, 0.0).into(), zoom: 500.0, }, system: System::new(&ct.systems[0]), + paused: false, time_scale: 1.0, test: Ship::new(&ct.ships[0], (100.0, 100.0).into()), @@ -57,17 +84,16 @@ impl Game { pub fn update(&mut self) { let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale; - self.player.engines_on = self.input.key_thrust; - if self.input.key_thrust { - self.player.physicsbody.thrust(50.0 * t); - } + self.projectiles.retain_mut(|p| { + p.tick(t); + !p.is_expired() + }); - if self.input.key_right { - self.player.physicsbody.rot(Deg(35.0) * t); - } - - if self.input.key_left { - self.player.physicsbody.rot(Deg(-35.0) * t); + // Update player and handle result + self.player.update_controls(&self.input); + let mut p = self.player.tick(t); + if p.projectiles.len() != 0 { + self.projectiles.append(&mut p.projectiles); } if self.input.v_scroll != 0.0 { @@ -76,7 +102,6 @@ impl Game { self.input.v_scroll = 0.0; } - self.player.physicsbody.tick(t); self.camera.pos = self.player.physicsbody.pos; self.last_update = Instant::now(); @@ -96,6 +121,21 @@ impl Game { // I've tried this, but it doesn't seem to work with transparent textures. sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z)); + // Don't waste time sorting these, they should always be on top. + for p in &self.projectiles { + sprites.push(Sprite { + texture: p.sprite.clone(), + pos: Point3 { + x: p.position.x, + y: p.position.y, + z: 1.0, + }, + size: 10.0, + angle: p.angle, + children: None, + }) + } + return sprites; } } diff --git a/src/game/inputstatus.rs b/src/game/inputstatus.rs index a496dbd..3bd1fd9 100644 --- a/src/game/inputstatus.rs +++ b/src/game/inputstatus.rs @@ -5,6 +5,7 @@ pub struct InputStatus { pub key_left: bool, pub key_right: bool, pub key_thrust: bool, + pub key_guns: bool, pub v_scroll: f32, } @@ -14,6 +15,7 @@ impl InputStatus { key_left: false, key_right: false, key_thrust: false, + key_guns: false, v_scroll: 0.0, scroll_speed: 10.0, } @@ -23,6 +25,7 @@ impl InputStatus { self.key_left = false; self.key_right = false; self.key_thrust = false; + self.key_guns = false; } pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) { @@ -31,6 +34,7 @@ impl InputStatus { VirtualKeyCode::Left => self.key_left = down, VirtualKeyCode::Right => self.key_right = down, VirtualKeyCode::Up => self.key_thrust = down, + VirtualKeyCode::Space => self.key_guns = down, _ => {} } } diff --git a/src/game/ship.rs b/src/game/ship.rs index 390f2f8..391d680 100644 --- a/src/game/ship.rs +++ b/src/game/ship.rs @@ -1,35 +1,118 @@ -use cgmath::{Deg, Point2, Point3}; +use cgmath::{Deg, EuclideanSpace, Matrix2, Point2, Point3, Vector2}; use crate::{ - content::{self, ship::Engine}, + content, physics::PhysicsBody, render::{Sprite, SpriteTexture, Spriteable, SubSprite}, }; +use super::{game::Projectile, InputStatus}; + +pub struct ShipControls { + pub left: bool, + pub right: bool, + pub thrust: bool, + pub guns: bool, +} + +impl ShipControls { + pub fn new() -> Self { + ShipControls { + left: false, + right: false, + thrust: false, + guns: false, + } + } +} + +pub struct ShipTickResult { + pub projectiles: Vec, +} + pub struct Ship { pub physicsbody: PhysicsBody, - pub engines_on: bool, + pub controls: ShipControls, sprite: SpriteTexture, size: f32, - engines: Vec, + engines: Vec, + guns: Vec, } impl Ship { - pub fn new(ct: &content::ship::Ship, pos: Point2) -> Self { + pub fn new(ct: &content::Ship, pos: Point2) -> Self { Ship { physicsbody: PhysicsBody::new(pos), sprite: SpriteTexture(ct.sprite.clone()), size: ct.size, engines: ct.engines.clone(), - engines_on: false, + guns: ct.guns.clone(), + controls: ShipControls::new(), } } + + pub fn update_controls(&mut self, input: &InputStatus) { + self.controls.thrust = input.key_thrust; + self.controls.right = input.key_right; + self.controls.left = input.key_left; + self.controls.guns = input.key_guns; + } + + pub fn fire_guns(&mut self) -> Vec { + let mut out = Vec::new(); + for i in &mut self.guns { + if i.active_cooldown > 0.0 { + continue; + } + i.active_cooldown = i.cooldown; + + let p = self.physicsbody.pos + + (Matrix2::from_angle(self.physicsbody.angle) * i.pos.to_vec()); + + out.push(Projectile { + position: p, + velocity: self.physicsbody.vel + + (Matrix2::from_angle(self.physicsbody.angle) * Vector2 { x: 0.0, y: 400.0 }), + angle: self.physicsbody.angle, + sprite: SpriteTexture("projectile::blaster".into()), + lifetime: 5.0, + }) + } + return out; + } + + pub fn tick(&mut self, t: f32) -> ShipTickResult { + if self.controls.thrust { + self.physicsbody.thrust(50.0 * t); + } + + if self.controls.right { + self.physicsbody.rot(Deg(35.0) * t); + } + + if self.controls.left { + self.physicsbody.rot(Deg(-35.0) * t); + } + + let p = if self.controls.guns { + self.fire_guns() + } else { + Vec::new() + }; + + self.physicsbody.tick(t); + for i in &mut self.guns { + i.active_cooldown -= t; + } + + return ShipTickResult { projectiles: p }; + } } impl Spriteable for Ship { fn get_sprite(&self) -> Sprite { - let engines = if self.engines_on { + let engines = if self.controls.thrust { Some( self.engines .iter()