Added faction relationships

master
Mark 2023-12-30 17:39:19 -08:00
parent cbe8054f45
commit 2c4db4ebc5
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
8 changed files with 115 additions and 35 deletions

View File

@ -24,9 +24,15 @@ impl PartialEq for TextureHandle {
} }
/// A lightweight representation of a faction /// A lightweight representation of a faction
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FactionHandle { pub struct FactionHandle {
/// The index of this faction in content.factions /// The index of this faction in content.factions
/// TODO: pub in crate, currently for debug. /// TODO: pub in crate, currently for debug.
pub index: usize, pub index: usize,
} }
impl Hash for FactionHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.index.hash(state)
}
}

View File

@ -18,7 +18,7 @@ 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, Ship, System, Texture}; pub use part::{Engine, EnginePoint, Faction, Gun, GunPoint, Relationship, Ship, System, Texture};
mod syntax { mod syntax {
use anyhow::{bail, Result}; use anyhow::{bail, Result};
@ -156,7 +156,7 @@ trait Build {
Self: Sized; Self: Sized;
} }
/// Represents generic game content, not connected to any game objects. /// Represents static game content
#[derive(Debug)] #[derive(Debug)]
pub struct Content { pub struct Content {
/// Star systems /// Star systems

View File

@ -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)] #[derive(Debug, Deserialize, Clone, Copy)]
pub enum Relationship { pub enum Relationship {
/// Attack this faction
#[serde(rename = "hostile")] #[serde(rename = "hostile")]
Hostile, Hostile,
/// Ignore this faction
#[serde(rename = "neutral")] #[serde(rename = "neutral")]
Neutral, Neutral,
#[serde(rename = "enemy")] /// Protect this faction
Enemy, #[serde(rename = "friend")]
Friend,
} }
/// Represents a game faction /// Represents a game faction
@ -39,6 +48,8 @@ pub struct Faction {
/// This faction's handle /// This faction's handle
pub handle: FactionHandle, pub handle: FactionHandle,
/// Relationships between this faction and other factions
/// This is guaranteed to contain an entry for ALL factions.
pub relationships: HashMap<FactionHandle, Relationship>, pub relationships: HashMap<FactionHandle, Relationship>,
} }
@ -50,6 +61,9 @@ impl crate::Build for Faction {
// This lets us build FactionHandles before finishing all factions. // This lets us build FactionHandles before finishing all factions.
let faction_names: Vec<String> = factions.keys().map(|x| x.to_owned()).collect(); let faction_names: Vec<String> = 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() { for f_idx in 0..faction_names.len() {
let faction_name = &faction_names[f_idx]; let faction_name = &faction_names[f_idx];
let faction = &factions[faction_name]; 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 { ct.factions.push(Self {
name: faction_name.to_owned(), name: faction_name.to_owned(),
handle: h, handle: h,

View File

@ -8,7 +8,7 @@ pub mod system;
pub mod texture; pub mod texture;
pub use engine::Engine; pub use engine::Engine;
pub use faction::Faction; pub use faction::{Faction, Relationship};
pub use gun::{Gun, Projectile}; pub use gun::{Gun, Projectile};
pub use ship::{EnginePoint, GunPoint, Ship}; pub use ship::{EnginePoint, GunPoint, Ship};
pub use system::{Object, System}; pub use system::{Object, System};

View File

@ -24,6 +24,7 @@ pub struct Game {
physics: Physics, physics: Physics,
shipbehaviors: Vec<Box<dyn ShipBehavior>>, shipbehaviors: Vec<Box<dyn ShipBehavior>>,
content: Content,
} }
impl Game { impl Game {
@ -62,8 +63,8 @@ impl Game {
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new(); let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
shipbehaviors.push(shipbehavior::Player::new(h1)); shipbehaviors.push(shipbehavior::Player::new(h1));
shipbehaviors.push(shipbehavior::Point::new(h3));
shipbehaviors.push(shipbehavior::Dummy::new(h2)); shipbehaviors.push(shipbehavior::Dummy::new(h2));
shipbehaviors.push(shipbehavior::Point::new(h3));
Game { Game {
last_update: Instant::now(), last_update: Instant::now(),
@ -80,6 +81,7 @@ impl Game {
time_scale: 1.0, time_scale: 1.0,
physics, physics,
shipbehaviors, shipbehaviors,
content: ct,
} }
} }
@ -111,12 +113,12 @@ impl Game {
if self.physics.get_ship_mut(&b.get_handle()).is_none() { if self.physics.get_ship_mut(&b.get_handle()).is_none() {
false false
} else { } else {
b.update_controls(&mut self.physics, &self.input, self.player); b.update_controls(&mut self.physics, &self.input, &self.content);
true true
} }
}); });
self.physics.step(t); self.physics.step(t, &self.content);
if self.input.v_scroll != 0.0 { if self.input.v_scroll != 0.0 {
self.camera.zoom = self.camera.zoom =

View File

@ -24,6 +24,8 @@ fn main() -> Result<()> {
consts::STARFIELD_TEXTURE_NAME.to_owned(), consts::STARFIELD_TEXTURE_NAME.to_owned(),
)?; )?;
println!("{:?}", content.factions);
pollster::block_on(run(content))?; pollster::block_on(run(content))?;
return Ok(()); return Ok(());
} }

View File

@ -1,5 +1,5 @@
use cgmath::Point2; use cgmath::Point2;
use content::FactionHandle; use content::{Content, FactionHandle};
use crossbeam::channel::Receiver; use crossbeam::channel::Receiver;
use nalgebra::vector; use nalgebra::vector;
use rapier2d::{ use rapier2d::{
@ -112,7 +112,7 @@ impl Physics {
return h; return h;
} }
pub fn step(&mut self, t: f32) { pub fn step(&mut self, t: f32, ct: &Content) {
// Run ship updates // Run ship updates
let mut res = Vec::new(); let mut res = Vec::new();
let mut to_remove = Vec::new(); let mut to_remove = Vec::new();
@ -152,12 +152,17 @@ impl Physics {
if let Some(p) = self.projectiles.get(a) { if let Some(p) = self.projectiles.get(a) {
if let Some(s) = self.ships.get_mut(b) { if let Some(s) = self.ships.get_mut(b) {
// TODO: better rules here let p_faction = ct.get_faction(p.faction);
if s.faction != p.faction { let r = p_faction.relationships[&s.faction];
s.hull -= p.damage; match r {
content::Relationship::Hostile => {
// TODO: implement death and spawning, and enable damage
//s.hull -= p.damage;
self.remove_projectile(*a); self.remove_projectile(*a);
self.remove_projectile(*b); self.remove_projectile(*b);
} }
_ => {}
}
} }
} }
} }
@ -189,6 +194,15 @@ impl Physics {
Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?)) Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?))
} }
pub fn iter_ship_body(&self) -> impl Iterator<Item = (&objects::Ship, &RigidBody)> + '_ {
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<Item = Sprite> + '_ { pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {
self.ships self.ships
.values() .values()

View File

@ -1,4 +1,5 @@
use cgmath::{Deg, InnerSpace}; use cgmath::{Deg, InnerSpace};
use galactica_content as content;
use crate::{ use crate::{
inputstatus::InputStatus, inputstatus::InputStatus,
@ -9,7 +10,12 @@ pub trait ShipBehavior
where where
Self: Send, 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; fn get_handle(&self) -> ShipHandle;
} }
@ -28,7 +34,7 @@ impl ShipBehavior for Dummy {
&mut self, &mut self,
_physics: &mut Physics, _physics: &mut Physics,
_input: &InputStatus, _input: &InputStatus,
_player: ShipHandle, _content: &content::Content,
) { ) {
} }
fn get_handle(&self) -> ShipHandle { fn get_handle(&self) -> ShipHandle {
@ -47,7 +53,12 @@ impl Player {
} }
impl ShipBehavior for 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(); let s = physics.get_ship_mut(&self.handle).unwrap();
s.controls.left = input.key_left; s.controls.left = input.key_left;
s.controls.right = input.key_right; s.controls.right = input.key_right;
@ -71,23 +82,60 @@ impl Point {
} }
impl ShipBehavior for Point { impl ShipBehavior for Point {
fn update_controls(&mut self, physics: &mut Physics, _input: &InputStatus, player: ShipHandle) { fn update_controls(
let (_, r) = physics.get_ship_body(&player).unwrap(); &mut self,
let p = util::rigidbody_position(r); 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 (my_s, my_r) = physics.get_ship_body(&self.handle).unwrap();
let t = util::rigidbody_position(r); let my_position = util::rigidbody_position(my_r);
let pa = util::rigidbody_rotation(r); let my_rotation = util::rigidbody_rotation(my_r);
let v = r.angvel(); let my_angvel = my_r.angvel();
let d: Deg<f32> = (pa).angle(p - t).into(); 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<f32> = (my_rotation)
.angle(closest_enemy_position - my_position)
.into();
let s = physics.get_ship_mut(&self.handle).unwrap(); let s = physics.get_ship_mut(&self.handle).unwrap();
s.controls.left = false; s.controls.left = false;
s.controls.right = 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; 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; s.controls.left = true;
} }