From 2c4db4ebc5df0e5ddb1cb7d4757c1f0ae1ce5456 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 30 Dec 2023 17:39:19 -0800 Subject: [PATCH] Added faction relationships --- crates/content/src/handle.rs | 8 +++- crates/content/src/lib.rs | 4 +- crates/content/src/part/faction.rs | 24 ++++++---- crates/content/src/part/mod.rs | 2 +- src/game/game.rs | 8 ++-- src/main.rs | 2 + src/physics/physics.rs | 28 ++++++++--- src/shipbehavior/mod.rs | 74 ++++++++++++++++++++++++------ 8 files changed, 115 insertions(+), 35 deletions(-) diff --git a/crates/content/src/handle.rs b/crates/content/src/handle.rs index ddea731..054e417 100644 --- a/crates/content/src/handle.rs +++ b/crates/content/src/handle.rs @@ -24,9 +24,15 @@ impl PartialEq for TextureHandle { } /// A lightweight representation of a faction -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct FactionHandle { /// The index of this faction in content.factions /// TODO: pub in crate, currently for debug. pub index: usize, } + +impl Hash for FactionHandle { + fn hash(&self, state: &mut H) { + self.index.hash(state) + } +} diff --git a/crates/content/src/lib.rs b/crates/content/src/lib.rs index bb5b218..7d92d4c 100644 --- a/crates/content/src/lib.rs +++ b/crates/content/src/lib.rs @@ -18,7 +18,7 @@ use toml; use walkdir::WalkDir; pub use handle::{FactionHandle, TextureHandle}; -pub use part::{Engine, EnginePoint, Faction, Gun, GunPoint, Ship, System, Texture}; +pub use part::{Engine, EnginePoint, Faction, Gun, GunPoint, Relationship, Ship, System, Texture}; mod syntax { use anyhow::{bail, Result}; @@ -156,7 +156,7 @@ trait Build { Self: Sized; } -/// Represents generic game content, not connected to any game objects. +/// Represents static game content #[derive(Debug)] pub struct Content { /// Star systems diff --git a/crates/content/src/part/faction.rs b/crates/content/src/part/faction.rs index 261298b..f4b728a 100644 --- a/crates/content/src/part/faction.rs +++ b/crates/content/src/part/faction.rs @@ -18,16 +18,25 @@ pub(crate) mod syntax { } } +/// How two factions should interact with each other. +/// Relationships are directional: the relationship of +/// `a` to `b` may not equal the relationship of `b` to `a`. +/// +/// Relationships dictate how a ship of THIS faction +/// will interact with a ship of the OTHER faction. #[derive(Debug, Deserialize, Clone, Copy)] pub enum Relationship { + /// Attack this faction #[serde(rename = "hostile")] Hostile, + /// Ignore this faction #[serde(rename = "neutral")] Neutral, - #[serde(rename = "enemy")] - Enemy, + /// Protect this faction + #[serde(rename = "friend")] + Friend, } /// Represents a game faction @@ -39,6 +48,8 @@ pub struct Faction { /// This faction's handle pub handle: FactionHandle, + /// Relationships between this faction and other factions + /// This is guaranteed to contain an entry for ALL factions. pub relationships: HashMap, } @@ -50,6 +61,9 @@ impl crate::Build for Faction { // This lets us build FactionHandles before finishing all factions. let faction_names: Vec = factions.keys().map(|x| x.to_owned()).collect(); + // Indexing will break if this is false. + assert!(ct.factions.len() == 0); + for f_idx in 0..faction_names.len() { let faction_name = &faction_names[f_idx]; let faction = &factions[faction_name]; @@ -80,12 +94,6 @@ impl crate::Build for Faction { } } - let relationships = faction - .relationship - .iter() - .map(|x| (h, x.1.clone())) - .collect(); - ct.factions.push(Self { name: faction_name.to_owned(), handle: h, diff --git a/crates/content/src/part/mod.rs b/crates/content/src/part/mod.rs index 8c9716d..ccdff82 100644 --- a/crates/content/src/part/mod.rs +++ b/crates/content/src/part/mod.rs @@ -8,7 +8,7 @@ pub mod system; pub mod texture; pub use engine::Engine; -pub use faction::Faction; +pub use faction::{Faction, Relationship}; pub use gun::{Gun, Projectile}; pub use ship::{EnginePoint, GunPoint, Ship}; pub use system::{Object, System}; diff --git a/src/game/game.rs b/src/game/game.rs index ed1caeb..ef6caa2 100644 --- a/src/game/game.rs +++ b/src/game/game.rs @@ -24,6 +24,7 @@ pub struct Game { physics: Physics, shipbehaviors: Vec>, + content: Content, } impl Game { @@ -62,8 +63,8 @@ impl Game { let mut shipbehaviors: Vec> = Vec::new(); shipbehaviors.push(shipbehavior::Player::new(h1)); - shipbehaviors.push(shipbehavior::Point::new(h3)); shipbehaviors.push(shipbehavior::Dummy::new(h2)); + shipbehaviors.push(shipbehavior::Point::new(h3)); Game { last_update: Instant::now(), @@ -80,6 +81,7 @@ impl Game { time_scale: 1.0, physics, shipbehaviors, + content: ct, } } @@ -111,12 +113,12 @@ impl Game { if self.physics.get_ship_mut(&b.get_handle()).is_none() { false } else { - b.update_controls(&mut self.physics, &self.input, self.player); + b.update_controls(&mut self.physics, &self.input, &self.content); true } }); - self.physics.step(t); + self.physics.step(t, &self.content); if self.input.v_scroll != 0.0 { self.camera.zoom = diff --git a/src/main.rs b/src/main.rs index 7717fb5..0dea64e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,8 @@ fn main() -> Result<()> { consts::STARFIELD_TEXTURE_NAME.to_owned(), )?; + println!("{:?}", content.factions); + pollster::block_on(run(content))?; return Ok(()); } diff --git a/src/physics/physics.rs b/src/physics/physics.rs index 8c9213e..005422c 100644 --- a/src/physics/physics.rs +++ b/src/physics/physics.rs @@ -1,5 +1,5 @@ use cgmath::Point2; -use content::FactionHandle; +use content::{Content, FactionHandle}; use crossbeam::channel::Receiver; use nalgebra::vector; use rapier2d::{ @@ -112,7 +112,7 @@ impl Physics { return h; } - pub fn step(&mut self, t: f32) { + pub fn step(&mut self, t: f32, ct: &Content) { // Run ship updates let mut res = Vec::new(); let mut to_remove = Vec::new(); @@ -152,11 +152,16 @@ impl Physics { if let Some(p) = self.projectiles.get(a) { if let Some(s) = self.ships.get_mut(b) { - // TODO: better rules here - if s.faction != p.faction { - s.hull -= p.damage; - self.remove_projectile(*a); - self.remove_projectile(*b); + let p_faction = ct.get_faction(p.faction); + let r = p_faction.relationships[&s.faction]; + match r { + content::Relationship::Hostile => { + // TODO: implement death and spawning, and enable damage + //s.hull -= p.damage; + self.remove_projectile(*a); + self.remove_projectile(*b); + } + _ => {} } } } @@ -189,6 +194,15 @@ impl Physics { Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?)) } + pub fn iter_ship_body(&self) -> impl Iterator + '_ { + self.ships.values().map(|x| { + ( + x, + self.wrapper.rigid_body_set.get(x.physics_handle.0).unwrap(), + ) + }) + } + pub fn get_ship_sprites(&self) -> impl Iterator + '_ { self.ships .values() diff --git a/src/shipbehavior/mod.rs b/src/shipbehavior/mod.rs index f120dde..4048348 100644 --- a/src/shipbehavior/mod.rs +++ b/src/shipbehavior/mod.rs @@ -1,4 +1,5 @@ use cgmath::{Deg, InnerSpace}; +use galactica_content as content; use crate::{ inputstatus::InputStatus, @@ -9,7 +10,12 @@ pub trait ShipBehavior where Self: Send, { - fn update_controls(&mut self, physics: &mut Physics, input: &InputStatus, player: ShipHandle); + fn update_controls( + &mut self, + physics: &mut Physics, + input: &InputStatus, + content: &content::Content, + ); fn get_handle(&self) -> ShipHandle; } @@ -28,7 +34,7 @@ impl ShipBehavior for Dummy { &mut self, _physics: &mut Physics, _input: &InputStatus, - _player: ShipHandle, + _content: &content::Content, ) { } fn get_handle(&self) -> ShipHandle { @@ -47,7 +53,12 @@ impl Player { } impl ShipBehavior for Player { - fn update_controls(&mut self, physics: &mut Physics, input: &InputStatus, _player: ShipHandle) { + fn update_controls( + &mut self, + physics: &mut Physics, + input: &InputStatus, + _content: &content::Content, + ) { let s = physics.get_ship_mut(&self.handle).unwrap(); s.controls.left = input.key_left; s.controls.right = input.key_right; @@ -71,23 +82,60 @@ impl Point { } impl ShipBehavior for Point { - fn update_controls(&mut self, physics: &mut Physics, _input: &InputStatus, player: ShipHandle) { - let (_, r) = physics.get_ship_body(&player).unwrap(); - let p = util::rigidbody_position(r); + fn update_controls( + &mut self, + physics: &mut Physics, + _input: &InputStatus, + content: &content::Content, + ) { + // Turn off all controls + let s = physics.get_ship_mut(&self.handle).unwrap(); + s.controls.left = false; + s.controls.right = false; + s.controls.guns = false; + s.controls.thrust = false; - let (_, r) = physics.get_ship_body(&self.handle).unwrap(); - let t = util::rigidbody_position(r); - let pa = util::rigidbody_rotation(r); - let v = r.angvel(); - let d: Deg = (pa).angle(p - t).into(); + let (my_s, my_r) = physics.get_ship_body(&self.handle).unwrap(); + let my_position = util::rigidbody_position(my_r); + let my_rotation = util::rigidbody_rotation(my_r); + let my_angvel = my_r.angvel(); + let my_faction = content.get_faction(my_s.faction); + + // Iterate all possible targets + let mut it = physics + .iter_ship_body() + .filter(|(s, _)| match my_faction.relationships[&s.faction] { + content::Relationship::Hostile => true, + _ => false, + }) + .map(|(_, r)| r); + + // Find the closest target + let mut closest_enemy_position = match it.next() { + Some(c) => util::rigidbody_position(c), + None => return, // Do nothing if no targets are available + }; + let mut d = (my_position - closest_enemy_position).magnitude(); + for r in it { + let p = util::rigidbody_position(r); + let new_d = (my_position - p).magnitude(); + if new_d < d { + d = new_d; + closest_enemy_position = p; + } + } + + let angle_delta: Deg = (my_rotation) + .angle(closest_enemy_position - my_position) + .into(); let s = physics.get_ship_mut(&self.handle).unwrap(); s.controls.left = false; s.controls.right = false; - if d < Deg(0.0) && v > -0.3 { + if angle_delta < Deg(0.0) && my_angvel > -0.3 { s.controls.right = true; - } else if d > Deg(0.0) && v < 0.3 { + } else if angle_delta > Deg(0.0) && my_angvel < 0.3 { s.controls.left = true; }