Added outfit manager
parent
389803eae9
commit
b283fadce9
|
@ -0,0 +1,4 @@
|
||||||
|
[engine."plasma"]
|
||||||
|
|
||||||
|
thrust = 50
|
||||||
|
flare.sprite = "flare::ion"
|
|
@ -1,5 +1,22 @@
|
||||||
[gun."blaster"]
|
[gun."blaster"]
|
||||||
|
|
||||||
|
# Angle of fire cone
|
||||||
|
# Smaller angle = more accurate
|
||||||
|
spread = 2
|
||||||
|
|
||||||
|
# Average delay between shots
|
||||||
|
rate = 0.2
|
||||||
|
# Random rate variation (+- this in both directions)
|
||||||
|
rate_rng = 0.1
|
||||||
|
|
||||||
|
|
||||||
projectile.sprite = "projectile::blaster"
|
projectile.sprite = "projectile::blaster"
|
||||||
projectile.size = 100
|
# Height of projectile in game units
|
||||||
|
projectile.size = 10
|
||||||
|
projectile.size_rng = 0.0
|
||||||
|
# Speed of projectile, in game units/second
|
||||||
projectile.speed = 300
|
projectile.speed = 300
|
||||||
|
projectile.speed_rng = 10.0
|
||||||
|
# Lifetime of projectile, in seconds
|
||||||
projectile.lifetime = 2.0
|
projectile.lifetime = 2.0
|
||||||
|
projectile.lifetime_rng = 0.2
|
||||||
|
|
|
@ -3,4 +3,4 @@ sprite = "ship::gypsum"
|
||||||
size = 100
|
size = 100
|
||||||
|
|
||||||
engines = [{ x = 0.0, y = -105, size = 50.0 }]
|
engines = [{ x = 0.0, y = -105, size = 50.0 }]
|
||||||
guns = [{ x = 0.0, y = 100 }]
|
guns = [{ x = 0.0, y = 100 }, { x = 10.0, y = 80 }, { x = -10.0, y = 80 }]
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub(super) mod syntax {
|
||||||
|
use serde::Deserialize;
|
||||||
|
// Raw serde syntax structs.
|
||||||
|
// These are never seen by code outside this crate.
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Engine {
|
||||||
|
pub thrust: f32,
|
||||||
|
pub flare: Flare,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Flare {
|
||||||
|
pub sprite: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Engine {
|
||||||
|
pub name: String,
|
||||||
|
pub thrust: f32,
|
||||||
|
pub flare_sprite: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Build for Engine {
|
||||||
|
fn build(root: &super::syntax::Root) -> Result<Vec<Self>> {
|
||||||
|
let engine = if let Some(engine) = &root.engine {
|
||||||
|
engine
|
||||||
|
} else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for (engine_name, engine) in engine {
|
||||||
|
out.push(Self {
|
||||||
|
name: engine_name.to_owned(),
|
||||||
|
thrust: engine.thrust,
|
||||||
|
flare_sprite: engine.flare.sprite.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(out);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use cgmath::Deg;
|
||||||
|
|
||||||
pub(super) mod syntax {
|
pub(super) mod syntax {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -8,14 +9,20 @@ pub(super) mod syntax {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Gun {
|
pub struct Gun {
|
||||||
pub projectile: Projectile,
|
pub projectile: Projectile,
|
||||||
|
pub spread: f32,
|
||||||
|
pub rate: f32,
|
||||||
|
pub rate_rng: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Projectile {
|
pub struct Projectile {
|
||||||
pub sprite: String,
|
pub sprite: String,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
|
pub size_rng: f32,
|
||||||
pub speed: f32,
|
pub speed: f32,
|
||||||
|
pub speed_rng: f32,
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
|
pub lifetime_rng: f32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,14 +30,20 @@ pub(super) mod syntax {
|
||||||
pub struct Gun {
|
pub struct Gun {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub projectile: Projectile,
|
pub projectile: Projectile,
|
||||||
|
pub spread: Deg<f32>,
|
||||||
|
pub rate: f32,
|
||||||
|
pub rate_rng: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Projectile {
|
pub struct Projectile {
|
||||||
pub sprite: String,
|
pub sprite: String,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
|
pub size_rng: f32,
|
||||||
pub speed: f32,
|
pub speed: f32,
|
||||||
|
pub speed_rng: f32,
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
|
pub lifetime_rng: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Build for Gun {
|
impl super::Build for Gun {
|
||||||
|
@ -45,11 +58,17 @@ impl super::Build for Gun {
|
||||||
for (gun_name, gun) in gun {
|
for (gun_name, gun) in gun {
|
||||||
out.push(Self {
|
out.push(Self {
|
||||||
name: gun_name.to_owned(),
|
name: gun_name.to_owned(),
|
||||||
|
spread: Deg(gun.spread),
|
||||||
|
rate: gun.rate,
|
||||||
|
rate_rng: gun.rate_rng,
|
||||||
projectile: Projectile {
|
projectile: Projectile {
|
||||||
sprite: gun.projectile.sprite.to_owned(),
|
sprite: gun.projectile.sprite.to_owned(),
|
||||||
size: gun.projectile.size,
|
size: gun.projectile.size,
|
||||||
|
size_rng: gun.projectile.size_rng,
|
||||||
speed: gun.projectile.speed,
|
speed: gun.projectile.speed,
|
||||||
|
speed_rng: gun.projectile.speed_rng,
|
||||||
lifetime: gun.projectile.lifetime,
|
lifetime: gun.projectile.lifetime,
|
||||||
|
lifetime_rng: gun.projectile.lifetime_rng,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
mod engine;
|
||||||
mod gun;
|
mod gun;
|
||||||
mod ship;
|
mod ship;
|
||||||
mod system;
|
mod system;
|
||||||
|
|
||||||
|
pub use engine::Engine;
|
||||||
pub use gun::{Gun, Projectile};
|
pub use gun::{Gun, Projectile};
|
||||||
pub use ship::{Engine, Ship, ShipGun};
|
pub use ship::{EnginePoint, GunPoint, Ship};
|
||||||
pub use system::{Object, System};
|
pub use system::{Object, System};
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
@ -15,7 +17,7 @@ use walkdir::WalkDir;
|
||||||
|
|
||||||
mod syntax {
|
mod syntax {
|
||||||
use super::HashMap;
|
use super::HashMap;
|
||||||
use super::{gun, ship, system};
|
use super::{engine, gun, ship, system};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -23,6 +25,7 @@ mod syntax {
|
||||||
pub gun: Option<HashMap<String, gun::syntax::Gun>>,
|
pub gun: Option<HashMap<String, gun::syntax::Gun>>,
|
||||||
pub ship: Option<HashMap<String, ship::syntax::Ship>>,
|
pub ship: Option<HashMap<String, ship::syntax::Ship>>,
|
||||||
pub system: Option<HashMap<String, system::syntax::System>>,
|
pub system: Option<HashMap<String, system::syntax::System>>,
|
||||||
|
pub engine: Option<HashMap<String, engine::syntax::Engine>>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +41,7 @@ pub struct Content {
|
||||||
pub systems: Vec<system::System>,
|
pub systems: Vec<system::System>,
|
||||||
pub ships: Vec<ship::Ship>,
|
pub ships: Vec<ship::Ship>,
|
||||||
pub guns: Vec<gun::Gun>,
|
pub guns: Vec<gun::Gun>,
|
||||||
|
pub engines: Vec<engine::Engine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! quick_name_dup_check {
|
macro_rules! quick_name_dup_check {
|
||||||
|
@ -70,6 +74,7 @@ impl Content {
|
||||||
quick_name_dup_check!(self.systems, root, system::System::build);
|
quick_name_dup_check!(self.systems, root, system::System::build);
|
||||||
quick_name_dup_check!(self.guns, root, gun::Gun::build);
|
quick_name_dup_check!(self.guns, root, gun::Gun::build);
|
||||||
quick_name_dup_check!(self.ships, root, ship::Ship::build);
|
quick_name_dup_check!(self.ships, root, ship::Ship::build);
|
||||||
|
quick_name_dup_check!(self.engines, root, engine::Engine::build);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +83,7 @@ impl Content {
|
||||||
systems: Vec::new(),
|
systems: Vec::new(),
|
||||||
ships: Vec::new(),
|
ships: Vec::new(),
|
||||||
guns: Vec::new(),
|
guns: Vec::new(),
|
||||||
|
engines: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
||||||
|
|
|
@ -36,21 +36,19 @@ pub struct Ship {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub sprite: String,
|
pub sprite: String,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
pub engines: Vec<Engine>,
|
pub engines: Vec<EnginePoint>,
|
||||||
pub guns: Vec<ShipGun>,
|
pub guns: Vec<GunPoint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Engine {
|
pub struct EnginePoint {
|
||||||
pub pos: Point2<f32>,
|
pub pos: Point2<f32>,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ShipGun {
|
pub struct GunPoint {
|
||||||
pub pos: Point2<f32>,
|
pub pos: Point2<f32>,
|
||||||
pub cooldown: f32,
|
|
||||||
pub active_cooldown: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Build for Ship {
|
impl super::Build for Ship {
|
||||||
|
@ -70,7 +68,7 @@ impl super::Build for Ship {
|
||||||
engines: ship
|
engines: ship
|
||||||
.engines
|
.engines
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| Engine {
|
.map(|e| EnginePoint {
|
||||||
pos: Point2 { x: e.x, y: e.y },
|
pos: Point2 { x: e.x, y: e.y },
|
||||||
size: e.size,
|
size: e.size,
|
||||||
})
|
})
|
||||||
|
@ -78,10 +76,8 @@ impl super::Build for Ship {
|
||||||
guns: ship
|
guns: ship
|
||||||
.guns
|
.guns
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| ShipGun {
|
.map(|e| GunPoint {
|
||||||
pos: Point2 { x: e.x, y: e.y },
|
pos: Point2 { x: e.x, y: e.y },
|
||||||
cooldown: 0.2,
|
|
||||||
active_cooldown: 0.0,
|
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ use cgmath::{Deg, Point2, Point3, Vector2};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||||
|
|
||||||
use super::{Camera, InputStatus, Ship, System};
|
use super::{ship, Camera, InputStatus, System};
|
||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
consts,
|
||||||
content::Content,
|
content::Content,
|
||||||
|
@ -16,6 +16,7 @@ pub struct Projectile {
|
||||||
pub sprite: SpriteTexture,
|
pub sprite: SpriteTexture,
|
||||||
pub angle: Deg<f32>,
|
pub angle: Deg<f32>,
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
|
pub size: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Projectile {
|
impl Projectile {
|
||||||
|
@ -32,8 +33,8 @@ impl Projectile {
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
pub input: InputStatus,
|
pub input: InputStatus,
|
||||||
pub last_update: Instant,
|
pub last_update: Instant,
|
||||||
pub player: Ship,
|
pub player: ship::Ship,
|
||||||
pub test: Ship,
|
pub test: ship::Ship,
|
||||||
pub system: System,
|
pub system: System,
|
||||||
pub camera: Camera,
|
pub camera: Camera,
|
||||||
paused: bool,
|
paused: bool,
|
||||||
|
@ -47,7 +48,15 @@ impl Game {
|
||||||
last_update: Instant::now(),
|
last_update: Instant::now(),
|
||||||
input: InputStatus::new(),
|
input: InputStatus::new(),
|
||||||
projectiles: Vec::new(),
|
projectiles: Vec::new(),
|
||||||
player: Ship::new(&ct.ships[0], (0.0, 0.0).into()),
|
player: ship::Ship::new(
|
||||||
|
&ct.ships[0],
|
||||||
|
vec![
|
||||||
|
ship::ShipOutfit::Gun(ship::ShipGun::new(ct.guns[0].clone(), 1)),
|
||||||
|
ship::ShipOutfit::Gun(ship::ShipGun::new(ct.guns[0].clone(), 2)),
|
||||||
|
ship::ShipOutfit::Engine(ct.engines[0].clone()),
|
||||||
|
],
|
||||||
|
(0.0, 0.0).into(),
|
||||||
|
),
|
||||||
camera: Camera {
|
camera: Camera {
|
||||||
pos: (0.0, 0.0).into(),
|
pos: (0.0, 0.0).into(),
|
||||||
zoom: 500.0,
|
zoom: 500.0,
|
||||||
|
@ -56,7 +65,7 @@ impl Game {
|
||||||
|
|
||||||
paused: false,
|
paused: false,
|
||||||
time_scale: 1.0,
|
time_scale: 1.0,
|
||||||
test: Ship::new(&ct.ships[0], (100.0, 100.0).into()),
|
test: ship::Ship::new(&ct.ships[0], vec![], (100.0, 100.0).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +139,7 @@ impl Game {
|
||||||
y: p.position.y,
|
y: p.position.y,
|
||||||
z: 1.0,
|
z: 1.0,
|
||||||
},
|
},
|
||||||
size: 10.0,
|
size: p.size,
|
||||||
angle: p.angle,
|
angle: p.angle,
|
||||||
children: None,
|
children: None,
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,6 +8,5 @@ mod systemobject;
|
||||||
pub use camera::Camera;
|
pub use camera::Camera;
|
||||||
pub use game::Game;
|
pub use game::Game;
|
||||||
pub use inputstatus::InputStatus;
|
pub use inputstatus::InputStatus;
|
||||||
pub use ship::Ship;
|
|
||||||
pub use system::System;
|
pub use system::System;
|
||||||
pub use systemobject::SystemObject;
|
pub use systemobject::SystemObject;
|
||||||
|
|
143
src/game/ship.rs
143
src/game/ship.rs
|
@ -1,143 +0,0 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, Matrix2, Point2, Point3, Vector2};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
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<Projectile>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Ship {
|
|
||||||
pub physicsbody: PhysicsBody,
|
|
||||||
pub controls: ShipControls,
|
|
||||||
|
|
||||||
sprite: SpriteTexture,
|
|
||||||
size: f32,
|
|
||||||
engines: Vec<content::Engine>,
|
|
||||||
guns: Vec<content::ShipGun>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ship {
|
|
||||||
pub fn new(ct: &content::Ship, pos: Point2<f32>) -> Self {
|
|
||||||
Ship {
|
|
||||||
physicsbody: PhysicsBody::new(pos),
|
|
||||||
sprite: SpriteTexture(ct.sprite.clone()),
|
|
||||||
size: ct.size,
|
|
||||||
engines: ct.engines.clone(),
|
|
||||||
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<Projectile> {
|
|
||||||
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.controls.thrust {
|
|
||||||
Some(
|
|
||||||
self.engines
|
|
||||||
.iter()
|
|
||||||
.map(|e| SubSprite {
|
|
||||||
pos: Point3 {
|
|
||||||
x: e.pos.x,
|
|
||||||
y: e.pos.y,
|
|
||||||
z: 1.0,
|
|
||||||
},
|
|
||||||
texture: SpriteTexture("flare::ion".to_owned()),
|
|
||||||
angle: Deg(0.0),
|
|
||||||
size: e.size,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Sprite {
|
|
||||||
pos: (self.physicsbody.pos.x, self.physicsbody.pos.y, 1.0).into(),
|
|
||||||
texture: self.sprite.clone(), // TODO: sprite texture should be easy to clone
|
|
||||||
angle: self.physicsbody.angle,
|
|
||||||
size: self.size,
|
|
||||||
children: engines,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
mod outfits;
|
||||||
|
mod ship;
|
||||||
|
|
||||||
|
pub use outfits::{ShipGun, ShipOutfit, ShipOutfits};
|
||||||
|
pub use ship::Ship;
|
||||||
|
|
||||||
|
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<Projectile>,
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
use cgmath::{Deg, Point3};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
content::{self, EnginePoint, GunPoint},
|
||||||
|
render::{SpriteTexture, SubSprite},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ShipGun {
|
||||||
|
pub kind: content::Gun,
|
||||||
|
pub cooldown: f32,
|
||||||
|
pub point: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShipGun {
|
||||||
|
pub fn new(kind: content::Gun, point: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
point,
|
||||||
|
cooldown: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ShipOutfit {
|
||||||
|
Gun(ShipGun),
|
||||||
|
Engine(content::Engine),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShipOutfit {
|
||||||
|
pub fn gun(&mut self) -> Option<&mut ShipGun> {
|
||||||
|
match self {
|
||||||
|
Self::Gun(g) => Some(g),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn engine(&self) -> Option<&content::Engine> {
|
||||||
|
match self {
|
||||||
|
Self::Engine(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShipOutfits {
|
||||||
|
outfits: Vec<ShipOutfit>,
|
||||||
|
enginepoints: Vec<content::EnginePoint>,
|
||||||
|
gunpoints: Vec<content::GunPoint>,
|
||||||
|
|
||||||
|
// Minor performance optimization, since we
|
||||||
|
// rarely need to re-compute these.
|
||||||
|
engine_flare_sprites: Vec<SubSprite>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ShipOutfits {
|
||||||
|
pub fn new(enginepoints: Vec<content::EnginePoint>, gunpoints: Vec<content::GunPoint>) -> Self {
|
||||||
|
Self {
|
||||||
|
outfits: Vec::new(),
|
||||||
|
enginepoints,
|
||||||
|
gunpoints,
|
||||||
|
engine_flare_sprites: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, o: ShipOutfit) {
|
||||||
|
self.outfits.push(o);
|
||||||
|
self.update_engine_flares();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
|
||||||
|
self.outfits
|
||||||
|
.iter_mut()
|
||||||
|
.map(|x| x.gun())
|
||||||
|
.filter(|x| x.is_some())
|
||||||
|
.map(|x| x.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &GunPoint)> {
|
||||||
|
self.outfits
|
||||||
|
.iter_mut()
|
||||||
|
.map(|x| x.gun())
|
||||||
|
.filter(|x| x.is_some())
|
||||||
|
.map(|x| x.unwrap())
|
||||||
|
.map(|x| (&self.gunpoints[x.point], x))
|
||||||
|
.map(|(a, b)| (b, a))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_engines(&self) -> impl Iterator<Item = &content::Engine> {
|
||||||
|
self.outfits
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.engine())
|
||||||
|
.filter(|x| x.is_some())
|
||||||
|
.map(|x| x.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_enginepoints(&self) -> impl Iterator<Item = &EnginePoint> {
|
||||||
|
self.enginepoints.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_engine_flares(&mut self) {
|
||||||
|
// TODO: better way to pick flare texture
|
||||||
|
self.engine_flare_sprites.clear();
|
||||||
|
let t = if let Some(e) = self.iter_engines().next() {
|
||||||
|
SpriteTexture(e.flare_sprite.clone())
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.engine_flare_sprites = self
|
||||||
|
.iter_enginepoints()
|
||||||
|
.map(|p| SubSprite {
|
||||||
|
pos: Point3 {
|
||||||
|
x: p.pos.x,
|
||||||
|
y: p.pos.y,
|
||||||
|
z: 1.0,
|
||||||
|
},
|
||||||
|
texture: t.clone(),
|
||||||
|
angle: Deg(0.0),
|
||||||
|
size: p.size,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_engine_flares(&self) -> Vec<SubSprite> {
|
||||||
|
return self.engine_flare_sprites.clone();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
use cgmath::{Deg, EuclideanSpace, Matrix2, Point2, Vector2};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use super::super::game::Projectile;
|
||||||
|
use super::ShipOutfit;
|
||||||
|
use super::{outfits::ShipOutfits, InputStatus, ShipControls, ShipTickResult};
|
||||||
|
use crate::{
|
||||||
|
content,
|
||||||
|
physics::PhysicsBody,
|
||||||
|
render::{Sprite, SpriteTexture, Spriteable},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Ship {
|
||||||
|
pub physicsbody: PhysicsBody,
|
||||||
|
pub controls: ShipControls,
|
||||||
|
outfits: ShipOutfits,
|
||||||
|
|
||||||
|
sprite: SpriteTexture,
|
||||||
|
size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ship {
|
||||||
|
pub fn new(ct: &content::Ship, outfits: Vec<ShipOutfit>, pos: Point2<f32>) -> Self {
|
||||||
|
let mut o = ShipOutfits::new(ct.engines.clone(), ct.guns.clone());
|
||||||
|
for x in outfits.into_iter() {
|
||||||
|
o.add(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ship {
|
||||||
|
physicsbody: PhysicsBody::new(pos),
|
||||||
|
controls: ShipControls::new(),
|
||||||
|
|
||||||
|
outfits: o,
|
||||||
|
sprite: SpriteTexture(ct.sprite.clone()),
|
||||||
|
size: ct.size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Projectile> {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
for (g, p) in self.outfits.iter_guns_points() {
|
||||||
|
if g.cooldown > 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
g.cooldown = g.kind.rate + rng.gen_range(-g.kind.rate_rng..=g.kind.rate_rng);
|
||||||
|
|
||||||
|
let pos = self.physicsbody.pos
|
||||||
|
+ (Matrix2::from_angle(self.physicsbody.angle) * p.pos.to_vec());
|
||||||
|
|
||||||
|
let vel = self.physicsbody.vel
|
||||||
|
+ (Matrix2::from_angle(
|
||||||
|
self.physicsbody.angle
|
||||||
|
+ Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)),
|
||||||
|
) * Vector2 {
|
||||||
|
x: 0.0,
|
||||||
|
y: g.kind.projectile.speed
|
||||||
|
+ rng.gen_range(-g.kind.projectile.speed_rng..=g.kind.projectile.speed_rng),
|
||||||
|
});
|
||||||
|
|
||||||
|
out.push(Projectile {
|
||||||
|
position: pos,
|
||||||
|
velocity: vel,
|
||||||
|
angle: self.physicsbody.angle,
|
||||||
|
sprite: SpriteTexture(g.kind.projectile.sprite.clone()),
|
||||||
|
lifetime: g.kind.projectile.lifetime
|
||||||
|
+ rng.gen_range(
|
||||||
|
-g.kind.projectile.lifetime_rng..=g.kind.projectile.lifetime_rng,
|
||||||
|
),
|
||||||
|
size: g.kind.projectile.size
|
||||||
|
+ rng.gen_range(-g.kind.projectile.size_rng..=g.kind.projectile.size_rng),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self, t: f32) -> ShipTickResult {
|
||||||
|
if self.controls.thrust {
|
||||||
|
for e in self.outfits.iter_engines() {
|
||||||
|
self.physicsbody.thrust(e.thrust * 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 self.outfits.iter_guns() {
|
||||||
|
i.cooldown -= t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShipTickResult { projectiles: p };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spriteable for Ship {
|
||||||
|
fn get_sprite(&self) -> Sprite {
|
||||||
|
Sprite {
|
||||||
|
pos: (self.physicsbody.pos.x, self.physicsbody.pos.y, 1.0).into(),
|
||||||
|
texture: self.sprite.clone(), // TODO: sprite texture should be easy to clone
|
||||||
|
angle: self.physicsbody.angle,
|
||||||
|
size: self.size,
|
||||||
|
|
||||||
|
children: if self.controls.thrust {
|
||||||
|
Some(self.outfits.get_engine_flares())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ use cgmath::{Deg, Point3};
|
||||||
|
|
||||||
use super::SpriteTexture;
|
use super::SpriteTexture;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Sprite {
|
pub struct Sprite {
|
||||||
/// The sprite texture to draw
|
/// The sprite texture to draw
|
||||||
pub texture: SpriteTexture,
|
pub texture: SpriteTexture,
|
||||||
|
@ -21,6 +22,7 @@ pub struct Sprite {
|
||||||
pub children: Option<Vec<SubSprite>>,
|
pub children: Option<Vec<SubSprite>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct SubSprite {
|
pub struct SubSprite {
|
||||||
/// The sprite texture to draw
|
/// The sprite texture to draw
|
||||||
pub texture: SpriteTexture,
|
pub texture: SpriteTexture,
|
||||||
|
|
Loading…
Reference in New Issue