Repaired ship behaviors
parent
7854245a4b
commit
64885a8b6d
|
@ -45,23 +45,29 @@ impl Game {
|
|||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 1 }));
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 2 }));
|
||||
|
||||
gamedata.create_ship(
|
||||
let a = gamedata.create_ship(
|
||||
&ct,
|
||||
content::ShipHandle { index: 0 },
|
||||
content::FactionHandle { index: 1 },
|
||||
ShipPersonality::Dummy,
|
||||
&content::SystemHandle { index: 0 },
|
||||
);
|
||||
let s = gamedata.get_ship_mut(a).unwrap();
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 0 }));
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 1 }));
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 2 }));
|
||||
|
||||
let a = gamedata.create_ship(
|
||||
&ct,
|
||||
content::ShipHandle { index: 0 },
|
||||
content::FactionHandle { index: 1 },
|
||||
content::FactionHandle { index: 0 },
|
||||
ShipPersonality::Point,
|
||||
&content::SystemHandle { index: 0 },
|
||||
);
|
||||
let s = gamedata.get_ship_mut(a).unwrap();
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 0 }));
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 1 }));
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 2 }));
|
||||
|
||||
let physics = World::new(&ct, &gamedata, content::SystemHandle { index: 0 });
|
||||
|
||||
|
|
|
@ -56,7 +56,9 @@ impl Ship {
|
|||
|
||||
/// Add an outfit to this ship
|
||||
pub fn add_outfit(&mut self, o: &content::Outfit) -> super::OutfitAddResult {
|
||||
self.outfits.add(o)
|
||||
let r = self.outfits.add(o);
|
||||
self.shields = self.outfits.get_shield_strength();
|
||||
return r;
|
||||
}
|
||||
|
||||
/// Remove an outfit from this ship
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
//! Various implementations of [`crate::ShipBehavior`]
|
||||
|
||||
mod null;
|
||||
//mod point;
|
||||
mod point;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use galactica_gameobject::GameShipHandle;
|
||||
pub use null::*;
|
||||
//pub use point::Point;
|
||||
pub use point::Point;
|
||||
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
|
||||
|
||||
use crate::{objects::ShipControls, StepResources};
|
||||
use crate::{
|
||||
objects::{ShipControls, ShipWorldObject},
|
||||
StepResources,
|
||||
};
|
||||
|
||||
/// Main behavior trait. Any struct that implements this
|
||||
/// may be used to control a ship.
|
||||
|
@ -14,5 +21,12 @@ pub trait ShipBehavior {
|
|||
/// Update a ship's controls based on world state.
|
||||
/// This method does not return anything, it modifies
|
||||
/// the ship's controls in-place.
|
||||
fn update_controls(&mut self, res: &StepResources) -> ShipControls;
|
||||
fn update_controls(
|
||||
&mut self,
|
||||
res: &StepResources,
|
||||
rigid_bodies: &RigidBodySet,
|
||||
ships: &HashMap<GameShipHandle, ShipWorldObject>,
|
||||
this_ship: RigidBodyHandle,
|
||||
this_data: GameShipHandle,
|
||||
) -> ShipControls;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use galactica_gameobject::GameShipHandle;
|
||||
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
|
||||
|
||||
use super::ShipBehavior;
|
||||
use crate::{objects::ShipControls, StepResources};
|
||||
use crate::{
|
||||
objects::{ShipControls, ShipWorldObject},
|
||||
StepResources,
|
||||
};
|
||||
|
||||
/// The Null behaviors is assigned to objects that are not controlled by the computer.
|
||||
/// Most notably, the player's ship has a Null behavior.
|
||||
|
@ -13,7 +21,14 @@ impl Null {
|
|||
}
|
||||
|
||||
impl ShipBehavior for Null {
|
||||
fn update_controls(&mut self, _res: &StepResources) -> ShipControls {
|
||||
fn update_controls(
|
||||
&mut self,
|
||||
_res: &StepResources,
|
||||
_rigid_bodies: &RigidBodySet,
|
||||
_ships: &HashMap<GameShipHandle, ShipWorldObject>,
|
||||
_this_ship: RigidBodyHandle,
|
||||
_this_data: GameShipHandle,
|
||||
) -> ShipControls {
|
||||
ShipControls::new()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
use cgmath::{Deg, InnerSpace};
|
||||
use galactica_gameobject::GameData;
|
||||
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
objects::{ShipControls, ShipWorldObject},
|
||||
util, StepResources,
|
||||
};
|
||||
use galactica_content as content;
|
||||
|
||||
use crate::World;
|
||||
use galactica_gameobject::GameShipHandle;
|
||||
|
||||
use super::ShipBehavior;
|
||||
|
||||
|
@ -21,43 +25,47 @@ impl Point {
|
|||
impl ShipBehavior for Point {
|
||||
fn update_controls(
|
||||
&mut self,
|
||||
physics: &mut World,
|
||||
content: &content::Content,
|
||||
data: &GameData,
|
||||
) {
|
||||
// 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;
|
||||
res: &StepResources,
|
||||
rigid_bodies: &RigidBodySet,
|
||||
ships: &HashMap<GameShipHandle, ShipWorldObject>,
|
||||
this_ship: RigidBodyHandle,
|
||||
this_data: GameShipHandle,
|
||||
) -> ShipControls {
|
||||
let mut controls = ShipControls::new();
|
||||
|
||||
let (my_s, my_r) = physics.get_ship_body(self.handle).unwrap();
|
||||
let my_data = data.get_ship(my_s.data_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_data.get_faction());
|
||||
let this_rigidbody = rigid_bodies.get(this_ship).unwrap();
|
||||
let my_data = res.dt.get_ship(this_data).unwrap();
|
||||
let my_position = util::rigidbody_position(this_rigidbody);
|
||||
let my_rotation = util::rigidbody_rotation(this_rigidbody);
|
||||
let my_angvel = this_rigidbody.angvel();
|
||||
let my_faction = res.ct.get_faction(my_data.get_faction());
|
||||
|
||||
// Iterate all possible targets
|
||||
let mut it = physics
|
||||
.iter_ship_body()
|
||||
.filter(|(s, _)| {
|
||||
let data = data.get_ship(s.data_handle).unwrap();
|
||||
match my_faction.relationships.get(&data.get_faction()).unwrap() {
|
||||
content::Relationship::Hostile => true,
|
||||
_ => false,
|
||||
let mut hostile_ships = ships
|
||||
.values()
|
||||
.filter(|s| {
|
||||
let data = res.dt.get_ship(s.data_handle);
|
||||
if let Some(data) = data {
|
||||
match my_faction.relationships.get(&data.get_faction()).unwrap() {
|
||||
content::Relationship::Hostile => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
// This check is necessary---we don't want to target (or panic on)
|
||||
// ships that don't have data (and are thus playing their collapse animation)
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|(_, r)| r);
|
||||
.map(|s| rigid_bodies.get(s.rigid_body).unwrap());
|
||||
|
||||
// Find the closest target
|
||||
let mut closest_enemy_position = match it.next() {
|
||||
let mut closest_enemy_position = match hostile_ships.next() {
|
||||
Some(c) => util::rigidbody_position(c),
|
||||
None => return, // Do nothing if no targets are available
|
||||
None => return controls, // Do nothing if no targets are available
|
||||
};
|
||||
|
||||
let mut d = (my_position - closest_enemy_position).magnitude();
|
||||
for r in it {
|
||||
for r in hostile_ships {
|
||||
let p = util::rigidbody_position(r);
|
||||
let new_d = (my_position - p).magnitude();
|
||||
if new_d < d {
|
||||
|
@ -70,17 +78,13 @@ impl ShipBehavior for Point {
|
|||
.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 angle_delta < Deg(0.0) && my_angvel > -0.3 {
|
||||
s.controls.right = true;
|
||||
controls.right = true;
|
||||
} else if angle_delta > Deg(0.0) && my_angvel < 0.3 {
|
||||
s.controls.left = true;
|
||||
controls.left = true;
|
||||
}
|
||||
|
||||
s.controls.guns = true;
|
||||
s.controls.thrust = false;
|
||||
controls.guns = true;
|
||||
return controls;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
||||
use content::{FactionHandle, ShipHandle};
|
||||
use nalgebra::{point, vector};
|
||||
|
||||
use object::GameShipHandle;
|
||||
use rand::{rngs::ThreadRng, Rng};
|
||||
use rapier2d::{
|
||||
|
@ -9,7 +8,7 @@ use rapier2d::{
|
|||
geometry::{Collider, ColliderHandle},
|
||||
};
|
||||
|
||||
use crate::{behavior::ShipBehavior, util, ParticleBuilder, StepResources};
|
||||
use crate::{util, ParticleBuilder, StepResources};
|
||||
use galactica_content as content;
|
||||
use galactica_gameobject as object;
|
||||
|
||||
|
@ -41,6 +40,7 @@ impl ShipControls {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ShipCollapseSequence {
|
||||
total_length: f32,
|
||||
time_elapsed: f32,
|
||||
|
@ -181,6 +181,7 @@ impl ShipCollapseSequence {
|
|||
}
|
||||
|
||||
/// A ship instance in the physics system
|
||||
#[derive(Debug)]
|
||||
pub struct ShipWorldObject {
|
||||
/// This ship's physics handle
|
||||
pub rigid_body: RigidBodyHandle,
|
||||
|
@ -194,9 +195,6 @@ pub struct ShipWorldObject {
|
|||
/// This ship's controls
|
||||
pub(crate) controls: ShipControls,
|
||||
|
||||
/// This ship's behavior
|
||||
behavior: Box<dyn ShipBehavior>,
|
||||
|
||||
/// This ship's collapse sequence
|
||||
collapse_sequence: ShipCollapseSequence,
|
||||
|
||||
|
@ -209,10 +207,9 @@ pub struct ShipWorldObject {
|
|||
|
||||
impl ShipWorldObject {
|
||||
/// Make a new ship
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
ct: &content::Content,
|
||||
data_handle: GameShipHandle,
|
||||
behavior: Box<dyn ShipBehavior>,
|
||||
faction: FactionHandle,
|
||||
rigid_body: RigidBodyHandle,
|
||||
collider: ColliderHandle,
|
||||
|
@ -222,18 +219,12 @@ impl ShipWorldObject {
|
|||
rigid_body,
|
||||
collider,
|
||||
data_handle,
|
||||
behavior,
|
||||
controls: ShipControls::new(),
|
||||
faction,
|
||||
collapse_sequence: ShipCollapseSequence::new(ship_content.collapse.length),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute this ship's controls using its behavior
|
||||
pub fn update_controls(&mut self, res: &StepResources) {
|
||||
self.controls = self.behavior.update_controls(res);
|
||||
}
|
||||
|
||||
/// If this is true, remove this ship from the physics system.
|
||||
pub fn should_be_removed(&self) -> bool {
|
||||
self.collapse_sequence.is_done()
|
||||
|
|
|
@ -12,7 +12,8 @@ use rapier2d::{
|
|||
use std::{collections::HashMap, f32::consts::PI};
|
||||
|
||||
use crate::{
|
||||
behavior, objects,
|
||||
behavior::{self, ShipBehavior},
|
||||
objects,
|
||||
objects::{ProjectileWorldObject, ShipWorldObject},
|
||||
util,
|
||||
wrapper::Wrapper,
|
||||
|
@ -29,6 +30,7 @@ pub struct World {
|
|||
wrapper: Wrapper,
|
||||
projectiles: HashMap<ColliderHandle, objects::ProjectileWorldObject>,
|
||||
ships: HashMap<GameShipHandle, objects::ShipWorldObject>,
|
||||
ship_behaviors: HashMap<GameShipHandle, Box<dyn ShipBehavior>>,
|
||||
collider_ship_table: HashMap<ColliderHandle, GameShipHandle>,
|
||||
|
||||
collision_handler: ChannelEventCollector,
|
||||
|
@ -79,6 +81,7 @@ impl<'a> World {
|
|||
true,
|
||||
);
|
||||
let h = self.collider_ship_table.remove(&s.collider).unwrap();
|
||||
self.ship_behaviors.remove(&h);
|
||||
self.ships.remove(&h);
|
||||
}
|
||||
|
||||
|
@ -177,6 +180,7 @@ impl World {
|
|||
wrapper: Wrapper::new(),
|
||||
projectiles: HashMap::new(),
|
||||
ships: HashMap::new(),
|
||||
ship_behaviors: HashMap::new(),
|
||||
collider_ship_table: HashMap::new(),
|
||||
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
|
||||
collision_queue,
|
||||
|
@ -227,16 +231,11 @@ impl World {
|
|||
);
|
||||
|
||||
self.collider_ship_table.insert(c, ship.get_handle());
|
||||
self.ship_behaviors
|
||||
.insert(ship.get_handle(), Box::new(behavior::Point::new()));
|
||||
self.ships.insert(
|
||||
ship.get_handle(),
|
||||
objects::ShipWorldObject::new(
|
||||
ct,
|
||||
ship.get_handle(),
|
||||
Box::new(behavior::Null::new()),
|
||||
ship.get_faction(),
|
||||
r,
|
||||
c,
|
||||
),
|
||||
objects::ShipWorldObject::new(ct, ship.get_handle(), ship.get_faction(), r, c),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -245,25 +244,57 @@ impl World {
|
|||
/// - applies ship controls
|
||||
/// - creates projectiles
|
||||
fn step_ships(&mut self, res: &mut StepResources) {
|
||||
// We can't apply these right away since self is borrowed
|
||||
// by the iterator
|
||||
// TODO: don't allocate!
|
||||
let mut projectiles = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
for (_, ship_object) in &mut self.ships {
|
||||
for (_, handle) in &self.collider_ship_table {
|
||||
// Borrow immutably for now...
|
||||
// (required to compute controls)
|
||||
let ship_object = self.ships.get(handle).unwrap();
|
||||
if ship_object.should_be_removed() {
|
||||
to_remove.push(ship_object.collider);
|
||||
continue;
|
||||
}
|
||||
|
||||
let rigid_body = &mut self.wrapper.rigid_body_set[ship_object.rigid_body];
|
||||
let collider = &mut self.wrapper.collider_set[ship_object.collider];
|
||||
|
||||
if ship_object.data_handle == res.player {
|
||||
ship_object.controls = res.player_controls.clone();
|
||||
} else {
|
||||
ship_object.update_controls(&res);
|
||||
// Short-circuit continue if this ship isn't in game data
|
||||
// (which means it's playing a collapse sequence)
|
||||
if res.dt.get_ship(*handle).is_none() {
|
||||
let ship_object = self.ships.get_mut(handle).unwrap();
|
||||
ship_object.step(
|
||||
res,
|
||||
&mut self.wrapper.rigid_body_set[ship_object.rigid_body],
|
||||
&mut self.wrapper.collider_set[ship_object.collider],
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: unified step info struct
|
||||
ship_object.step(res, rigid_body, collider);
|
||||
// Compute new controls
|
||||
let controls;
|
||||
if ship_object.data_handle == res.player {
|
||||
controls = res.player_controls.clone();
|
||||
} else {
|
||||
let b = self.ship_behaviors.get_mut(handle).unwrap();
|
||||
controls = b.update_controls(
|
||||
&res,
|
||||
&self.wrapper.rigid_body_set,
|
||||
&self.ships,
|
||||
ship_object.rigid_body,
|
||||
*handle,
|
||||
);
|
||||
}
|
||||
|
||||
// Now re-borrow mutably to apply changes
|
||||
let ship_object = self.ships.get_mut(handle).unwrap();
|
||||
ship_object.controls = controls;
|
||||
ship_object.step(
|
||||
res,
|
||||
&mut self.wrapper.rigid_body_set[ship_object.rigid_body],
|
||||
&mut self.wrapper.collider_set[ship_object.collider],
|
||||
);
|
||||
|
||||
// If we're firing, try to fire each gun
|
||||
if ship_object.controls.guns {
|
||||
let ship_data = res.dt.get_ship_mut(ship_object.data_handle).unwrap();
|
||||
|
||||
|
|
Loading…
Reference in New Issue