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
#[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<H: std::hash::Hasher>(&self, state: &mut H) {
self.index.hash(state)
}
}

View File

@ -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

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)]
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<FactionHandle, Relationship>,
}
@ -50,6 +61,9 @@ impl crate::Build for Faction {
// This lets us build FactionHandles before finishing all factions.
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() {
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,

View File

@ -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};

View File

@ -24,6 +24,7 @@ pub struct Game {
physics: Physics,
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
content: Content,
}
impl Game {
@ -62,8 +63,8 @@ impl Game {
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = 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 =

View File

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

View File

@ -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<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> + '_ {
self.ships
.values()

View File

@ -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<f32> = (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<f32> = (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;
}