Rework world crate
parent
901f407068
commit
35c6676e95
|
@ -1,23 +1,23 @@
|
|||
use cgmath::Point2;
|
||||
use content::Ship;
|
||||
use object::{ship::ShipPersonality, GameData};
|
||||
use object::{
|
||||
ship::{OutfitSet, ShipPersonality},
|
||||
GameData, GameShipHandle,
|
||||
};
|
||||
use std::time::Instant;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||
|
||||
use crate::camera::Camera;
|
||||
use crate::inputstatus::InputStatus;
|
||||
|
||||
use galactica_behavior::{behavior, ShipBehavior};
|
||||
use galactica_constants;
|
||||
use galactica_content as content;
|
||||
use galactica_gameobject as object;
|
||||
use galactica_render::RenderState;
|
||||
use galactica_world::{util, ParticleBuilder, ShipPhysicsHandle, World};
|
||||
use galactica_world::{objects::ShipControls, ParticleBuilder, StepResources, World};
|
||||
|
||||
pub struct Game {
|
||||
input: InputStatus,
|
||||
last_update: Instant,
|
||||
player: ShipPhysicsHandle,
|
||||
player: GameShipHandle,
|
||||
paused: bool,
|
||||
time_scale: f32,
|
||||
start_instant: Instant,
|
||||
|
@ -25,9 +25,6 @@ pub struct Game {
|
|||
|
||||
// TODO: include system in world
|
||||
//system: object::System,
|
||||
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
|
||||
playerbehavior: behavior::Player,
|
||||
|
||||
gamedata: GameData,
|
||||
|
||||
content: content::Content,
|
||||
|
@ -37,28 +34,28 @@ pub struct Game {
|
|||
|
||||
impl Game {
|
||||
pub fn new(ct: content::Content) -> Self {
|
||||
let mut physics = World::new();
|
||||
let mut gamedata = GameData::new();
|
||||
let mut gamedata = GameData::new(&ct);
|
||||
|
||||
let ss = ct.get_ship(content::ShipHandle { index: 0 });
|
||||
|
||||
let mut o1 = object::OutfitSet::new(ss);
|
||||
o1.add(&ct, content::OutfitHandle { index: 0 });
|
||||
o1.add(&ct, content::OutfitHandle { index: 1 });
|
||||
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
let mut o1 = OutfitSet::new(ss.space, &[]);
|
||||
o1.add(&ct.get_outfit(content::OutfitHandle { index: 0 }));
|
||||
o1.add(&ct.get_outfit(content::OutfitHandle { index: 1 }));
|
||||
//o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
//o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
//o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
|
||||
let mut o1 = object::OutfitSet::new(ss);
|
||||
o1.add(&ct, content::OutfitHandle { index: 0 });
|
||||
o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
let mut o1 = OutfitSet::new(ss.space, &[]);
|
||||
o1.add(&ct.get_outfit(content::OutfitHandle { index: 0 }));
|
||||
//o1.add_gun(&ct, content::GunHandle { index: 0 });
|
||||
|
||||
gamedata.create_ship(
|
||||
let player = gamedata.create_ship(
|
||||
&ct,
|
||||
content::ShipHandle { index: 0 },
|
||||
content::FactionHandle { index: 0 },
|
||||
ShipPersonality::Player,
|
||||
o1,
|
||||
o1.clone(),
|
||||
&content::SystemHandle { index: 0 },
|
||||
);
|
||||
|
||||
gamedata.create_ship(
|
||||
|
@ -66,7 +63,8 @@ impl Game {
|
|||
content::ShipHandle { index: 0 },
|
||||
content::FactionHandle { index: 1 },
|
||||
ShipPersonality::Dummy,
|
||||
object::OutfitSet::new(ss),
|
||||
OutfitSet::new(ss.space, &[]),
|
||||
&content::SystemHandle { index: 0 },
|
||||
);
|
||||
|
||||
gamedata.create_ship(
|
||||
|
@ -75,16 +73,15 @@ impl Game {
|
|||
content::FactionHandle { index: 1 },
|
||||
ShipPersonality::Point,
|
||||
o1,
|
||||
&content::SystemHandle { index: 0 },
|
||||
);
|
||||
|
||||
//let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
|
||||
//shipbehaviors.push(Box::new(behavior::Dummy::new(h2)));
|
||||
//shipbehaviors.push(Box::new(behavior::Point::new(h3)));
|
||||
let physics = World::new(&ct, &gamedata, content::SystemHandle { index: 0 });
|
||||
|
||||
Game {
|
||||
last_update: Instant::now(),
|
||||
input: InputStatus::new(),
|
||||
player: h1,
|
||||
player,
|
||||
start_instant: Instant::now(),
|
||||
|
||||
camera: Camera {
|
||||
|
@ -96,10 +93,8 @@ impl Game {
|
|||
paused: false,
|
||||
time_scale: 1.0,
|
||||
world: physics,
|
||||
shipbehaviors,
|
||||
gamedata,
|
||||
content: ct,
|
||||
playerbehavior: behavior::Player::new(h1),
|
||||
new_particles: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -132,39 +127,27 @@ impl Game {
|
|||
pub fn update(&mut self) {
|
||||
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
|
||||
|
||||
self.playerbehavior.key_guns = self.input.key_guns;
|
||||
self.playerbehavior.key_thrust = self.input.key_thrust;
|
||||
self.playerbehavior.key_right = self.input.key_right;
|
||||
self.playerbehavior.key_left = self.input.key_left;
|
||||
self.playerbehavior
|
||||
.update_controls(&mut self.world, &self.content);
|
||||
|
||||
self.shipbehaviors.retain_mut(|b| {
|
||||
// Remove shipbehaviors of destroyed ships
|
||||
if self.world.get_ship_mut(&b.get_handle()).is_none() {
|
||||
false
|
||||
} else {
|
||||
b.update_controls(&mut self.world, &self.content);
|
||||
true
|
||||
}
|
||||
let world_output = self.world.step(StepResources {
|
||||
player: self.player,
|
||||
player_controls: ShipControls {
|
||||
left: self.input.key_left,
|
||||
right: self.input.key_right,
|
||||
thrust: self.input.key_thrust,
|
||||
guns: self.input.key_guns,
|
||||
},
|
||||
ct: &self.content,
|
||||
dt: &mut self.gamedata,
|
||||
particles: &mut self.new_particles,
|
||||
t,
|
||||
});
|
||||
|
||||
self.world.step(t, &self.content, &mut self.new_particles);
|
||||
|
||||
if self.input.v_scroll != 0.0 {
|
||||
self.camera.zoom = (self.camera.zoom + self.input.v_scroll)
|
||||
.clamp(galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX);
|
||||
self.input.v_scroll = 0.0;
|
||||
}
|
||||
|
||||
let r = self
|
||||
.world
|
||||
.get_ship_mut(&self.player)
|
||||
.unwrap()
|
||||
.physics_handle;
|
||||
let r = self.world.get_rigid_body(r.0).unwrap(); // TODO: r.0 shouldn't be public
|
||||
let ship_pos = util::rigidbody_position(r);
|
||||
self.camera.pos = ship_pos;
|
||||
self.camera.pos = world_output.player_position;
|
||||
self.last_update = Instant::now();
|
||||
}
|
||||
|
||||
|
@ -176,7 +159,8 @@ impl Game {
|
|||
content: &self.content,
|
||||
world: &self.world,
|
||||
particles: &mut self.new_particles,
|
||||
player: &self.player,
|
||||
player_data: self.player,
|
||||
data: &self.gamedata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@ mod projectile;
|
|||
mod ship;
|
||||
|
||||
pub use projectile::ProjectileWorldObject;
|
||||
pub use ship::ShipWorldObject;
|
||||
pub use ship::{ShipControls, ShipWorldObject};
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
use rand::Rng;
|
||||
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
|
||||
|
||||
use galactica_gameobject as object;
|
||||
use galactica_content as content;
|
||||
|
||||
/// A single projectile in the world
|
||||
#[derive(Debug)]
|
||||
pub struct ProjectileWorldObject {
|
||||
/// This projectile's game data
|
||||
pub projectile: object::Projectile,
|
||||
pub content: content::Projectile,
|
||||
|
||||
/// The remaining lifetime of this projectile, in seconds
|
||||
pub lifetime: f32,
|
||||
|
||||
/// The faction this projectile belongs to
|
||||
pub faction: content::FactionHandle,
|
||||
|
||||
/// This projectile's rigidbody
|
||||
pub rigid_body: RigidBodyHandle,
|
||||
|
@ -20,19 +26,33 @@ pub struct ProjectileWorldObject {
|
|||
}
|
||||
|
||||
impl ProjectileWorldObject {
|
||||
/// Make a new projectile
|
||||
/// Create a new projectile
|
||||
pub fn new(
|
||||
projectile: object::Projectile,
|
||||
content: content::Projectile, // TODO: use a handle
|
||||
rigid_body: RigidBodyHandle,
|
||||
faction: content::FactionHandle,
|
||||
collider: ColliderHandle,
|
||||
) -> Self {
|
||||
let mut rng = rand::thread_rng();
|
||||
let size_rng = projectile.content.size_rng;
|
||||
let size_rng = content.size_rng;
|
||||
let lifetime = content.lifetime;
|
||||
ProjectileWorldObject {
|
||||
rigid_body,
|
||||
collider,
|
||||
projectile,
|
||||
content,
|
||||
lifetime,
|
||||
faction,
|
||||
size_rng: rng.gen_range(-size_rng..=size_rng),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process this projectile's state after `t` seconds
|
||||
pub fn tick(&mut self, t: f32) {
|
||||
self.lifetime -= t;
|
||||
}
|
||||
|
||||
/// Has this projectile expired?
|
||||
pub fn is_expired(&self) -> bool {
|
||||
return self.lifetime < 0.0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,30 @@
|
|||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
||||
use nalgebra::{point, vector};
|
||||
|
||||
use object::GameShipHandle;
|
||||
use rand::{rngs::ThreadRng, Rng};
|
||||
use rapier2d::{dynamics::RigidBody, geometry::Collider};
|
||||
use rapier2d::{
|
||||
dynamics::{RigidBody, RigidBodyHandle},
|
||||
geometry::{Collider, ColliderHandle},
|
||||
};
|
||||
|
||||
use crate::{util, ParticleBuilder, ShipPhysicsHandle};
|
||||
use crate::{behavior::ShipBehavior, util, ParticleBuilder, StepResources};
|
||||
use galactica_content as content;
|
||||
use galactica_gameobject as object;
|
||||
|
||||
/// A ship's controls
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShipControls {
|
||||
/// True if turning left
|
||||
pub left: bool,
|
||||
|
||||
/// True if turning right
|
||||
pub right: bool,
|
||||
|
||||
/// True if foward thrust
|
||||
pub thrust: bool,
|
||||
|
||||
/// True if firing guns
|
||||
pub guns: bool,
|
||||
}
|
||||
|
||||
|
@ -35,22 +48,23 @@ struct ShipCollapseSequence {
|
|||
impl ShipCollapseSequence {
|
||||
fn new(total_length: f32) -> Self {
|
||||
Self {
|
||||
total_length: total_length,
|
||||
total_length,
|
||||
time_elapsed: 0.0,
|
||||
rng: rand::thread_rng(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Has this sequence been fully played out?
|
||||
fn is_done(&self) -> bool {
|
||||
self.time_elapsed >= self.total_length
|
||||
}
|
||||
|
||||
/// Pick a random points inside a ship's collider
|
||||
fn random_in_ship(
|
||||
&mut self,
|
||||
ship_content: &content::Ship,
|
||||
collider: &Collider,
|
||||
) -> Vector2<f32> {
|
||||
// Pick a random point inside this ship's collider
|
||||
let mut y = 0.0;
|
||||
let mut x = 0.0;
|
||||
let mut a = false;
|
||||
|
@ -63,17 +77,16 @@ impl ShipCollapseSequence {
|
|||
Vector2 { x, y }
|
||||
}
|
||||
|
||||
/// Step this sequence `t` seconds
|
||||
fn step(
|
||||
&mut self,
|
||||
ship: &object::Ship,
|
||||
ct: &content::Content,
|
||||
particles: &mut Vec<ParticleBuilder>,
|
||||
res: &mut StepResources,
|
||||
ship: GameShipHandle,
|
||||
rigid_body: &mut RigidBody,
|
||||
collider: &mut Collider,
|
||||
t: f32,
|
||||
) {
|
||||
let h = ship.handle;
|
||||
let ship_content = ct.get_ship(h);
|
||||
let h = ship.content_handle();
|
||||
let ship_content = res.ct.get_ship(h);
|
||||
let ship_pos = util::rigidbody_position(rigid_body);
|
||||
let ship_rot = util::rigidbody_rotation(rigid_body);
|
||||
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 });
|
||||
|
@ -85,11 +98,11 @@ impl ShipCollapseSequence {
|
|||
for event in &ship_content.collapse.events {
|
||||
match event {
|
||||
content::CollapseEvent::Effect(event) => {
|
||||
if (event.time > self.time_elapsed && event.time <= self.time_elapsed + t)
|
||||
if (event.time > self.time_elapsed && event.time <= self.time_elapsed + res.t)
|
||||
|| (event.time == 0.0 && self.time_elapsed == 0.0)
|
||||
{
|
||||
for spawner in &event.effects {
|
||||
let effect = ct.get_effect(spawner.effect);
|
||||
let effect = res.ct.get_effect(spawner.effect);
|
||||
|
||||
for _ in 0..spawner.count as usize {
|
||||
let pos = if let Some(pos) = spawner.pos {
|
||||
|
@ -102,7 +115,7 @@ impl ShipCollapseSequence {
|
|||
|
||||
let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
|
||||
|
||||
particles.push(ParticleBuilder::from_content(
|
||||
res.particles.push(ParticleBuilder::from_content(
|
||||
effect,
|
||||
pos,
|
||||
Rad::zero(),
|
||||
|
@ -121,7 +134,7 @@ impl ShipCollapseSequence {
|
|||
|
||||
// Create collapse effects
|
||||
for spawner in &ship_content.collapse.effects {
|
||||
let effect = ct.get_effect(spawner.effect);
|
||||
let effect = res.ct.get_effect(spawner.effect);
|
||||
|
||||
// Probability of adding a particle this frame.
|
||||
// The area of this function over [0, 1] should be 1.
|
||||
|
@ -136,7 +149,7 @@ impl ShipCollapseSequence {
|
|||
return y;
|
||||
};
|
||||
|
||||
let p_add = (t / self.total_length) * pdf(frac_done) * spawner.count;
|
||||
let p_add = (res.t / self.total_length) * pdf(frac_done) * spawner.count;
|
||||
|
||||
if self.rng.gen_range(0.0..=1.0) <= p_add {
|
||||
let pos = if let Some(pos) = spawner.pos {
|
||||
|
@ -149,7 +162,7 @@ impl ShipCollapseSequence {
|
|||
let pos = ship_pos + Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
|
||||
let vel = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
|
||||
|
||||
particles.push(ParticleBuilder {
|
||||
res.particles.push(ParticleBuilder {
|
||||
sprite: effect.sprite,
|
||||
pos,
|
||||
velocity: Vector2 { x: vel.x, y: vel.y },
|
||||
|
@ -162,21 +175,28 @@ impl ShipCollapseSequence {
|
|||
}
|
||||
}
|
||||
|
||||
self.time_elapsed += t;
|
||||
self.time_elapsed += res.t;
|
||||
}
|
||||
}
|
||||
|
||||
/// A ship instance in the physics system
|
||||
pub struct ShipWorldObject {
|
||||
/// This ship's physics handle
|
||||
pub physics_handle: ShipPhysicsHandle,
|
||||
pub rigid_body: RigidBodyHandle,
|
||||
|
||||
/// This ship's collider
|
||||
pub collider: ColliderHandle,
|
||||
|
||||
/// This ship's game data
|
||||
pub ship: object::Ship,
|
||||
pub data_handle: GameShipHandle,
|
||||
|
||||
/// This ship's controls
|
||||
pub controls: ShipControls,
|
||||
pub(crate) controls: ShipControls,
|
||||
|
||||
/// This ship's behavior
|
||||
behavior: Box<dyn ShipBehavior>,
|
||||
|
||||
/// This ship's collapse sequence
|
||||
collapse_sequence: ShipCollapseSequence,
|
||||
}
|
||||
|
||||
|
@ -184,51 +204,45 @@ impl ShipWorldObject {
|
|||
/// Make a new ship
|
||||
pub fn new(
|
||||
ct: &content::Content,
|
||||
ship: object::Ship,
|
||||
physics_handle: ShipPhysicsHandle,
|
||||
data_handle: GameShipHandle,
|
||||
behavior: Box<dyn ShipBehavior>,
|
||||
rigid_body: RigidBodyHandle,
|
||||
collider: ColliderHandle,
|
||||
) -> Self {
|
||||
let ship_content = ct.get_ship(ship.handle);
|
||||
let ship_content = ct.get_ship(data_handle.content_handle());
|
||||
ShipWorldObject {
|
||||
physics_handle,
|
||||
ship,
|
||||
rigid_body,
|
||||
collider,
|
||||
data_handle,
|
||||
behavior,
|
||||
controls: ShipControls::new(),
|
||||
collapse_sequence: ShipCollapseSequence::new(ship_content.collapse.length),
|
||||
}
|
||||
}
|
||||
|
||||
/// Should this ship should be removed from the world?
|
||||
pub fn remove_from_world(&self) -> bool {
|
||||
return self.ship.is_dead() && self.collapse_sequence.is_done();
|
||||
/// Compute this ship's controls using its behavior
|
||||
pub fn update_controls(&mut self, res: &StepResources) {
|
||||
self.controls = self.behavior.update_controls(res);
|
||||
}
|
||||
|
||||
/// Step this ship's state by t seconds
|
||||
pub fn step(
|
||||
&mut self,
|
||||
ct: &content::Content,
|
||||
particles: &mut Vec<ParticleBuilder>,
|
||||
res: &mut StepResources,
|
||||
rigid_body: &mut RigidBody,
|
||||
collider: &mut Collider,
|
||||
t: f32,
|
||||
) {
|
||||
if self.ship.is_dead() {
|
||||
return self
|
||||
.collapse_sequence
|
||||
.step(&self.ship, ct, particles, rigid_body, collider, t);
|
||||
}
|
||||
|
||||
self.ship.step(t);
|
||||
|
||||
let ship_content = ct.get_ship(self.ship.handle);
|
||||
let ship = res.dt.get_ship(self.data_handle).unwrap();
|
||||
let ship_content = res.ct.get_ship(self.data_handle.content_handle());
|
||||
let ship_pos = util::rigidbody_position(&rigid_body);
|
||||
let ship_rot = util::rigidbody_rotation(rigid_body);
|
||||
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 });
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
if self.ship.hull <= ship_content.damage.hull {
|
||||
if ship.get_hull() <= ship_content.damage.hull {
|
||||
for e in &ship_content.damage.effects {
|
||||
if rng.gen_range(0.0..=1.0) <= t / e.frequency {
|
||||
let effect = ct.get_effect(e.effect);
|
||||
let ship_content = ct.get_ship(self.ship.handle);
|
||||
if rng.gen_range(0.0..=1.0) <= res.t / e.frequency {
|
||||
let effect = res.ct.get_effect(e.effect);
|
||||
|
||||
let pos = if let Some(pos) = e.pos {
|
||||
pos.to_vec()
|
||||
|
@ -252,7 +266,7 @@ impl ShipWorldObject {
|
|||
|
||||
let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
|
||||
|
||||
particles.push(ParticleBuilder::from_content(
|
||||
res.particles.push(ParticleBuilder::from_content(
|
||||
effect,
|
||||
pos,
|
||||
Rad::zero(),
|
||||
|
@ -266,28 +280,27 @@ impl ShipWorldObject {
|
|||
}
|
||||
}
|
||||
|
||||
let engine_force = ship_rot * t;
|
||||
let engine_force = ship_rot * res.t;
|
||||
|
||||
if self.controls.thrust {
|
||||
rigid_body.apply_impulse(
|
||||
vector![engine_force.x, engine_force.y]
|
||||
* self.ship.outfits.stat_sum().engine_thrust,
|
||||
vector![engine_force.x, engine_force.y] * ship.get_outfits().get_engine_thrust(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
if self.controls.right {
|
||||
rigid_body
|
||||
.apply_torque_impulse(self.ship.outfits.stat_sum().steer_power * -100.0 * t, true);
|
||||
.apply_torque_impulse(ship.get_outfits().get_steer_power() * -100.0 * res.t, true);
|
||||
}
|
||||
|
||||
if self.controls.left {
|
||||
rigid_body
|
||||
.apply_torque_impulse(self.ship.outfits.stat_sum().steer_power * 100.0 * t, true);
|
||||
.apply_torque_impulse(ship.get_outfits().get_steer_power() * 100.0 * res.t, true);
|
||||
}
|
||||
|
||||
for i in self.ship.outfits.iter_guns() {
|
||||
i.cooldown -= t;
|
||||
}
|
||||
//for i in self.ship.outfits.iter_guns() {
|
||||
// i.cooldown -= t;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
|
||||
use crossbeam::channel::Receiver;
|
||||
use nalgebra::{point, vector};
|
||||
use object::{ship::Ship, GameData, GameShipHandle};
|
||||
use rand::Rng;
|
||||
use rapier2d::{
|
||||
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
||||
|
@ -10,21 +11,24 @@ use rapier2d::{
|
|||
use std::{collections::HashMap, f32::consts::PI};
|
||||
|
||||
use crate::{
|
||||
objects,
|
||||
behavior, objects,
|
||||
objects::{ProjectileWorldObject, ShipWorldObject},
|
||||
util,
|
||||
wrapper::Wrapper,
|
||||
ParticleBuilder, ShipPhysicsHandle,
|
||||
ParticleBuilder, StepOutput, StepResources,
|
||||
};
|
||||
use galactica_content as content;
|
||||
use galactica_gameobject as object;
|
||||
|
||||
/// Keeps track of all objects in the world that we can interact with.
|
||||
/// Also wraps our physics engine
|
||||
/// Manages the physics state of one system
|
||||
pub struct World {
|
||||
/// The system this world is attached to
|
||||
system: content::SystemHandle,
|
||||
|
||||
wrapper: Wrapper,
|
||||
projectiles: HashMap<ColliderHandle, objects::ProjectileWorldObject>,
|
||||
ships: HashMap<ColliderHandle, objects::ShipWorldObject>,
|
||||
ships: HashMap<GameShipHandle, objects::ShipWorldObject>,
|
||||
collider_ship_table: HashMap<ColliderHandle, GameShipHandle>,
|
||||
|
||||
collision_handler: ChannelEventCollector,
|
||||
collision_queue: Receiver<CollisionEvent>,
|
||||
|
@ -56,23 +60,25 @@ impl<'a> World {
|
|||
return Some((r, p));
|
||||
}
|
||||
|
||||
fn remove_ship(&mut self, h: ShipPhysicsHandle) {
|
||||
fn remove_ship(&mut self, s: &ShipWorldObject) {
|
||||
self.wrapper.rigid_body_set.remove(
|
||||
h.0,
|
||||
s.rigid_body,
|
||||
&mut self.wrapper.im,
|
||||
&mut self.wrapper.collider_set,
|
||||
&mut self.wrapper.ij,
|
||||
&mut self.wrapper.mj,
|
||||
true,
|
||||
);
|
||||
self.ships.remove(&h.1);
|
||||
let h = self.collider_ship_table.remove(&s.collider).unwrap();
|
||||
self.ships.remove(&h);
|
||||
}
|
||||
|
||||
/*
|
||||
/// Add a projectile fired from a ship
|
||||
fn add_projectiles(
|
||||
&mut self,
|
||||
s: ShipPhysicsHandle,
|
||||
p: Vec<(object::Projectile, content::GunPoint)>,
|
||||
p: Vec<(ProjectileWorldObject, content::GunPoint)>,
|
||||
) {
|
||||
let mut rng = rand::thread_rng();
|
||||
for (projectile, point) in p {
|
||||
|
@ -126,37 +132,44 @@ impl<'a> World {
|
|||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn collide_projectile_ship(
|
||||
&mut self,
|
||||
ct: &content::Content,
|
||||
particles: &mut Vec<ParticleBuilder>,
|
||||
res: &mut StepResources,
|
||||
projectile_h: ColliderHandle,
|
||||
ship_h: ColliderHandle,
|
||||
) {
|
||||
let projectile = self.projectiles.get(&projectile_h);
|
||||
let ship = self.ships.get_mut(&ship_h);
|
||||
let ship = self
|
||||
.ships
|
||||
.get_mut(self.collider_ship_table.get(&ship_h).unwrap());
|
||||
if projectile.is_none() || ship.is_none() {
|
||||
return;
|
||||
}
|
||||
let projectile = projectile.unwrap();
|
||||
let ship = ship.unwrap();
|
||||
|
||||
let hit = ship
|
||||
.ship
|
||||
.handle_projectile_collision(ct, &projectile.projectile);
|
||||
let s = ship.physics_handle;
|
||||
if hit {
|
||||
let ship_d = res.dt.get_ship_mut(ship.data_handle).unwrap();
|
||||
|
||||
// TODO: check faction
|
||||
ship_d.apply_damage(projectile.content.damage);
|
||||
|
||||
if true {
|
||||
let pr = self
|
||||
.wrapper
|
||||
.rigid_body_set
|
||||
.get(projectile.rigid_body)
|
||||
.unwrap();
|
||||
let v = util::rigidbody_velocity(pr).normalize() * projectile.projectile.content.force;
|
||||
let v = util::rigidbody_velocity(pr).normalize() * projectile.content.force;
|
||||
let pos = util::rigidbody_position(pr);
|
||||
let _ = pr;
|
||||
|
||||
let r = self.wrapper.rigid_body_set.get_mut(s.0).unwrap();
|
||||
let r = self
|
||||
.wrapper
|
||||
.rigid_body_set
|
||||
.get_mut(ship.rigid_body)
|
||||
.unwrap();
|
||||
r.apply_impulse_at_point(vector![v.x, v.y], point![pos.x, pos.y], true);
|
||||
|
||||
// Borrow again, we can only have one at a time
|
||||
|
@ -168,16 +181,17 @@ impl<'a> World {
|
|||
let pos = util::rigidbody_position(pr);
|
||||
let angle = util::rigidbody_rotation(pr).angle(Vector2 { x: 1.0, y: 0.0 });
|
||||
|
||||
match &projectile.projectile.content.impact_effect {
|
||||
match &projectile.content.impact_effect {
|
||||
None => {}
|
||||
Some(x) => {
|
||||
let effect = ct.get_effect(*x);
|
||||
let (_, sr) = self.get_ship_body(s).unwrap();
|
||||
let effect = res.ct.get_effect(*x);
|
||||
let r = ship.rigid_body;
|
||||
let sr = self.get_rigid_body(r).unwrap();
|
||||
let parent_velocity = util::rigidbody_velocity(pr);
|
||||
let target_velocity =
|
||||
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
|
||||
|
||||
particles.push(ParticleBuilder::from_content(
|
||||
res.particles.push(ParticleBuilder::from_content(
|
||||
effect,
|
||||
pos,
|
||||
-angle,
|
||||
|
@ -198,27 +212,41 @@ impl<'a> World {
|
|||
// Public methods
|
||||
impl<'a> World {
|
||||
/// Create a new physics system
|
||||
pub fn new() -> Self {
|
||||
pub fn new(ct: &content::Content, dt: &GameData, system: content::SystemHandle) -> Self {
|
||||
let (collision_send, collision_queue) = crossbeam::channel::unbounded();
|
||||
let (contact_force_send, _) = crossbeam::channel::unbounded();
|
||||
|
||||
Self {
|
||||
let mut w = Self {
|
||||
system,
|
||||
wrapper: Wrapper::new(),
|
||||
projectiles: HashMap::new(),
|
||||
ships: HashMap::new(),
|
||||
collider_ship_table: HashMap::new(),
|
||||
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
|
||||
collision_queue,
|
||||
};
|
||||
|
||||
// TODO: guarantee not touching
|
||||
// TODO: add, remove ships each tick
|
||||
// Maybe store position in gamedata?
|
||||
let mut rng = rand::thread_rng();
|
||||
for s in dt.iter_ships() {
|
||||
w.add_ship(
|
||||
ct,
|
||||
s,
|
||||
Point2 {
|
||||
x: rng.gen_range(-500.0..=500.0),
|
||||
y: rng.gen_range(-500.0..=500.0),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/// Add a ship to this physics system
|
||||
pub fn add_ship(
|
||||
&mut self,
|
||||
ct: &content::Content,
|
||||
ship: object::Ship,
|
||||
position: Point2<f32>,
|
||||
) -> ShipPhysicsHandle {
|
||||
let ship_content = ct.get_ship(ship.handle);
|
||||
pub fn add_ship(&mut self, ct: &content::Content, ship: &Ship, position: Point2<f32>) {
|
||||
let ship_content = ct.get_ship(ship.get_content());
|
||||
let cl = ColliderBuilder::convex_decomposition(
|
||||
&ship_content.collision.points[..],
|
||||
&ship_content.collision.indices[..],
|
||||
|
@ -242,42 +270,60 @@ impl<'a> World {
|
|||
&mut self.wrapper.rigid_body_set,
|
||||
);
|
||||
|
||||
let h = ShipPhysicsHandle(r, c);
|
||||
self.ships
|
||||
.insert(c, objects::ShipWorldObject::new(ct, ship, h));
|
||||
return h;
|
||||
self.collider_ship_table.insert(c, ship.get_handle());
|
||||
self.ships.insert(
|
||||
ship.get_handle(),
|
||||
objects::ShipWorldObject::new(
|
||||
ct,
|
||||
ship.get_handle(),
|
||||
Box::new(behavior::Null::new()),
|
||||
r,
|
||||
c,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Step this physics system by `t` seconds
|
||||
pub fn step(&mut self, t: f32, ct: &content::Content, particles: &mut Vec<ParticleBuilder>) {
|
||||
pub fn step(&mut self, mut res: StepResources) -> StepOutput {
|
||||
let mut output = StepOutput {
|
||||
player_position: Point2 { x: 0.0, y: 0.0 },
|
||||
};
|
||||
|
||||
// Run ship updates
|
||||
// TODO: maybe reorganize projectile creation?
|
||||
let mut projectiles = Vec::new();
|
||||
//let mut projectiles = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
for (_, s) in &mut self.ships {
|
||||
let r = &mut self.wrapper.rigid_body_set[s.physics_handle.0];
|
||||
let c = &mut self.wrapper.collider_set[s.physics_handle.1];
|
||||
let r = &mut self.wrapper.rigid_body_set[s.rigid_body];
|
||||
let c = &mut self.wrapper.collider_set[s.collider];
|
||||
|
||||
if s.data_handle == res.player {
|
||||
s.controls = res.player_controls.clone();
|
||||
output.player_position = util::rigidbody_position(r);
|
||||
} else {
|
||||
s.update_controls(&res);
|
||||
}
|
||||
|
||||
// TODO: unified step info struct
|
||||
s.step(ct, particles, r, c, t);
|
||||
if s.controls.guns {
|
||||
projectiles.push((s.physics_handle, s.ship.fire_guns()));
|
||||
}
|
||||
s.step(&mut res, r, c);
|
||||
//if s.controls.guns {
|
||||
// projectiles.push((s.physics_handle, s.ship.fire_guns()));
|
||||
//}
|
||||
|
||||
if s.remove_from_world() {
|
||||
to_remove.push(s.physics_handle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (s, p) in projectiles {
|
||||
self.add_projectiles(s, p);
|
||||
//if s.remove_from_world() {
|
||||
// to_remove.push(s.physics_handle);
|
||||
// continue;
|
||||
//}
|
||||
}
|
||||
//for (s, p) in projectiles {
|
||||
// self.add_projectiles(s, p);
|
||||
//}
|
||||
for s in to_remove {
|
||||
self.remove_ship(s);
|
||||
}
|
||||
|
||||
// Update physics
|
||||
self.wrapper.step(t, &self.collision_handler);
|
||||
self.wrapper.step(res.t, &self.collision_handler);
|
||||
|
||||
// Handle collision events
|
||||
while let Ok(event) = &self.collision_queue.try_recv() {
|
||||
|
@ -294,19 +340,21 @@ impl<'a> World {
|
|||
};
|
||||
|
||||
let p = self.projectiles.get(&a);
|
||||
let s = self.ships.get_mut(&b);
|
||||
let s = self
|
||||
.ships
|
||||
.get_mut(self.collider_ship_table.get(&b).unwrap());
|
||||
if p.is_none() || s.is_none() {
|
||||
continue;
|
||||
}
|
||||
self.collide_projectile_ship(ct, particles, a, b);
|
||||
self.collide_projectile_ship(&mut res, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete projectiles
|
||||
let mut to_remove = Vec::new();
|
||||
for (c, p) in &mut self.projectiles {
|
||||
p.projectile.tick(t);
|
||||
if p.projectile.is_expired() {
|
||||
p.tick(res.t);
|
||||
if p.is_expired() {
|
||||
to_remove.push(*c);
|
||||
}
|
||||
}
|
||||
|
@ -315,10 +363,10 @@ impl<'a> World {
|
|||
for c in to_remove {
|
||||
let (pr, p) = self.remove_projectile(c).unwrap();
|
||||
|
||||
match &p.projectile.content.expire_effect {
|
||||
match &p.content.expire_effect {
|
||||
None => {}
|
||||
Some(x) => {
|
||||
let x = ct.get_effect(*x);
|
||||
let x = res.ct.get_effect(*x);
|
||||
let pos = util::rigidbody_position(&pr);
|
||||
let vel = util::rigidbody_velocity(&pr);
|
||||
let angle = util::rigidbody_rotation(&pr).angle(Vector2 { x: 1.0, y: 0.0 });
|
||||
|
@ -332,7 +380,7 @@ impl<'a> World {
|
|||
velocity
|
||||
};
|
||||
|
||||
particles.push(ParticleBuilder::from_content(
|
||||
res.particles.push(ParticleBuilder::from_content(
|
||||
x,
|
||||
pos,
|
||||
-angle,
|
||||
|
@ -342,6 +390,12 @@ impl<'a> World {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
pub fn get_ship(&self, ship: GameShipHandle) -> Option<&ShipWorldObject> {
|
||||
self.ships.get(&ship)
|
||||
}
|
||||
|
||||
/// Get a rigid body from a handle
|
||||
|
@ -354,29 +408,13 @@ impl<'a> World {
|
|||
self.wrapper.rigid_body_set.get_mut(r)
|
||||
}
|
||||
|
||||
/// Get a ship from a handle
|
||||
pub fn get_ship_mut(&mut self, s: &ShipPhysicsHandle) -> Option<&mut objects::ShipWorldObject> {
|
||||
self.ships.get_mut(&s.1)
|
||||
}
|
||||
|
||||
/// Get a ship and its rigidbody from a handle
|
||||
pub fn get_ship_body(
|
||||
&self,
|
||||
s: ShipPhysicsHandle,
|
||||
) -> Option<(&objects::ShipWorldObject, &RigidBody)> {
|
||||
Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?))
|
||||
}
|
||||
|
||||
/// Iterate over all ships in this physics system
|
||||
pub fn iter_ship_body(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&objects::ShipWorldObject, &RigidBody)> + '_ {
|
||||
self.ships.values().map(|x| {
|
||||
(
|
||||
x,
|
||||
self.wrapper.rigid_body_set.get(x.physics_handle.0).unwrap(),
|
||||
)
|
||||
})
|
||||
self.ships
|
||||
.values()
|
||||
.map(|x| (x, self.wrapper.rigid_body_set.get(x.rigid_body).unwrap()))
|
||||
}
|
||||
|
||||
/// Iterate over all ships in this physics system
|
||||
|
|
Loading…
Reference in New Issue