Added faction relationships
parent
cbe8054f45
commit
2c4db4ebc5
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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(());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue