Compare commits
2 Commits
ca3f289de5
...
0de278cb03
Author | SHA1 | Date |
---|---|---|
Mark | 0de278cb03 | |
Mark | 19fd1a91f2 |
|
@ -1,4 +0,0 @@
|
||||||
[engine."plasma"]
|
|
||||||
|
|
||||||
thrust = 100
|
|
||||||
flare.sprite_texture = "flare::ion"
|
|
|
@ -1,5 +1,7 @@
|
||||||
[gun."blaster"]
|
[gun."blaster"]
|
||||||
|
|
||||||
|
space.weapon = 10
|
||||||
|
|
||||||
# Angle of fire cone
|
# Angle of fire cone
|
||||||
# Smaller angle = more accurate
|
# Smaller angle = more accurate
|
||||||
spread = 2
|
spread = 2
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
[outfit."plasma engines"]
|
||||||
|
|
||||||
|
space.engine = 20
|
||||||
|
|
||||||
|
engine.thrust = 100
|
||||||
|
engine.flare_texture = "flare::ion"
|
||||||
|
steering.power = 20
|
|
@ -6,6 +6,11 @@ hull = 200
|
||||||
linear_drag = 0.2
|
linear_drag = 0.2
|
||||||
angular_drag = 0.2
|
angular_drag = 0.2
|
||||||
|
|
||||||
|
|
||||||
|
space.outfit = 200
|
||||||
|
space.engine = 50
|
||||||
|
space.weapon = 50
|
||||||
|
|
||||||
engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
|
engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
|
||||||
guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]
|
guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]
|
||||||
|
|
||||||
|
|
|
@ -18,21 +18,23 @@ use toml;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
pub use handle::{FactionHandle, TextureHandle};
|
pub use handle::{FactionHandle, TextureHandle};
|
||||||
pub use part::{Engine, EnginePoint, Faction, Gun, GunPoint, Relationship, Ship, System, Texture};
|
pub use part::{
|
||||||
|
EnginePoint, Faction, Gun, GunPoint, Outfit, OutfitSpace, Relationship, Ship, System, Texture,
|
||||||
|
};
|
||||||
|
|
||||||
mod syntax {
|
mod syntax {
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::part::{engine, faction, gun, ship, system, texture};
|
use crate::part::{faction, gun, outfit, ship, system, texture};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
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>>,
|
pub outfit: Option<HashMap<String, outfit::syntax::Outfit>>,
|
||||||
pub texture: Option<HashMap<String, texture::syntax::Texture>>,
|
pub texture: Option<HashMap<String, texture::syntax::Texture>>,
|
||||||
pub faction: Option<HashMap<String, faction::syntax::Faction>>,
|
pub faction: Option<HashMap<String, faction::syntax::Faction>>,
|
||||||
}
|
}
|
||||||
|
@ -43,7 +45,7 @@ mod syntax {
|
||||||
gun: None,
|
gun: None,
|
||||||
ship: None,
|
ship: None,
|
||||||
system: None,
|
system: None,
|
||||||
engine: None,
|
outfit: None,
|
||||||
texture: None,
|
texture: None,
|
||||||
faction: None,
|
faction: None,
|
||||||
}
|
}
|
||||||
|
@ -97,11 +99,11 @@ mod syntax {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(a) = other.engine {
|
if let Some(a) = other.outfit {
|
||||||
if self.engine.is_none() {
|
if self.outfit.is_none() {
|
||||||
self.engine = Some(a);
|
self.outfit = Some(a);
|
||||||
} else {
|
} else {
|
||||||
let sg = self.engine.as_mut().unwrap();
|
let sg = self.outfit.as_mut().unwrap();
|
||||||
for (k, v) in a {
|
for (k, v) in a {
|
||||||
if sg.contains_key(&k) {
|
if sg.contains_key(&k) {
|
||||||
bail!("Repeated engine name {k}");
|
bail!("Repeated engine name {k}");
|
||||||
|
@ -165,11 +167,11 @@ pub struct Content {
|
||||||
/// Ship bodies
|
/// Ship bodies
|
||||||
pub ships: Vec<part::ship::Ship>,
|
pub ships: Vec<part::ship::Ship>,
|
||||||
|
|
||||||
/// Gun outfits
|
/// Ship guns
|
||||||
pub guns: Vec<part::gun::Gun>,
|
pub guns: Vec<part::gun::Gun>,
|
||||||
|
|
||||||
/// Engine outfits
|
/// Outfits
|
||||||
pub engines: Vec<part::engine::Engine>,
|
pub outfits: Vec<part::outfit::Outfit>,
|
||||||
|
|
||||||
/// Textures
|
/// Textures
|
||||||
pub textures: Vec<part::texture::Texture>,
|
pub textures: Vec<part::texture::Texture>,
|
||||||
|
@ -256,7 +258,7 @@ impl Content {
|
||||||
systems: Vec::new(),
|
systems: Vec::new(),
|
||||||
ships: Vec::new(),
|
ships: Vec::new(),
|
||||||
guns: Vec::new(),
|
guns: Vec::new(),
|
||||||
engines: Vec::new(),
|
outfits: Vec::new(),
|
||||||
textures: Vec::new(),
|
textures: Vec::new(),
|
||||||
factions: Vec::new(),
|
factions: Vec::new(),
|
||||||
texture_index: HashMap::new(),
|
texture_index: HashMap::new(),
|
||||||
|
@ -275,8 +277,8 @@ impl Content {
|
||||||
if root.gun.is_some() {
|
if root.gun.is_some() {
|
||||||
part::gun::Gun::build(root.gun.take().unwrap(), &mut content)?;
|
part::gun::Gun::build(root.gun.take().unwrap(), &mut content)?;
|
||||||
}
|
}
|
||||||
if root.engine.is_some() {
|
if root.outfit.is_some() {
|
||||||
part::engine::Engine::build(root.engine.take().unwrap(), &mut content)?;
|
part::outfit::Outfit::build(root.outfit.take().unwrap(), &mut content)?;
|
||||||
}
|
}
|
||||||
if root.system.is_some() {
|
if root.system.is_some() {
|
||||||
part::system::System::build(root.system.take().unwrap(), &mut content)?;
|
part::system::System::build(root.system.take().unwrap(), &mut content)?;
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
|
||||||
|
|
||||||
use crate::{handle::TextureHandle, Content};
|
|
||||||
|
|
||||||
pub(crate) 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_texture: String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents an (foward) engine outfit that may be attached to a ship.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Engine {
|
|
||||||
/// The name of this outfit
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
/// How much thrust this engine produces
|
|
||||||
pub thrust: f32,
|
|
||||||
|
|
||||||
/// The flare sprite this engine creates.
|
|
||||||
/// Its location and size is determined by a ship's
|
|
||||||
/// engine points.
|
|
||||||
pub flare_sprite_texture: TextureHandle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::Build for Engine {
|
|
||||||
type InputSyntax = HashMap<String, syntax::Engine>;
|
|
||||||
|
|
||||||
fn build(engine: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
|
||||||
for (engine_name, engine) in engine {
|
|
||||||
let th = match ct.texture_index.get(&engine.flare.sprite_texture) {
|
|
||||||
None => bail!(
|
|
||||||
"In engine `{}`: texture `{}` doesn't exist",
|
|
||||||
engine_name,
|
|
||||||
engine.flare.sprite_texture
|
|
||||||
),
|
|
||||||
Some(t) => *t,
|
|
||||||
};
|
|
||||||
|
|
||||||
ct.engines.push(Self {
|
|
||||||
name: engine_name,
|
|
||||||
thrust: engine.thrust,
|
|
||||||
flare_sprite_texture: th,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,10 @@ use cgmath::Deg;
|
||||||
|
|
||||||
use crate::{handle::TextureHandle, Content};
|
use crate::{handle::TextureHandle, Content};
|
||||||
|
|
||||||
|
use super::OutfitSpace;
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
|
use super::super::shared;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
// These are never seen by code outside this crate.
|
// These are never seen by code outside this crate.
|
||||||
|
@ -16,6 +19,7 @@ pub(crate) mod syntax {
|
||||||
pub spread: f32,
|
pub spread: f32,
|
||||||
pub rate: f32,
|
pub rate: f32,
|
||||||
pub rate_rng: f32,
|
pub rate_rng: f32,
|
||||||
|
pub space: shared::syntax::OutfitSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -53,6 +57,9 @@ pub struct Gun {
|
||||||
/// Random variation of projectile delay, in seconds.
|
/// Random variation of projectile delay, in seconds.
|
||||||
/// Each shot waits (rate += rate_rng).
|
/// Each shot waits (rate += rate_rng).
|
||||||
pub rate_rng: f32,
|
pub rate_rng: f32,
|
||||||
|
|
||||||
|
/// How much space this gun uses
|
||||||
|
pub space: OutfitSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a projectile that a [`Gun`] produces.
|
/// Represents a projectile that a [`Gun`] produces.
|
||||||
|
@ -98,6 +105,7 @@ impl crate::Build for Gun {
|
||||||
|
|
||||||
ct.guns.push(Self {
|
ct.guns.push(Self {
|
||||||
name: gun_name,
|
name: gun_name,
|
||||||
|
space: gun.space.into(),
|
||||||
spread: Deg(gun.spread),
|
spread: Deg(gun.spread),
|
||||||
rate: gun.rate,
|
rate: gun.rate,
|
||||||
rate_rng: gun.rate_rng,
|
rate_rng: gun.rate_rng,
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
//! Content parts
|
//! Content parts
|
||||||
|
|
||||||
pub mod engine;
|
|
||||||
pub mod faction;
|
pub mod faction;
|
||||||
pub mod gun;
|
pub mod gun;
|
||||||
|
pub mod outfit;
|
||||||
|
mod shared;
|
||||||
pub mod ship;
|
pub mod ship;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
|
|
||||||
pub use engine::Engine;
|
|
||||||
pub use faction::{Faction, Relationship};
|
pub use faction::{Faction, Relationship};
|
||||||
pub use gun::{Gun, Projectile};
|
pub use gun::{Gun, Projectile};
|
||||||
|
pub use outfit::Outfit;
|
||||||
|
pub use shared::OutfitSpace;
|
||||||
pub use ship::{EnginePoint, GunPoint, Ship};
|
pub use ship::{EnginePoint, GunPoint, Ship};
|
||||||
pub use system::{Object, System};
|
pub use system::{Object, System};
|
||||||
pub use texture::Texture;
|
pub use texture::Texture;
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
use crate::{handle::TextureHandle, Content};
|
||||||
|
|
||||||
|
use super::OutfitSpace;
|
||||||
|
|
||||||
|
pub(crate) mod syntax {
|
||||||
|
use super::super::shared;
|
||||||
|
use serde::Deserialize;
|
||||||
|
// Raw serde syntax structs.
|
||||||
|
// These are never seen by code outside this crate.
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Outfit {
|
||||||
|
pub engine: Option<Engine>,
|
||||||
|
pub steering: Option<Steering>,
|
||||||
|
pub space: shared::syntax::OutfitSpace,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Engine {
|
||||||
|
pub thrust: f32,
|
||||||
|
pub flare_texture: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Steering {
|
||||||
|
pub power: f32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an outfit that may be attached to a ship.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Outfit {
|
||||||
|
/// The name of this outfit
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// How much engine thrust this outfit produces
|
||||||
|
pub engine_thrust: f32,
|
||||||
|
|
||||||
|
/// How much steering power this outfit provids
|
||||||
|
pub steer_power: f32,
|
||||||
|
|
||||||
|
/// The engine flare sprite this outfit creates.
|
||||||
|
/// Its location and size is determined by a ship's
|
||||||
|
/// engine points.
|
||||||
|
pub engine_flare_texture: Option<TextureHandle>,
|
||||||
|
|
||||||
|
/// How much space this outfit requires
|
||||||
|
pub space: OutfitSpace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::Build for Outfit {
|
||||||
|
type InputSyntax = HashMap<String, syntax::Outfit>;
|
||||||
|
|
||||||
|
fn build(outfits: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
||||||
|
for (outfit_name, outfit) in outfits {
|
||||||
|
let mut o = Self {
|
||||||
|
name: outfit_name.clone(),
|
||||||
|
engine_thrust: 0.0,
|
||||||
|
steer_power: 0.0,
|
||||||
|
engine_flare_texture: None,
|
||||||
|
space: OutfitSpace::from(outfit.space),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Engine stats
|
||||||
|
if let Some(engine) = outfit.engine {
|
||||||
|
let th = match ct.texture_index.get(&engine.flare_texture) {
|
||||||
|
None => bail!(
|
||||||
|
"In outfit `{}`: texture `{}` doesn't exist",
|
||||||
|
outfit_name,
|
||||||
|
engine.flare_texture
|
||||||
|
),
|
||||||
|
Some(t) => *t,
|
||||||
|
};
|
||||||
|
o.engine_thrust = engine.thrust;
|
||||||
|
o.engine_flare_texture = Some(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steering stats
|
||||||
|
if let Some(steer) = outfit.steering {
|
||||||
|
o.steer_power = steer.power;
|
||||||
|
}
|
||||||
|
|
||||||
|
ct.outfits.push(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
pub(crate) mod syntax {
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct OutfitSpace {
|
||||||
|
pub outfit: Option<u32>,
|
||||||
|
pub weapon: Option<u32>,
|
||||||
|
pub engine: Option<u32>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents outfit space, either that available in a ship
|
||||||
|
/// or that used by an outfit.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct OutfitSpace {
|
||||||
|
/// Total available outfit space.
|
||||||
|
/// This should be greater than weapon and engine.
|
||||||
|
pub outfit: u32,
|
||||||
|
|
||||||
|
/// Space for weapons
|
||||||
|
pub weapon: u32,
|
||||||
|
|
||||||
|
/// Space for engine
|
||||||
|
pub engine: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutfitSpace {
|
||||||
|
/// Make a new, zero OutfitSpace
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
outfit: 0,
|
||||||
|
weapon: 0,
|
||||||
|
engine: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does this outfit contain `smaller`?
|
||||||
|
pub fn can_contain(&self, smaller: Self) -> bool {
|
||||||
|
self.outfit >= (smaller.outfit + smaller.weapon + smaller.engine)
|
||||||
|
&& self.weapon >= smaller.weapon
|
||||||
|
&& self.engine >= smaller.engine
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free outfit space
|
||||||
|
pub fn free(&mut self, rhs: &Self) {
|
||||||
|
self.outfit += rhs.outfit + rhs.weapon + rhs.engine;
|
||||||
|
self.weapon += rhs.weapon;
|
||||||
|
self.engine += rhs.engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Occupy outfit space
|
||||||
|
pub fn occupy(&mut self, rhs: &Self) {
|
||||||
|
self.outfit -= rhs.outfit + rhs.weapon + rhs.engine;
|
||||||
|
self.weapon -= rhs.weapon;
|
||||||
|
self.engine -= rhs.engine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<syntax::OutfitSpace> for OutfitSpace {
|
||||||
|
fn from(value: syntax::OutfitSpace) -> Self {
|
||||||
|
Self {
|
||||||
|
outfit: value.outfit.unwrap_or(0),
|
||||||
|
engine: value.engine.unwrap_or(0),
|
||||||
|
weapon: value.weapon.unwrap_or(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,12 @@ use nalgebra::{point, Point};
|
||||||
|
|
||||||
use crate::{handle::TextureHandle, Content};
|
use crate::{handle::TextureHandle, Content};
|
||||||
|
|
||||||
|
use super::OutfitSpace;
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
|
use super::super::shared;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
// These are never seen by code outside this crate.
|
// These are never seen by code outside this crate.
|
||||||
|
|
||||||
|
@ -22,6 +26,7 @@ pub(crate) mod syntax {
|
||||||
pub collision: Collision,
|
pub collision: Collision,
|
||||||
pub angular_drag: f32,
|
pub angular_drag: f32,
|
||||||
pub linear_drag: f32,
|
pub linear_drag: f32,
|
||||||
|
pub space: shared::syntax::OutfitSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -86,6 +91,9 @@ pub struct Ship {
|
||||||
|
|
||||||
/// Reduction in velocity over time
|
/// Reduction in velocity over time
|
||||||
pub linear_drag: f32,
|
pub linear_drag: f32,
|
||||||
|
|
||||||
|
/// Outfit space in this ship
|
||||||
|
pub space: OutfitSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collision shape for this ship
|
/// Collision shape for this ship
|
||||||
|
@ -138,6 +146,7 @@ impl crate::Build for Ship {
|
||||||
name: ship_name,
|
name: ship_name,
|
||||||
sprite_texture: th,
|
sprite_texture: th,
|
||||||
mass: ship.mass,
|
mass: ship.mass,
|
||||||
|
space: OutfitSpace::from(ship.space),
|
||||||
angular_drag: ship.angular_drag,
|
angular_drag: ship.angular_drag,
|
||||||
linear_drag: ship.linear_drag,
|
linear_drag: ship.linear_drag,
|
||||||
size,
|
size,
|
||||||
|
|
|
@ -31,29 +31,31 @@ impl Game {
|
||||||
pub fn new(ct: Content) -> Self {
|
pub fn new(ct: Content) -> Self {
|
||||||
let mut physics = Physics::new();
|
let mut physics = Physics::new();
|
||||||
|
|
||||||
|
let mut o1 = outfits::ShipOutfits::new(&ct.ships[0]);
|
||||||
|
o1.add(ct.outfits[0].clone());
|
||||||
|
o1.add_gun(ct.guns[0].clone());
|
||||||
|
o1.add_gun(ct.guns[0].clone());
|
||||||
|
|
||||||
let h1 = physics.add_ship(
|
let h1 = physics.add_ship(
|
||||||
&ct.ships[0],
|
&ct.ships[0],
|
||||||
vec![
|
o1,
|
||||||
outfits::ShipOutfit::Gun(outfits::ShipGun::new(&ct.guns[0], 1)),
|
|
||||||
outfits::ShipOutfit::Gun(outfits::ShipGun::new(&ct.guns[0], 2)),
|
|
||||||
outfits::ShipOutfit::Engine(ct.engines[0].clone()),
|
|
||||||
],
|
|
||||||
Point2 { x: 0.0, y: 0.0 },
|
Point2 { x: 0.0, y: 0.0 },
|
||||||
FactionHandle { index: 0 },
|
FactionHandle { index: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
let h2 = physics.add_ship(
|
let h2 = physics.add_ship(
|
||||||
&ct.ships[0],
|
&ct.ships[0],
|
||||||
vec![],
|
outfits::ShipOutfits::new(&ct.ships[0]),
|
||||||
Point2 { x: 300.0, y: 300.0 },
|
Point2 { x: 300.0, y: 300.0 },
|
||||||
FactionHandle { index: 0 },
|
FactionHandle { index: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut o2 = outfits::ShipOutfits::new(&ct.ships[0]);
|
||||||
|
o2.add(ct.outfits[0].clone());
|
||||||
|
o2.add_gun(ct.guns[0].clone());
|
||||||
let h3 = physics.add_ship(
|
let h3 = physics.add_ship(
|
||||||
&ct.ships[0],
|
&ct.ships[0],
|
||||||
vec![outfits::ShipOutfit::Gun(outfits::ShipGun::new(
|
o2,
|
||||||
&ct.guns[0],
|
|
||||||
0,
|
|
||||||
))],
|
|
||||||
Point2 {
|
Point2 {
|
||||||
x: -300.0,
|
x: -300.0,
|
||||||
y: 300.0,
|
y: 300.0,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use cgmath::{Deg, Point3};
|
use cgmath::{Deg, Point3};
|
||||||
|
use content::{OutfitSpace, TextureHandle};
|
||||||
|
|
||||||
use crate::{content, render::SubSprite};
|
use crate::{content, render::SubSprite};
|
||||||
|
|
||||||
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ShipGun {
|
pub struct ShipGun {
|
||||||
pub kind: content::Gun,
|
pub kind: content::Gun,
|
||||||
pub cooldown: f32,
|
pub cooldown: f32,
|
||||||
|
@ -10,39 +12,66 @@ pub struct ShipGun {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShipGun {
|
impl ShipGun {
|
||||||
pub fn new(kind: &content::Gun, point: usize) -> Self {
|
pub fn new(kind: content::Gun, point: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: kind.clone(),
|
kind: kind,
|
||||||
point,
|
point,
|
||||||
cooldown: 0.0,
|
cooldown: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a specific outfit attached to a specific ship
|
/// This struct keeps track of the combined stats of a set of outfits.
|
||||||
pub enum ShipOutfit {
|
/// It does NOT check for sanity (e.g, removing an outfit that was never added)
|
||||||
Gun(ShipGun),
|
/// That is handled by ShipOutfits.
|
||||||
Engine(content::Engine),
|
#[derive(Debug)]
|
||||||
|
pub struct OutfitStatSum {
|
||||||
|
pub engine_thrust: f32,
|
||||||
|
pub steer_power: f32,
|
||||||
|
pub engine_flare_textures: Vec<TextureHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShipOutfit {
|
impl OutfitStatSum {
|
||||||
pub fn gun(&mut self) -> Option<&mut ShipGun> {
|
pub fn new() -> Self {
|
||||||
match self {
|
Self {
|
||||||
Self::Gun(g) => Some(g),
|
engine_thrust: 0.0,
|
||||||
_ => None,
|
steer_power: 0.0,
|
||||||
|
engine_flare_textures: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn engine(&self) -> Option<&content::Engine> {
|
pub fn add(&mut self, o: &content::Outfit) {
|
||||||
match self {
|
self.engine_thrust += o.engine_thrust;
|
||||||
Self::Engine(e) => Some(e),
|
if let Some(t) = o.engine_flare_texture {
|
||||||
_ => None,
|
self.engine_flare_textures.push(t);
|
||||||
|
};
|
||||||
|
self.steer_power += o.steer_power;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, o: &content::Outfit) {
|
||||||
|
self.engine_thrust -= o.engine_thrust;
|
||||||
|
if let Some(t) = o.engine_flare_texture {
|
||||||
|
self.engine_flare_textures.remove(
|
||||||
|
self.engine_flare_textures
|
||||||
|
.iter()
|
||||||
|
.position(|x| *x == t)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
self.steer_power -= o.steer_power;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents all the outfits attached to a ship.
|
||||||
|
/// Keeps track of the sum of their stats, so it mustn't be re-computed every frame.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ShipOutfits {
|
pub struct ShipOutfits {
|
||||||
outfits: Vec<ShipOutfit>,
|
pub stats: OutfitStatSum,
|
||||||
|
pub total_space: OutfitSpace,
|
||||||
|
|
||||||
|
available_space: OutfitSpace,
|
||||||
|
outfits: Vec<content::Outfit>,
|
||||||
|
guns: Vec<ShipGun>,
|
||||||
enginepoints: Vec<content::EnginePoint>,
|
enginepoints: Vec<content::EnginePoint>,
|
||||||
gunpoints: Vec<content::GunPoint>,
|
gunpoints: Vec<content::GunPoint>,
|
||||||
|
|
||||||
|
@ -54,66 +83,107 @@ pub struct ShipOutfits {
|
||||||
impl<'a> ShipOutfits {
|
impl<'a> ShipOutfits {
|
||||||
pub fn new(content: &content::Ship) -> Self {
|
pub fn new(content: &content::Ship) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
stats: OutfitStatSum::new(),
|
||||||
outfits: Vec::new(),
|
outfits: Vec::new(),
|
||||||
|
guns: Vec::new(),
|
||||||
|
available_space: content.space.clone(),
|
||||||
|
total_space: content.space.clone(),
|
||||||
enginepoints: content.engines.clone(),
|
enginepoints: content.engines.clone(),
|
||||||
gunpoints: content.guns.clone(),
|
gunpoints: content.guns.clone(),
|
||||||
engine_flare_sprites: vec![],
|
engine_flare_sprites: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, o: ShipOutfit) {
|
/// Does this outfit set contain the specified outfit?
|
||||||
|
pub fn contains_outfit(&self, o: &content::Outfit) -> bool {
|
||||||
|
match self.outfits.iter().position(|x| x.name == o.name) {
|
||||||
|
Some(_) => true,
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an outfit to this ship.
|
||||||
|
/// Returns true on success, and false on failure
|
||||||
|
/// TODO: failure reason enum
|
||||||
|
pub fn add(&mut self, o: content::Outfit) -> bool {
|
||||||
|
if !self.available_space.can_contain(o.space) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.available_space.occupy(&o.space);
|
||||||
|
self.stats.add(&o);
|
||||||
self.outfits.push(o);
|
self.outfits.push(o);
|
||||||
self.update_engine_flares();
|
self.update_engine_flares();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: is outfit in set?
|
||||||
|
pub fn remove(&mut self, o: content::Outfit) {
|
||||||
|
let i = match self.outfits.iter().position(|x| x.name == o.name) {
|
||||||
|
Some(i) => i,
|
||||||
|
None => panic!("Removed non-existing outfit"),
|
||||||
|
};
|
||||||
|
self.available_space.free(&o.space);
|
||||||
|
self.outfits.remove(i);
|
||||||
|
self.stats.remove(&o);
|
||||||
|
self.update_engine_flares();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a gun to this outfit set.
|
||||||
|
/// This automatically attaches the gun to the first available gunpoint,
|
||||||
|
/// and returns false (applying no changes) if no points are available.
|
||||||
|
pub fn add_gun(&mut self, gun: content::Gun) -> bool {
|
||||||
|
// Find first unused point
|
||||||
|
let mut p = None;
|
||||||
|
'outer: for i in 0..self.gunpoints.len() {
|
||||||
|
for g in &self.guns {
|
||||||
|
if g.point == i {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = Some(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All points are taken
|
||||||
|
if p.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sg = ShipGun::new(gun, p.unwrap());
|
||||||
|
self.guns.push(sg);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
|
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
|
||||||
self.outfits
|
self.guns.iter_mut()
|
||||||
.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, &content::GunPoint)> {
|
pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &content::GunPoint)> {
|
||||||
self.outfits
|
self.guns
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|x| x.gun())
|
|
||||||
.filter(|x| x.is_some())
|
|
||||||
.map(|x| x.unwrap())
|
|
||||||
.map(|x| (&self.gunpoints[x.point], x))
|
.map(|x| (&self.gunpoints[x.point], x))
|
||||||
.map(|(a, b)| (b, a))
|
.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 = &content::EnginePoint> {
|
|
||||||
self.enginepoints.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_engine_flares(&mut self) {
|
pub fn update_engine_flares(&mut self) {
|
||||||
// TODO: better way to pick flare texture
|
// TODO: better way to pick flare texture
|
||||||
self.engine_flare_sprites.clear();
|
self.engine_flare_sprites.clear();
|
||||||
let t = if let Some(e) = self.iter_engines().next() {
|
let t = if let Some(e) = self.stats.engine_flare_textures.iter().next() {
|
||||||
e.flare_sprite_texture
|
e
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.engine_flare_sprites = self
|
self.engine_flare_sprites = self
|
||||||
.iter_enginepoints()
|
.enginepoints
|
||||||
|
.iter()
|
||||||
.map(|p| SubSprite {
|
.map(|p| SubSprite {
|
||||||
pos: Point3 {
|
pos: Point3 {
|
||||||
x: p.pos.x,
|
x: p.pos.x,
|
||||||
y: p.pos.y,
|
y: p.pos.y,
|
||||||
z: 1.0,
|
z: 1.0,
|
||||||
},
|
},
|
||||||
texture: t,
|
texture: *t,
|
||||||
angle: Deg(0.0),
|
angle: Deg(0.0),
|
||||||
size: p.size,
|
size: p.size,
|
||||||
})
|
})
|
||||||
|
|
|
@ -52,18 +52,13 @@ pub struct Ship {
|
||||||
impl Ship {
|
impl Ship {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
c: &content::Ship,
|
c: &content::Ship,
|
||||||
outfits: Vec<outfits::ShipOutfit>,
|
outfits: outfits::ShipOutfits,
|
||||||
physics_handle: ShipHandle,
|
physics_handle: ShipHandle,
|
||||||
faction: FactionHandle,
|
faction: FactionHandle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut o = outfits::ShipOutfits::new(c);
|
|
||||||
for x in outfits.into_iter() {
|
|
||||||
o.add(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ship {
|
Ship {
|
||||||
physics_handle,
|
physics_handle,
|
||||||
outfits: o,
|
outfits,
|
||||||
sprite_texture: c.sprite_texture,
|
sprite_texture: c.sprite_texture,
|
||||||
size: c.size,
|
size: c.size,
|
||||||
hull: c.hull,
|
hull: c.hull,
|
||||||
|
@ -135,17 +130,18 @@ impl Ship {
|
||||||
let engine_force = ship_rot * t;
|
let engine_force = ship_rot * t;
|
||||||
|
|
||||||
if self.controls.thrust {
|
if self.controls.thrust {
|
||||||
for e in self.outfits.iter_engines() {
|
r.apply_impulse(
|
||||||
r.apply_impulse(vector![engine_force.x, engine_force.y] * e.thrust, true);
|
vector![engine_force.x, engine_force.y] * self.outfits.stats.engine_thrust,
|
||||||
}
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.controls.right {
|
if self.controls.right {
|
||||||
r.apply_torque_impulse(-1000.0 * t, true);
|
r.apply_torque_impulse(self.outfits.stats.steer_power * -100.0 * t, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.controls.left {
|
if self.controls.left {
|
||||||
r.apply_torque_impulse(1000.0 * t, true);
|
r.apply_torque_impulse(self.outfits.stats.steer_power * 100.0 * t, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let p = if self.controls.guns {
|
let p = if self.controls.guns {
|
||||||
|
|
|
@ -82,7 +82,7 @@ impl Physics {
|
||||||
pub fn add_ship(
|
pub fn add_ship(
|
||||||
&mut self,
|
&mut self,
|
||||||
ct: &content::Ship,
|
ct: &content::Ship,
|
||||||
outfits: Vec<outfits::ShipOutfit>,
|
outfits: outfits::ShipOutfits,
|
||||||
position: Point2<f32>,
|
position: Point2<f32>,
|
||||||
faction: FactionHandle,
|
faction: FactionHandle,
|
||||||
) -> ShipHandle {
|
) -> ShipHandle {
|
||||||
|
|
Loading…
Reference in New Issue