diff --git a/crates/render/src/ui/api/state.rs b/crates/render/src/ui/api/state.rs index 829a8bf..93b2f67 100644 --- a/crates/render/src/ui/api/state.rs +++ b/crates/render/src/ui/api/state.rs @@ -5,6 +5,7 @@ use galactica_system::{ }; use galactica_util::to_degrees; use log::error; +use nalgebra::Vector2; use rapier2d::dynamics::RigidBody; use rhai::{Array, CustomType, Dynamic, ImmutableString, TypeBuilder}; use std::sync::Arc; @@ -89,18 +90,16 @@ impl CustomType for ShipState { .with_fn("content_index", |s: &mut Self| { s.get_content().display_name.clone() }) - .with_fn("thumbnail", |s: &mut Self| { - s.get_content().thumbnail.clone() + .with_fn("thumbnail", |s: &mut Self| -> ImmutableString { + s.get_content().thumbnail.index.clone().into() }) .with_fn("landed_on", |s: &mut Self| s.landed_on()) - .with_fn("get_shields", |s: &mut Self| { + .with_fn("current_shields", |s: &mut Self| { s.get_ship().get_data().get_shields() }) - .with_fn("get_total_shields", |s: &mut Self| { - s.get_ship().get_data().get_outfits().get_total_shields() - }) - .with_fn("get_total_hull", |s: &mut Self| s.get_content().hull) - .with_fn("get_hull", |s: &mut Self| { + .with_fn("total_hull", |s: &mut Self| s.get_content().hull) + .with_fn("empty_mass", |s: &mut Self| s.get_content().mass) + .with_fn("current_hull", |s: &mut Self| { s.get_ship().get_data().get_hull() }) .with_fn("get_size", |s: &mut Self| s.get_content().size) @@ -113,25 +112,132 @@ impl CustomType for ShipState { let h = s.get_ship().get_data().get_faction(); let c = h.color; Color::new(c[0], c[1], c[2], 1.0) + }) + // + // Stat getters + // + .with_fn("stat_thrust", |s: &mut Self| { + s.get_ship() + .get_data() + .get_outfits() + .get_stats() + .engine_thrust + }) + .with_fn("stat_steer_power", |s: &mut Self| { + s.get_ship() + .get_data() + .get_outfits() + .get_stats() + .steer_power + }) + .with_fn("stat_shield_strength", |s: &mut Self| { + s.get_ship() + .get_data() + .get_outfits() + .get_stats() + .shield_strength + }) + .with_fn("stat_max_shield_generation", |s: &mut Self| { + s.get_ship() + .get_data() + .get_outfits() + .get_stats() + .shield_generation }); } } #[derive(Debug, Clone)] pub struct OutfitState { - outfit: Arc, + outfit: Option>, } -impl OutfitState {} +impl OutfitState { + pub fn new(outfit: Arc) -> Self { + Self { + outfit: Some(outfit), + } + } + + pub fn new_none() -> Self { + Self { outfit: None } + } +} impl CustomType for OutfitState { fn build(mut builder: TypeBuilder) { builder .with_name("OutfitState") - .with_fn("display_name", |s: &mut Self| s.outfit.display_name.clone()) - .with_fn("index", |s: &mut Self| s.outfit.index.to_string()) + .with_fn("is_some", |s: &mut Self| s.outfit.is_some()) + .with_fn("display_name", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.display_name.clone()) + .unwrap_or("".to_string()) + }) + .with_fn("desc", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.desc.clone()) + .unwrap_or("".to_string()) + }) + .with_fn("index", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.index.to_string()) + .unwrap_or("".to_string()) + }) .with_fn("thumbnail", |s: &mut Self| { - s.outfit.thumbnail.index.to_string() + s.outfit + .as_ref() + .map(|x| x.thumbnail.index.to_string()) + .unwrap_or("".to_string()) + }) + // + // Stat getters + // + .with_fn("stat_thrust", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.stats.steer_power) + .unwrap_or(0.0) + }) + .with_fn("stat_steer_power", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.stats.steer_power) + .unwrap_or(0.0) + }) + .with_fn("stat_shield_strength", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.stats.shield_strength) + .unwrap_or(0.0) + }) + .with_fn("stat_shield_generation", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.stats.shield_generation) + .unwrap_or(0.0) + }) + .with_fn("stat_shield_delay", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| x.stats.shield_delay) + .unwrap_or(0.0) + }) + .with_fn("stat_shield_dps", |s: &mut Self| { + s.outfit + .as_ref() + .map(|x| { + if x.gun.is_some() { + (1.0 / x.gun.as_ref().unwrap().rate) + * x.gun.as_ref().unwrap().projectile.damage + } else { + 0.0 + } + }) + .unwrap_or(0.0) }); } } @@ -153,7 +259,7 @@ impl SystemObjectState { .unwrap() .outfitter { - a.push(Dynamic::from(OutfitState { outfit: o.clone() })); + a.push(Dynamic::from(OutfitState::new(o.clone()))); } return a; } @@ -234,6 +340,7 @@ impl CustomType for SystemObjectState { pub struct State { input: Arc, window_aspect: f32, + window_size: Vector2, } // TODO: remove this @@ -245,6 +352,7 @@ impl State { Self { input: input.clone(), window_aspect: state.window_aspect, + window_size: Vector2::new(state.window_size.width, state.window_size.height), } } @@ -284,6 +392,12 @@ impl CustomType for State { .with_fn("player_ship", Self::player_ship) .with_fn("ships", Self::ships) .with_fn("objects", Self::objects) - .with_fn("window_aspect", |s: &mut Self| s.window_aspect); + .with_fn("window_aspect", |s: &mut Self| s.window_aspect) + .with_fn("window_size", |s: &mut Self| { + UiVector::new( + s.window_size.x as f32 / s.input.ct.config.ui_scale, + s.window_size.y as f32 / s.input.ct.config.ui_scale, + ) + }); } } diff --git a/crates/system/src/data/ship/outfitset.rs b/crates/system/src/data/ship/outfitset.rs index 503bb3d..e042841 100644 --- a/crates/system/src/data/ship/outfitset.rs +++ b/crates/system/src/data/ship/outfitset.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, sync::Arc}; -use galactica_content::{ContentIndex, GunPoint, Outfit, OutfitSpace}; +use galactica_content::{ContentIndex, GunPoint, Outfit, OutfitSpace, OutfitStats}; /// Possible outcomes when adding an outfit pub enum OutfitAddResult { @@ -61,12 +61,17 @@ pub struct OutfitSet { /// if value is Some, this point is taken. gun_points: HashMap>>, - /// 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, + /// The combined stats of all outfits in this set. + /// There are two things to note here: + /// First, shield_delay is always zero. That is handled + /// seperately, since it is different for every outfit. + /// Second, shield_generation represents the MAXIMUM POSSIBLE + /// shield generation, after all delays have expired. + /// + /// Note that this field isn't strictly necessary... we could compute stats + /// by iterating over the outfits in this set. We don't want to do this every + /// frame, though, so we keep track of the total sum here. + stats: OutfitStats, /// All shield generators in this outfit set // These can't be summed into one value, since each has a @@ -81,10 +86,7 @@ impl OutfitSet { 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, + stats: OutfitStats::zero(), shield_generators: Vec::new(), } } @@ -111,13 +113,12 @@ impl OutfitSet { self.used_space += o.space; - self.engine_thrust += o.engine_thrust; - self.steer_power += o.steer_power; - self.shield_strength += o.shield_strength; + self.stats.add(&o.stats); + self.shield_generators.push(ShieldGenerator { outfit: o.clone(), - delay: o.shield_delay, - generation: o.shield_generation, + delay: o.stats.shield_delay, + generation: o.stats.shield_generation, }); if self.outfits.contains_key(&o.index) { @@ -143,9 +144,7 @@ impl OutfitSet { self.used_space -= o.space; - self.engine_thrust -= o.engine_thrust; - self.steer_power -= o.steer_power; - self.shield_strength -= o.shield_strength; + self.stats.subtract(&o.stats); { // This index will exist, since we checked the hashmap @@ -183,11 +182,6 @@ impl OutfitSet { 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() - } - /// Get the outfit attached to the given gun point /// Will panic if this gunpoint is not in this outfit set. pub fn get_gun(&self, point: &GunPoint) -> Option> { @@ -204,18 +198,13 @@ impl OutfitSet { &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_total_shields(&self) -> f32 { - self.shield_strength + /// Get the combined stats of all outfits in this set. + /// There are two things to note here: + /// First, shield_delay is always zero. That is handled + /// seperately, since it is different for every outfit. + /// Second, shield_generation represents the MAXIMUM POSSIBLE + /// shield generation, after all delays have expired. + pub fn get_stats(&self) -> &OutfitStats { + &self.stats } } diff --git a/crates/system/src/data/ship/ship.rs b/crates/system/src/data/ship/ship.rs index 31fc37f..a30c0b3 100644 --- a/crates/system/src/data/ship/ship.rs +++ b/crates/system/src/data/ship/ship.rs @@ -178,7 +178,7 @@ impl ShipData { /// Add an outfit to this ship pub fn add_outfit(&mut self, o: &Arc) -> super::OutfitAddResult { let r = self.outfits.add(o); - self.shields = self.outfits.get_total_shields(); + self.shields = self.outfits.get_stats().shield_strength; return r; } @@ -245,8 +245,8 @@ impl ShipData { } // Regenerate shields - if self.shields != self.outfits.get_total_shields() { - self.shields = self.outfits.get_total_shields(); + if self.shields != self.outfits.get_stats().shield_strength { + self.shields = self.outfits.get_stats().shield_strength; } } @@ -260,12 +260,12 @@ impl ShipData { // Regenerate shields let time_since = self.last_hit.elapsed().as_secs_f32(); - if self.shields != self.outfits.get_total_shields() { + if self.shields != self.outfits.get_stats().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_total_shields() { - self.shields = self.outfits.get_total_shields(); + if self.shields > self.outfits.get_stats().shield_strength { + self.shields = self.outfits.get_stats().shield_strength; break; } } diff --git a/crates/system/src/phys/objects/ship/ship.rs b/crates/system/src/phys/objects/ship/ship.rs index 9bbcffa..d3d9ea6 100644 --- a/crates/system/src/phys/objects/ship/ship.rs +++ b/crates/system/src/phys/objects/ship/ship.rs @@ -390,21 +390,21 @@ impl PhysShip { if self.controls.thrust { rigid_body.apply_impulse( vector![engine_force.x, engine_force.y] - * self.data.get_outfits().get_engine_thrust(), + * self.data.get_outfits().get_stats().engine_thrust, true, ); } if self.controls.right { rigid_body.apply_torque_impulse( - self.data.get_outfits().get_steer_power() * -100.0 * res.t, + self.data.get_outfits().get_stats().steer_power * -100.0 * res.t, true, ); } if self.controls.left { rigid_body.apply_torque_impulse( - self.data.get_outfits().get_steer_power() * 100.0 * res.t, + self.data.get_outfits().get_stats().steer_power * 100.0 * res.t, true, ); }