Reworked game data
parent
acb3315392
commit
9e0551ae12
|
@ -1,3 +1,5 @@
|
|||
use std::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
|
||||
pub(crate) mod syntax {
|
||||
use serde::Deserialize;
|
||||
|
||||
|
@ -9,6 +11,8 @@ pub(crate) mod syntax {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: user-defined space values
|
||||
|
||||
/// Represents outfit space, either that available in a ship
|
||||
/// or that used by an outfit.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -25,7 +29,7 @@ pub struct OutfitSpace {
|
|||
}
|
||||
|
||||
impl OutfitSpace {
|
||||
/// Make a new, zero OutfitSpace
|
||||
/// Make a new, zeroed OutfitSpace
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
outfit: 0,
|
||||
|
@ -34,28 +38,54 @@ impl OutfitSpace {
|
|||
}
|
||||
}
|
||||
|
||||
/// Does this outfit contain `smaller`?
|
||||
/// Can this outfit contain `smaller`?
|
||||
pub fn can_contain(&self, smaller: &Self) -> bool {
|
||||
self.outfit >= (smaller.outfit + smaller.weapon + smaller.engine)
|
||||
self.outfit >= smaller.outfit
|
||||
&& 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;
|
||||
impl Sub for OutfitSpace {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
OutfitSpace {
|
||||
outfit: self.outfit - rhs.outfit,
|
||||
weapon: self.weapon - rhs.weapon,
|
||||
engine: self.engine - rhs.engine,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Occupy outfit space
|
||||
pub fn occupy(&mut self, rhs: &Self) {
|
||||
self.outfit -= rhs.outfit + rhs.weapon + rhs.engine;
|
||||
impl SubAssign for OutfitSpace {
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
self.outfit -= rhs.outfit;
|
||||
self.weapon -= rhs.weapon;
|
||||
self.engine -= rhs.engine;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for OutfitSpace {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
OutfitSpace {
|
||||
outfit: self.outfit + rhs.outfit,
|
||||
weapon: self.weapon + rhs.weapon,
|
||||
engine: self.engine + rhs.engine,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for OutfitSpace {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.outfit += rhs.outfit;
|
||||
self.weapon += rhs.weapon;
|
||||
self.engine += rhs.engine;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<syntax::OutfitSpace> for OutfitSpace {
|
||||
fn from(value: syntax::OutfitSpace) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use cgmath::Point2;
|
||||
|
@ -160,11 +160,28 @@ pub struct EnginePoint {
|
|||
/// A gun point on a ship.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GunPoint {
|
||||
/// This gun point's index in this ship
|
||||
pub idx: u32,
|
||||
|
||||
/// This gun point's position, in game units,
|
||||
/// relative to the ship's center.
|
||||
pub pos: Point2<f32>,
|
||||
}
|
||||
|
||||
impl Eq for GunPoint {}
|
||||
impl PartialEq for GunPoint {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.idx == other.idx
|
||||
}
|
||||
}
|
||||
|
||||
// We use a hashmap of these in OutfitSet
|
||||
impl Hash for GunPoint {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.idx.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters for a ship's collapse sequence
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShipCollapse {
|
||||
|
@ -345,6 +362,18 @@ impl crate::Build for Ship {
|
|||
}
|
||||
};
|
||||
|
||||
// TODO: document this
|
||||
let mut guns = Vec::new();
|
||||
for g in ship.guns {
|
||||
guns.push(GunPoint {
|
||||
idx: guns.len() as u32,
|
||||
pos: Point2 {
|
||||
x: g.x * size * aspect / 2.0,
|
||||
y: g.y * size / 2.0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
ct.ships.push(Self {
|
||||
aspect,
|
||||
collapse,
|
||||
|
@ -370,16 +399,7 @@ impl crate::Build for Ship {
|
|||
})
|
||||
.collect(),
|
||||
|
||||
guns: ship
|
||||
.guns
|
||||
.iter()
|
||||
.map(|e| GunPoint {
|
||||
pos: Point2 {
|
||||
x: e.x * size * aspect / 2.0,
|
||||
y: e.y * size / 2.0,
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
guns,
|
||||
|
||||
collision: Collision {
|
||||
indices: (0..ship.collision.len())
|
||||
|
|
|
@ -4,14 +4,10 @@
|
|||
//! of every ship in the game, but it has no understanding of physics.
|
||||
//! That is done in `galactica_world`.
|
||||
|
||||
mod outfits;
|
||||
mod projectile;
|
||||
mod ship;
|
||||
mod system;
|
||||
mod systemobject;
|
||||
|
||||
pub use outfits::{OutfitSet, OutfitStatSum, ShipGun};
|
||||
pub use projectile::Projectile;
|
||||
pub use ship::Ship;
|
||||
pub use system::System;
|
||||
pub use systemobject::SystemObject;
|
||||
pub use system::{System, SystemObject};
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
use content::{EnginePoint, OutfitHandle, SpriteHandle};
|
||||
use galactica_content as content;
|
||||
|
||||
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
||||
#[derive(Debug)]
|
||||
pub struct ShipGun {
|
||||
/// The kind of gun this is
|
||||
pub kind: content::Gun,
|
||||
|
||||
/// How many seconds we must wait before this gun can fire
|
||||
pub cooldown: f32,
|
||||
|
||||
/// Index of the gunpoint this gun is attached to
|
||||
pub point: usize,
|
||||
}
|
||||
|
||||
impl ShipGun {
|
||||
/// Make a new shipgun
|
||||
pub fn new(kind: &content::Gun, point: usize) -> Self {
|
||||
Self {
|
||||
kind: kind.clone(),
|
||||
point,
|
||||
cooldown: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct keeps track of the combined stats of a set of outfits.
|
||||
/// It does NOT check for sanity (e.g, removing an outfit that was never added)
|
||||
/// That is handled by ShipOutfits.
|
||||
#[derive(Debug)]
|
||||
pub struct OutfitStatSum {
|
||||
pub engine_thrust: f32,
|
||||
pub steer_power: f32,
|
||||
pub engine_flare_sprites: Vec<content::SpriteHandle>,
|
||||
pub shield_strength: f32,
|
||||
|
||||
// Delay, generation
|
||||
pub shield_generators: Vec<(OutfitHandle, f32, f32)>,
|
||||
}
|
||||
|
||||
impl OutfitStatSum {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
engine_thrust: 0.0,
|
||||
steer_power: 0.0,
|
||||
engine_flare_sprites: Vec::new(),
|
||||
shield_strength: 0.0,
|
||||
shield_generators: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, o: &content::Outfit) {
|
||||
self.engine_thrust += o.engine_thrust;
|
||||
if let Some(t) = o.engine_flare_sprite {
|
||||
self.engine_flare_sprites.push(t);
|
||||
};
|
||||
self.steer_power += o.steer_power;
|
||||
self.shield_strength += o.shield_strength;
|
||||
self.shield_generators
|
||||
.push((o.handle, o.shield_delay, o.shield_generation));
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, o: &content::Outfit) {
|
||||
self.engine_thrust -= o.engine_thrust;
|
||||
if let Some(t) = o.engine_flare_sprite {
|
||||
self.engine_flare_sprites.remove(
|
||||
self.engine_flare_sprites
|
||||
.iter()
|
||||
.position(|x| *x == t)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
self.steer_power -= o.steer_power;
|
||||
self.shield_strength -= o.shield_strength;
|
||||
{
|
||||
// Assume such an index exists
|
||||
let index = self
|
||||
.shield_generators
|
||||
.iter()
|
||||
.position(|(h, _, _)| *h == o.handle)
|
||||
.unwrap();
|
||||
self.shield_generators.remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a set of 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 OutfitSet {
|
||||
/// The total sum of the stats this set of outfits provides
|
||||
stats: OutfitStatSum,
|
||||
|
||||
//pub total_space: content::OutfitSpace,
|
||||
available_space: content::OutfitSpace,
|
||||
outfits: Vec<content::OutfitHandle>,
|
||||
guns: Vec<ShipGun>,
|
||||
enginepoints: Vec<content::EnginePoint>,
|
||||
gunpoints: Vec<content::GunPoint>,
|
||||
}
|
||||
|
||||
impl<'a> OutfitSet {
|
||||
/// Make a new outfit array
|
||||
pub fn new(content: &content::Ship) -> Self {
|
||||
Self {
|
||||
stats: OutfitStatSum::new(),
|
||||
outfits: Vec::new(),
|
||||
guns: Vec::new(),
|
||||
available_space: content.space.clone(),
|
||||
//total_space: content.space.clone(),
|
||||
enginepoints: content.engines.clone(),
|
||||
gunpoints: content.guns.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Does this outfit set contain the specified outfit?
|
||||
pub fn contains_outfit(&self, o: content::OutfitHandle) -> bool {
|
||||
match self.outfits.iter().position(|x| *x == o) {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stat_sum(&self) -> &OutfitStatSum {
|
||||
&self.stats
|
||||
}
|
||||
|
||||
/// Add an outfit to this ship.
|
||||
/// Returns true on success, and false on failure
|
||||
/// TODO: failure reason enum
|
||||
pub fn add(&mut self, ct: &content::Content, o: content::OutfitHandle) -> bool {
|
||||
let outfit = ct.get_outfit(o);
|
||||
if !self.available_space.can_contain(&outfit.space) {
|
||||
return false;
|
||||
}
|
||||
self.available_space.occupy(&outfit.space);
|
||||
self.stats.add(&outfit);
|
||||
self.outfits.push(o);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Remove an outfit from this set
|
||||
pub fn remove(&mut self, ct: &content::Content, o: content::OutfitHandle) {
|
||||
let outfit = ct.get_outfit(o);
|
||||
let i = match self.outfits.iter().position(|x| *x == o) {
|
||||
Some(i) => i,
|
||||
None => panic!("removed non-existing outfit"),
|
||||
};
|
||||
self.available_space.free(&outfit.space);
|
||||
self.outfits.remove(i);
|
||||
}
|
||||
|
||||
/// 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, ct: &content::Content, g: content::GunHandle) -> 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;
|
||||
}
|
||||
let gun = ct.get_gun(g);
|
||||
|
||||
// All points are taken
|
||||
if p.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let sg = ShipGun::new(gun, p.unwrap());
|
||||
self.guns.push(sg);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Iterate over all guns in this outfitset
|
||||
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
|
||||
self.guns.iter_mut()
|
||||
}
|
||||
|
||||
/// Iterate over all guns and the gunpoints they're attached to
|
||||
pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &content::GunPoint)> {
|
||||
self.guns
|
||||
.iter_mut()
|
||||
.map(|x| (&self.gunpoints[x.point], x))
|
||||
.map(|(a, b)| (b, a))
|
||||
}
|
||||
|
||||
// TODO: move to ship
|
||||
/// Iterate over all ships in this physics system
|
||||
pub fn iter_enginepoints(&self) -> impl Iterator<Item = &EnginePoint> + '_ {
|
||||
self.enginepoints.iter()
|
||||
}
|
||||
|
||||
pub fn get_flare_sprite(&self) -> Option<SpriteHandle> {
|
||||
self.stats.engine_flare_sprites.iter().next().map(|x| *x)
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
use rand::Rng;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::{OutfitSet, Projectile};
|
||||
use galactica_content as content;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ship {
|
||||
pub handle: content::ShipHandle,
|
||||
pub faction: content::FactionHandle,
|
||||
pub outfits: OutfitSet,
|
||||
pub last_hit: Instant,
|
||||
|
||||
// TODO: unified ship stats struct, like space
|
||||
// TODO: unified outfit stats struct: check
|
||||
pub hull: f32,
|
||||
pub shields: f32,
|
||||
}
|
||||
|
||||
impl Ship {
|
||||
pub fn new(
|
||||
ct: &content::Content,
|
||||
handle: content::ShipHandle,
|
||||
faction: content::FactionHandle,
|
||||
outfits: OutfitSet,
|
||||
) -> Self {
|
||||
let s = ct.get_ship(handle);
|
||||
let shields = outfits.stat_sum().shield_strength;
|
||||
Ship {
|
||||
handle: handle,
|
||||
faction,
|
||||
outfits,
|
||||
hull: s.hull,
|
||||
shields,
|
||||
last_hit: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dead(&self) -> bool {
|
||||
self.hull <= 0.0
|
||||
}
|
||||
|
||||
/// Called whenever a projectile hits this ship.
|
||||
/// Returns true if that projectile should be destroyed and false otherwise.
|
||||
pub fn handle_projectile_collision(&mut self, ct: &content::Content, p: &Projectile) -> bool {
|
||||
let f = ct.get_faction(self.faction);
|
||||
let r = f.relationships.get(&p.faction).unwrap();
|
||||
match r {
|
||||
content::Relationship::Hostile => {
|
||||
let mut d = p.content.damage;
|
||||
if self.is_dead() {
|
||||
return true;
|
||||
}
|
||||
if self.shields >= d {
|
||||
self.shields -= d
|
||||
} else {
|
||||
d -= self.shields;
|
||||
self.shields = 0.0;
|
||||
self.hull -= d;
|
||||
}
|
||||
self.last_hit = Instant::now();
|
||||
|
||||
return true;
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fire_guns(&mut self) -> Vec<(Projectile, content::GunPoint)> {
|
||||
if self.is_dead() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
self.outfits
|
||||
.iter_guns_points()
|
||||
.filter(|(g, _)| g.cooldown <= 0.0)
|
||||
.map(|(g, p)| {
|
||||
let mut rng = rand::thread_rng();
|
||||
g.cooldown = g.kind.rate + &rng.gen_range(-g.kind.rate_rng..=g.kind.rate_rng);
|
||||
|
||||
let lifetime = g.kind.projectile.lifetime
|
||||
+ rng.gen_range(
|
||||
-g.kind.projectile.lifetime_rng..=g.kind.projectile.lifetime_rng,
|
||||
);
|
||||
|
||||
(
|
||||
Projectile {
|
||||
content: g.kind.projectile.clone(),
|
||||
lifetime: 0f32.max(lifetime),
|
||||
faction: self.faction,
|
||||
},
|
||||
p.clone(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn step(&mut self, t: f32) {
|
||||
let time_since = self.last_hit.elapsed().as_secs_f32();
|
||||
if self.shields != self.outfits.stat_sum().shield_strength {
|
||||
for (_, d, g) in &self.outfits.stat_sum().shield_generators {
|
||||
if time_since >= *d {
|
||||
self.shields += g * t;
|
||||
if self.shields > self.outfits.stat_sum().shield_strength {
|
||||
self.shields = self.outfits.stat_sum().shield_strength;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
mod outfitset;
|
||||
mod ship;
|
||||
|
||||
pub use outfitset::*;
|
||||
pub use ship::*;
|
|
@ -0,0 +1,187 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use content::{GunPoint, OutfitHandle, OutfitSpace};
|
||||
use galactica_content as content;
|
||||
|
||||
/// Possible outcomes when adding an outfit
|
||||
pub enum OutfitAddResult {
|
||||
/// An outfit was successfully added
|
||||
Ok,
|
||||
|
||||
/// An outfit could not be added because we don't have enough free space.
|
||||
/// The string tells us what kind of space we need:
|
||||
/// `outfit,` `weapon,` `engine,` etc. Note that these sometimes overlap:
|
||||
/// outfits may need outfit AND weapon space. In these cases, this result
|
||||
/// should name the "most specific" kind of space we lack.
|
||||
NotEnoughSpace(String),
|
||||
}
|
||||
|
||||
/// Possible outcomes when removing an outfit
|
||||
pub enum OutfitRemoveResult {
|
||||
/// This outfit was successfully removed
|
||||
Ok,
|
||||
|
||||
/// This outfit isn't in this set
|
||||
NotExist,
|
||||
// TODO:
|
||||
// This is where we'll add non-removable outfits,
|
||||
// outfits that provide space, etc
|
||||
}
|
||||
|
||||
/// A simple data class, used to keep track of delayed shield generators
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ShieldGenerator {
|
||||
pub outfit: OutfitHandle,
|
||||
pub delay: f32,
|
||||
pub generation: f32,
|
||||
}
|
||||
|
||||
/// This struct keeps track of a ship's outfit loadout.
|
||||
#[derive(Debug)]
|
||||
pub struct OutfitSet {
|
||||
/// What outfits does this statsum contain?
|
||||
outfits: HashMap<OutfitHandle, u32>,
|
||||
|
||||
/// Space available in this outfitset.
|
||||
/// set at creation and never changes.
|
||||
total_space: OutfitSpace,
|
||||
|
||||
/// Space used by the outfits in this set.
|
||||
/// This may be negative if certain outfits provide space!
|
||||
used_space: OutfitSpace,
|
||||
|
||||
/// The gun points available in this ship.
|
||||
/// If value is None, this point is free.
|
||||
/// if value is Some, this point is taken.
|
||||
gun_points: HashMap<GunPoint, Option<OutfitHandle>>,
|
||||
|
||||
// Outfit values
|
||||
// This isn't strictly necessary, but we don't want to
|
||||
// re-compute this on each frame.
|
||||
engine_thrust: f32,
|
||||
steer_power: f32,
|
||||
shield_strength: f32,
|
||||
|
||||
// Delay, generation
|
||||
// TODO: struct
|
||||
shield_generators: Vec<ShieldGenerator>,
|
||||
}
|
||||
|
||||
impl OutfitSet {
|
||||
pub fn new(available_space: OutfitSpace, gun_points: &[GunPoint]) -> Self {
|
||||
Self {
|
||||
outfits: HashMap::new(),
|
||||
total_space: available_space,
|
||||
used_space: OutfitSpace::new(),
|
||||
gun_points: gun_points.iter().map(|x| (x.clone(), None)).collect(),
|
||||
|
||||
engine_thrust: 0.0,
|
||||
steer_power: 0.0,
|
||||
shield_strength: 0.0,
|
||||
shield_generators: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, o: &content::Outfit) -> OutfitAddResult {
|
||||
if !(self.total_space - self.used_space).can_contain(&o.space) {
|
||||
return OutfitAddResult::NotEnoughSpace("TODO".to_string());
|
||||
}
|
||||
|
||||
self.used_space += o.space;
|
||||
|
||||
self.engine_thrust += o.engine_thrust;
|
||||
self.steer_power += o.steer_power;
|
||||
self.shield_strength += o.shield_strength;
|
||||
self.shield_generators.push(ShieldGenerator {
|
||||
outfit: o.handle,
|
||||
delay: o.shield_delay,
|
||||
generation: o.shield_generation,
|
||||
});
|
||||
|
||||
return OutfitAddResult::Ok;
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, o: &content::Outfit) -> OutfitRemoveResult {
|
||||
if !self.outfits.contains_key(&o.handle) {
|
||||
return OutfitRemoveResult::NotExist;
|
||||
} else {
|
||||
let n = *self.outfits.get(&o.handle).unwrap();
|
||||
if n == 1u32 {
|
||||
self.outfits.remove(&o.handle);
|
||||
} else {
|
||||
*self.outfits.get_mut(&o.handle).unwrap() -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
self.used_space -= o.space;
|
||||
|
||||
self.engine_thrust -= o.engine_thrust;
|
||||
self.steer_power -= o.steer_power;
|
||||
self.shield_strength -= o.shield_strength;
|
||||
|
||||
{
|
||||
// This index will exist, since we checked the hashmap
|
||||
let index = self
|
||||
.shield_generators
|
||||
.iter()
|
||||
.position(|g| g.outfit == o.handle)
|
||||
.unwrap();
|
||||
self.shield_generators.remove(index);
|
||||
}
|
||||
|
||||
return OutfitRemoveResult::Ok;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple getters to make sure nobody meddles with our internal state
|
||||
impl OutfitSet {
|
||||
/// The number of outfits in this set
|
||||
pub fn len(&self) -> u32 {
|
||||
self.outfits.iter().map(|(_, x)| x).sum()
|
||||
}
|
||||
|
||||
/// Iterate over all outfits
|
||||
pub fn iter_outfits(&self) -> impl Iterator<Item = (&OutfitHandle, &u32)> {
|
||||
self.outfits.iter()
|
||||
}
|
||||
|
||||
/// Iterate over all gun points
|
||||
pub fn iter_gun_points(&self) -> impl Iterator<Item = (&GunPoint, &Option<OutfitHandle>)> {
|
||||
self.gun_points.iter()
|
||||
}
|
||||
|
||||
/// Iterate over all shield generators
|
||||
pub(crate) fn iter_shield_generators(&self) -> impl Iterator<Item = &ShieldGenerator> {
|
||||
self.shield_generators.iter()
|
||||
}
|
||||
|
||||
/// Get maximum possible shield regen
|
||||
pub fn get_max_shield_regen(&self) -> f32 {
|
||||
self.shield_generators.iter().map(|x| x.generation).sum()
|
||||
}
|
||||
|
||||
/// Total available outfit space
|
||||
pub fn get_total_space(&self) -> &OutfitSpace {
|
||||
&self.total_space
|
||||
}
|
||||
|
||||
/// Used outfit space
|
||||
pub fn get_used_space(&self) -> &OutfitSpace {
|
||||
&self.used_space
|
||||
}
|
||||
|
||||
/// Total foward thrust
|
||||
pub fn get_engine_thrust(&self) -> f32 {
|
||||
self.engine_thrust
|
||||
}
|
||||
|
||||
/// Total steer power
|
||||
pub fn get_steer_power(&self) -> f32 {
|
||||
self.steer_power
|
||||
}
|
||||
|
||||
/// Total shield strength
|
||||
pub fn get_shield_strength(&self) -> f32 {
|
||||
self.shield_strength
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use super::OutfitSet;
|
||||
use galactica_content as content;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ship {
|
||||
// Metadata values
|
||||
pub ct_handle: content::ShipHandle,
|
||||
pub faction: content::FactionHandle,
|
||||
pub outfits: OutfitSet,
|
||||
|
||||
// State values
|
||||
// TODO: unified ship stats struct, like space
|
||||
pub hull: f32,
|
||||
pub shields: f32,
|
||||
|
||||
// Utility values
|
||||
/// The last time this ship was damaged
|
||||
pub last_hit: Instant,
|
||||
}
|
||||
|
||||
impl Ship {
|
||||
pub fn new(
|
||||
ct: &content::Content,
|
||||
ct_handle: content::ShipHandle,
|
||||
faction: content::FactionHandle,
|
||||
outfits: OutfitSet,
|
||||
) -> Self {
|
||||
let s = ct.get_ship(ct_handle);
|
||||
let shields = outfits.get_shield_strength();
|
||||
Ship {
|
||||
ct_handle,
|
||||
faction,
|
||||
outfits,
|
||||
last_hit: Instant::now(),
|
||||
|
||||
// Initial stats
|
||||
hull: s.hull,
|
||||
shields,
|
||||
}
|
||||
}
|
||||
|
||||
/// If this ship is dead, it will be removed from the game.
|
||||
pub fn is_dead(&self) -> bool {
|
||||
self.hull <= 0.0
|
||||
}
|
||||
|
||||
/// Hit this ship with the given amount of damage
|
||||
pub fn apply_damage(&mut self, mut d: f32) {
|
||||
if self.is_dead() {
|
||||
return;
|
||||
}
|
||||
if self.shields >= d {
|
||||
self.shields -= d
|
||||
} else {
|
||||
d -= self.shields;
|
||||
self.shields = 0.0;
|
||||
self.hull = 0f32.max(self.hull - d);
|
||||
}
|
||||
self.last_hit = Instant::now();
|
||||
}
|
||||
|
||||
/// Update this ship's state by `t` seconds
|
||||
pub fn step(&mut self, t: f32) {
|
||||
let time_since = self.last_hit.elapsed().as_secs_f32();
|
||||
if self.shields != self.outfits.get_shield_strength() {
|
||||
for g in self.outfits.iter_shield_generators() {
|
||||
if time_since >= g.delay {
|
||||
self.shields += g.generation * t;
|
||||
if self.shields > self.outfits.get_shield_strength() {
|
||||
self.shields = self.outfits.get_shield_strength();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
use crate::SystemObject;
|
||||
use cgmath::{Point3, Rad};
|
||||
use galactica_content as content;
|
||||
|
||||
// TODO: rework
|
||||
|
||||
pub struct System {
|
||||
pub name: String,
|
||||
pub bodies: Vec<SystemObject>,
|
||||
|
@ -25,8 +27,11 @@ impl System {
|
|||
|
||||
return s;
|
||||
}
|
||||
|
||||
//pub fn get_sprites(&self) -> Vec<ObjectSprite> {
|
||||
// return self.bodies.iter().map(|x| x.get_sprite()).collect();
|
||||
//}
|
||||
}
|
||||
|
||||
pub struct SystemObject {
|
||||
pub sprite: content::SpriteHandle,
|
||||
pub pos: Point3<f32>,
|
||||
pub size: f32,
|
||||
pub angle: Rad<f32>,
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
use cgmath::{Point3, Rad};
|
||||
|
||||
use galactica_content as content;
|
||||
|
||||
pub struct SystemObject {
|
||||
pub sprite: content::SpriteHandle,
|
||||
pub pos: Point3<f32>,
|
||||
pub size: f32,
|
||||
pub angle: Rad<f32>,
|
||||
}
|
Loading…
Reference in New Issue