Reworked guns and projectile spawning
parent
0095a23dd6
commit
07ec685c13
|
@ -1,7 +1,4 @@
|
|||
use object::{
|
||||
ship::{OutfitSet, ShipPersonality},
|
||||
GameData, GameShipHandle,
|
||||
};
|
||||
use object::{ship::ShipPersonality, GameData, GameShipHandle};
|
||||
use std::time::Instant;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||
|
||||
|
@ -12,7 +9,7 @@ use galactica_constants;
|
|||
use galactica_content as content;
|
||||
use galactica_gameobject as object;
|
||||
use galactica_render::RenderState;
|
||||
use galactica_world::{objects::ShipControls, ParticleBuilder, StepResources, World};
|
||||
use galactica_world::{objects::ShipControls, util, ParticleBuilder, StepResources, World};
|
||||
|
||||
pub struct Game {
|
||||
input: InputStatus,
|
||||
|
@ -36,45 +33,35 @@ impl Game {
|
|||
pub fn new(ct: content::Content) -> Self {
|
||||
let mut gamedata = GameData::new(&ct);
|
||||
|
||||
let ss = ct.get_ship(content::ShipHandle { 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 player = gamedata.create_ship(
|
||||
&ct,
|
||||
content::ShipHandle { index: 0 },
|
||||
content::FactionHandle { index: 0 },
|
||||
ShipPersonality::Player,
|
||||
o1.clone(),
|
||||
&content::SystemHandle { index: 0 },
|
||||
);
|
||||
let s = gamedata.get_ship_mut(player).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 }));
|
||||
|
||||
gamedata.create_ship(
|
||||
&ct,
|
||||
content::ShipHandle { index: 0 },
|
||||
content::FactionHandle { index: 1 },
|
||||
ShipPersonality::Dummy,
|
||||
OutfitSet::new(ss.space, &[]),
|
||||
&content::SystemHandle { 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 a = gamedata.create_ship(
|
||||
&ct,
|
||||
content::ShipHandle { index: 0 },
|
||||
content::FactionHandle { index: 1 },
|
||||
ShipPersonality::Point,
|
||||
o1,
|
||||
&content::SystemHandle { index: 0 },
|
||||
);
|
||||
let s = gamedata.get_ship_mut(a).unwrap();
|
||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 0 }));
|
||||
|
||||
let physics = World::new(&ct, &gamedata, content::SystemHandle { index: 0 });
|
||||
|
||||
|
@ -127,7 +114,9 @@ impl Game {
|
|||
pub fn update(&mut self) {
|
||||
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
|
||||
|
||||
let world_output = self.world.step(StepResources {
|
||||
self.gamedata.step(t);
|
||||
|
||||
self.world.step(StepResources {
|
||||
player: self.player,
|
||||
player_controls: ShipControls {
|
||||
left: self.input.key_left,
|
||||
|
@ -147,7 +136,12 @@ impl Game {
|
|||
self.input.v_scroll = 0.0;
|
||||
}
|
||||
|
||||
self.camera.pos = world_output.player_position;
|
||||
self.camera.pos = {
|
||||
let o = self.world.get_ship(self.player).unwrap();
|
||||
let r = self.world.get_rigid_body(o.rigid_body).unwrap();
|
||||
util::rigidbody_position(r)
|
||||
};
|
||||
|
||||
self.last_update = Instant::now();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
handles::GameShipHandle,
|
||||
ship::Ship,
|
||||
ship::{OutfitSet, ShipPersonality},
|
||||
};
|
||||
use crate::{handles::GameShipHandle, ship::Ship, ship::ShipPersonality};
|
||||
use galactica_content as content;
|
||||
|
||||
/// Keeps track of all objects in the galaxy.
|
||||
|
@ -44,7 +40,6 @@ impl GameData {
|
|||
ship: content::ShipHandle,
|
||||
faction: content::FactionHandle,
|
||||
personality: ShipPersonality,
|
||||
outfits: OutfitSet,
|
||||
system: &content::SystemHandle,
|
||||
) -> GameShipHandle {
|
||||
let handle = GameShipHandle {
|
||||
|
@ -53,15 +48,19 @@ impl GameData {
|
|||
};
|
||||
self.index += 1;
|
||||
|
||||
self.ships.insert(
|
||||
handle,
|
||||
Ship::new(ct, handle, ship, faction, personality, outfits),
|
||||
);
|
||||
self.ships
|
||||
.insert(handle, Ship::new(ct, handle, ship, faction, personality));
|
||||
self.system_ship_table.get_mut(system).unwrap().push(handle);
|
||||
self.ship_system_table.insert(handle, *system);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
pub fn step(&mut self, t: f32) {
|
||||
for (_, s) in &mut self.ships {
|
||||
s.step(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Public getters
|
||||
|
|
|
@ -14,6 +14,10 @@ pub enum OutfitAddResult {
|
|||
/// outfits may need outfit AND weapon space. In these cases, this result
|
||||
/// should name the "most specific" kind of space we lack.
|
||||
NotEnoughSpace(String),
|
||||
|
||||
/// An outfit couldn't be added because there weren't enough points for it
|
||||
/// (e.g, gun points, turret points, etc)
|
||||
NotEnoughPoints(String),
|
||||
}
|
||||
|
||||
/// Possible outcomes when removing an outfit
|
||||
|
@ -37,6 +41,9 @@ pub(crate) struct ShieldGenerator {
|
|||
}
|
||||
|
||||
/// This struct keeps track of a ship's outfit loadout.
|
||||
/// This is a fairly static data structure: it does not keep track of cooldowns,
|
||||
/// shield damage, etc. It only provides an interface for static stats which are
|
||||
/// then used elsewhere.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutfitSet {
|
||||
/// What outfits does this statsum contain?
|
||||
|
@ -68,7 +75,7 @@ pub struct OutfitSet {
|
|||
}
|
||||
|
||||
impl OutfitSet {
|
||||
pub fn new(available_space: OutfitSpace, gun_points: &[GunPoint]) -> Self {
|
||||
pub(super) fn new(available_space: OutfitSpace, gun_points: &[GunPoint]) -> Self {
|
||||
Self {
|
||||
outfits: HashMap::new(),
|
||||
total_space: available_space,
|
||||
|
@ -82,11 +89,26 @@ impl OutfitSet {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, o: &content::Outfit) -> OutfitAddResult {
|
||||
pub(super) fn add(&mut self, o: &content::Outfit) -> OutfitAddResult {
|
||||
if !(self.total_space - self.used_space).can_contain(&o.space) {
|
||||
return OutfitAddResult::NotEnoughSpace("TODO".to_string());
|
||||
}
|
||||
|
||||
// Check and return as fast as possible,
|
||||
// BEFORE we make any changes.
|
||||
if o.gun.is_some() {
|
||||
let mut added = false;
|
||||
for (_, outfit) in &mut self.gun_points {
|
||||
if outfit.is_none() {
|
||||
*outfit = Some(o.handle);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
if !added {
|
||||
return OutfitAddResult::NotEnoughPoints("gun".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
self.used_space += o.space;
|
||||
|
||||
self.engine_thrust += o.engine_thrust;
|
||||
|
@ -107,7 +129,7 @@ impl OutfitSet {
|
|||
return OutfitAddResult::Ok;
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, o: &content::Outfit) -> OutfitRemoveResult {
|
||||
pub(super) fn remove(&mut self, o: &content::Outfit) -> OutfitRemoveResult {
|
||||
if !self.outfits.contains_key(&o.handle) {
|
||||
return OutfitRemoveResult::NotExist;
|
||||
} else {
|
||||
|
@ -177,6 +199,12 @@ impl OutfitSet {
|
|||
self.shield_generators.iter().map(|x| x.generation).sum()
|
||||
}
|
||||
|
||||
/// Get the outfit attached to the given gun point
|
||||
/// Will panic if this gunpoint is not in this outfit set.
|
||||
pub fn get_gun(&self, point: &GunPoint) -> Option<OutfitHandle> {
|
||||
self.gun_points.get(point).unwrap().clone()
|
||||
}
|
||||
|
||||
/// Total available outfit space
|
||||
pub fn get_total_space(&self) -> &OutfitSpace {
|
||||
&self.total_space
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::time::Instant;
|
||||
use std::{collections::HashMap, time::Instant};
|
||||
|
||||
use crate::GameShipHandle;
|
||||
|
||||
use super::{OutfitSet, ShipPersonality};
|
||||
use content::GunPoint;
|
||||
use galactica_content as content;
|
||||
use rand::{rngs::ThreadRng, Rng};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ship {
|
||||
|
@ -19,6 +21,8 @@ pub struct Ship {
|
|||
// TODO: unified ship stats struct, like outfit space
|
||||
hull: f32,
|
||||
shields: f32,
|
||||
gun_cooldowns: HashMap<GunPoint, f32>,
|
||||
rng: ThreadRng,
|
||||
|
||||
// Utility values
|
||||
/// The last time this ship was damaged
|
||||
|
@ -32,29 +36,61 @@ impl Ship {
|
|||
ct_handle: content::ShipHandle,
|
||||
faction: content::FactionHandle,
|
||||
personality: ShipPersonality,
|
||||
outfits: OutfitSet,
|
||||
) -> Self {
|
||||
let s = ct.get_ship(ct_handle);
|
||||
let shields = outfits.get_shield_strength();
|
||||
Ship {
|
||||
handle,
|
||||
ct_handle,
|
||||
faction,
|
||||
outfits,
|
||||
outfits: OutfitSet::new(s.space, &s.guns),
|
||||
personality,
|
||||
last_hit: Instant::now(),
|
||||
rng: rand::thread_rng(),
|
||||
|
||||
// Initial stats
|
||||
hull: s.hull,
|
||||
shields,
|
||||
shields: 0.0,
|
||||
gun_cooldowns: s.guns.iter().map(|x| (x.clone(), 0.0)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an outfit to this ship
|
||||
pub fn add_outfit(&mut self, o: &content::Outfit) -> super::OutfitAddResult {
|
||||
self.outfits.add(o)
|
||||
}
|
||||
|
||||
/// Remove an outfit from this ship
|
||||
pub fn remove_outfit(&mut self, o: &content::Outfit) -> super::OutfitRemoveResult {
|
||||
self.outfits.remove(o)
|
||||
}
|
||||
|
||||
/// If this ship is dead, it will be removed from the game.
|
||||
pub fn is_dead(&self) -> bool {
|
||||
self.hull <= 0.0
|
||||
}
|
||||
|
||||
/// Try to fire a gun.
|
||||
/// Will panic if `which` isn't a point on this ship.
|
||||
/// Returns `true` if this gun was fired,
|
||||
/// and `false` if it is on cooldown or empty.
|
||||
pub fn fire_gun(&mut self, ct: &content::Content, which: &GunPoint) -> bool {
|
||||
let c = self.gun_cooldowns.get_mut(which).unwrap();
|
||||
|
||||
if *c > 0.0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let g = self.outfits.get_gun(which);
|
||||
if g.is_some() {
|
||||
let g = ct.get_outfit(g.unwrap());
|
||||
let gun = g.gun.as_ref().unwrap();
|
||||
*c = 0f32.max(gun.rate + self.rng.gen_range(-gun.rate_rng..=gun.rate_rng));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Hit this ship with the given amount of damage
|
||||
pub fn apply_damage(&mut self, mut d: f32) {
|
||||
if self.is_dead() {
|
||||
|
@ -72,6 +108,14 @@ impl Ship {
|
|||
|
||||
/// Update this ship's state by `t` seconds
|
||||
pub fn step(&mut self, t: f32) {
|
||||
// Cooldown guns
|
||||
for (_, c) in &mut self.gun_cooldowns {
|
||||
if *c > 0.0 {
|
||||
*c -= t;
|
||||
}
|
||||
}
|
||||
|
||||
// Regenerate shields
|
||||
let time_since = self.last_hit.elapsed().as_secs_f32();
|
||||
if self.shields != self.outfits.get_shield_strength() {
|
||||
for g in self.outfits.iter_shield_generators() {
|
||||
|
|
|
@ -298,10 +298,6 @@ impl ShipWorldObject {
|
|||
rigid_body
|
||||
.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;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::{objects::ShipControls, ParticleBuilder};
|
||||
use cgmath::Point2;
|
||||
use galactica_content::Content;
|
||||
use galactica_gameobject::{GameData, GameShipHandle};
|
||||
|
||||
|
@ -24,10 +23,3 @@ pub struct StepResources<'a> {
|
|||
/// The ship that the player controls
|
||||
pub player: GameShipHandle,
|
||||
}
|
||||
|
||||
/// Return values after computing time steps
|
||||
#[derive(Debug)]
|
||||
pub struct StepOutput {
|
||||
/// The player's position in world coordinates
|
||||
pub player_position: Point2<f32>,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
|
||||
use content::{GunPoint, OutfitHandle};
|
||||
use crossbeam::channel::Receiver;
|
||||
use nalgebra::{point, vector};
|
||||
use object::{ship::Ship, GameData, GameShipHandle};
|
||||
|
@ -15,7 +16,7 @@ use crate::{
|
|||
objects::{ProjectileWorldObject, ShipWorldObject},
|
||||
util,
|
||||
wrapper::Wrapper,
|
||||
ParticleBuilder, StepOutput, StepResources,
|
||||
ParticleBuilder, StepResources,
|
||||
};
|
||||
use galactica_content as content;
|
||||
use galactica_gameobject as object;
|
||||
|
@ -23,7 +24,7 @@ use galactica_gameobject as object;
|
|||
/// Manages the physics state of one system
|
||||
pub struct World {
|
||||
/// The system this world is attached to
|
||||
system: content::SystemHandle,
|
||||
_system: content::SystemHandle,
|
||||
|
||||
wrapper: Wrapper,
|
||||
projectiles: HashMap<ColliderHandle, objects::ProjectileWorldObject>,
|
||||
|
@ -73,67 +74,6 @@ impl<'a> World {
|
|||
self.ships.remove(&h);
|
||||
}
|
||||
|
||||
/*
|
||||
/// Add a projectile fired from a ship
|
||||
fn add_projectiles(
|
||||
&mut self,
|
||||
s: ShipPhysicsHandle,
|
||||
p: Vec<(ProjectileWorldObject, content::GunPoint)>,
|
||||
) {
|
||||
let mut rng = rand::thread_rng();
|
||||
for (projectile, point) in p {
|
||||
let r = self.get_ship_body(s).unwrap().1;
|
||||
|
||||
let ship_pos = util::rigidbody_position(r);
|
||||
let ship_rot = util::rigidbody_rotation(r);
|
||||
let ship_vel = util::rigidbody_velocity(r);
|
||||
let ship_ang = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 });
|
||||
|
||||
let pos = ship_pos + (Matrix2::from_angle(-ship_ang) * point.pos.to_vec());
|
||||
|
||||
let spread: Rad<f32> = Deg(
|
||||
rng.gen_range(-projectile.content.angle_rng.0..=projectile.content.angle_rng.0)
|
||||
)
|
||||
.into();
|
||||
|
||||
let vel = ship_vel
|
||||
+ (Matrix2::from_angle(-ship_ang + spread)
|
||||
* Vector2 {
|
||||
x: 0.0,
|
||||
y: projectile.content.speed
|
||||
+ rng.gen_range(
|
||||
-projectile.content.speed_rng..=projectile.content.speed_rng,
|
||||
),
|
||||
});
|
||||
|
||||
let rigid_body = RigidBodyBuilder::kinematic_velocity_based()
|
||||
.translation(vector![pos.x, pos.y])
|
||||
.rotation(-ship_ang.0)
|
||||
.linvel(vector![vel.x, vel.y])
|
||||
.build();
|
||||
|
||||
let collider = match &projectile.content.collider {
|
||||
content::ProjectileCollider::Ball(b) => ColliderBuilder::ball(b.radius)
|
||||
.sensor(true)
|
||||
.active_events(ActiveEvents::COLLISION_EVENTS)
|
||||
.build(),
|
||||
};
|
||||
|
||||
let rigid_body = self.wrapper.rigid_body_set.insert(rigid_body);
|
||||
let collider = self.wrapper.collider_set.insert_with_parent(
|
||||
collider,
|
||||
rigid_body,
|
||||
&mut self.wrapper.rigid_body_set,
|
||||
);
|
||||
|
||||
self.projectiles.insert(
|
||||
collider.clone(),
|
||||
ProjectileWorldObject::new(projectile, rigid_body, collider),
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn collide_projectile_ship(
|
||||
&mut self,
|
||||
res: &mut StepResources,
|
||||
|
@ -152,10 +92,17 @@ impl<'a> World {
|
|||
|
||||
let ship_d = res.dt.get_ship_mut(ship.data_handle).unwrap();
|
||||
|
||||
// TODO: check faction
|
||||
ship_d.apply_damage(projectile.content.damage);
|
||||
let f = res.ct.get_faction(projectile.faction);
|
||||
let r = f.relationships.get(&ship_d.get_faction()).unwrap();
|
||||
let destory_projectile = match r {
|
||||
content::Relationship::Hostile => {
|
||||
ship_d.apply_damage(projectile.content.damage);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if true {
|
||||
if destory_projectile {
|
||||
let pr = self
|
||||
.wrapper
|
||||
.rigid_body_set
|
||||
|
@ -210,14 +157,14 @@ impl<'a> World {
|
|||
}
|
||||
|
||||
// Public methods
|
||||
impl<'a> World {
|
||||
impl World {
|
||||
/// Create a new physics system
|
||||
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();
|
||||
|
||||
let mut w = Self {
|
||||
system,
|
||||
_system: system,
|
||||
wrapper: Wrapper::new(),
|
||||
projectiles: HashMap::new(),
|
||||
ships: HashMap::new(),
|
||||
|
@ -283,45 +230,125 @@ impl<'a> World {
|
|||
);
|
||||
}
|
||||
|
||||
/// Step this physics system by `t` seconds
|
||||
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();
|
||||
/// Run ship updates:
|
||||
/// - updates ship controls (runs behaviors)
|
||||
/// - applies ship controls
|
||||
/// - creates projectiles
|
||||
fn step_ships(&mut self, res: &mut StepResources) {
|
||||
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.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(&mut res, r, c);
|
||||
//if s.controls.guns {
|
||||
// projectiles.push((s.physics_handle, s.ship.fire_guns()));
|
||||
//}
|
||||
|
||||
for (_, ship_object) in &mut self.ships {
|
||||
//if s.remove_from_world() {
|
||||
// to_remove.push(s.physics_handle);
|
||||
// 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);
|
||||
}
|
||||
|
||||
// TODO: unified step info struct
|
||||
ship_object.step(res, rigid_body, collider);
|
||||
if ship_object.controls.guns {
|
||||
let ship_data = res.dt.get_ship_mut(ship_object.data_handle).unwrap();
|
||||
|
||||
// TODO: don't allocate here. This is a hack to satisfy the borrow checker,
|
||||
// convert this to a refcell or do the replace dance.
|
||||
let pairs: Vec<(GunPoint, Option<OutfitHandle>)> = ship_data
|
||||
.get_outfits()
|
||||
.iter_gun_points()
|
||||
.map(|(p, o)| (p.clone(), o.clone()))
|
||||
.collect();
|
||||
|
||||
for (gun, outfit) in pairs {
|
||||
if ship_data.fire_gun(res.ct, &gun) {
|
||||
projectiles.push((
|
||||
ship_object.data_handle,
|
||||
ship_object.rigid_body,
|
||||
gun.clone(),
|
||||
outfit.unwrap(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//for (s, p) in projectiles {
|
||||
// self.add_projectiles(s, p);
|
||||
//}
|
||||
|
||||
// Remove ships that don't exist
|
||||
for s in to_remove {
|
||||
self.remove_ship(s);
|
||||
}
|
||||
|
||||
// Create projectiles
|
||||
for (ship_dat, rigid_body, gun_point, outfit) in projectiles {
|
||||
let mut rng = rand::thread_rng();
|
||||
let rigid_body = self.get_rigid_body(rigid_body).unwrap();
|
||||
|
||||
let ship_dat = res.dt.get_ship(ship_dat).unwrap();
|
||||
let ship_pos = util::rigidbody_position(rigid_body);
|
||||
let ship_rot = util::rigidbody_rotation(rigid_body);
|
||||
let ship_vel = util::rigidbody_velocity(rigid_body);
|
||||
let ship_ang = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 });
|
||||
|
||||
let pos = ship_pos + (Matrix2::from_angle(-ship_ang) * gun_point.pos.to_vec());
|
||||
|
||||
let outfit = res.ct.get_outfit(outfit);
|
||||
let outfit = outfit.gun.as_ref().unwrap();
|
||||
|
||||
let spread: Rad<f32> =
|
||||
Deg(rng.gen_range(-outfit.projectile.angle_rng.0..=outfit.projectile.angle_rng.0))
|
||||
.into();
|
||||
|
||||
let vel = ship_vel
|
||||
+ (Matrix2::from_angle(-ship_ang + spread)
|
||||
* Vector2 {
|
||||
x: 0.0,
|
||||
y: outfit.projectile.speed
|
||||
+ rng.gen_range(
|
||||
-outfit.projectile.speed_rng..=outfit.projectile.speed_rng,
|
||||
),
|
||||
});
|
||||
|
||||
let rigid_body = RigidBodyBuilder::kinematic_velocity_based()
|
||||
.translation(vector![pos.x, pos.y])
|
||||
.rotation(-ship_ang.0)
|
||||
.linvel(vector![vel.x, vel.y])
|
||||
.build();
|
||||
|
||||
let collider = match &outfit.projectile.collider {
|
||||
content::ProjectileCollider::Ball(b) => ColliderBuilder::ball(b.radius)
|
||||
.sensor(true)
|
||||
.active_events(ActiveEvents::COLLISION_EVENTS)
|
||||
.build(),
|
||||
};
|
||||
|
||||
let rigid_body = self.wrapper.rigid_body_set.insert(rigid_body);
|
||||
let collider = self.wrapper.collider_set.insert_with_parent(
|
||||
collider,
|
||||
rigid_body,
|
||||
&mut self.wrapper.rigid_body_set,
|
||||
);
|
||||
|
||||
self.projectiles.insert(
|
||||
collider.clone(),
|
||||
ProjectileWorldObject::new(
|
||||
outfit.projectile.clone(),
|
||||
rigid_body,
|
||||
ship_dat.get_faction(),
|
||||
collider,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Step this physics system by `t` seconds
|
||||
pub fn step(&mut self, mut res: StepResources) {
|
||||
self.step_ships(&mut res);
|
||||
|
||||
// Update physics
|
||||
self.wrapper.step(res.t, &self.collision_handler);
|
||||
|
||||
|
@ -390,10 +417,12 @@ impl<'a> World {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
// Public getters
|
||||
impl World {
|
||||
/// Get a ship physics object
|
||||
pub fn get_ship(&self, ship: GameShipHandle) -> Option<&ShipWorldObject> {
|
||||
self.ships.get(&ship)
|
||||
}
|
||||
|
@ -418,12 +447,12 @@ impl<'a> World {
|
|||
}
|
||||
|
||||
/// Iterate over all ships in this physics system
|
||||
pub fn iter_ships(&'a self) -> impl Iterator<Item = &ShipWorldObject> + '_ {
|
||||
pub fn iter_ships(&self) -> impl Iterator<Item = &ShipWorldObject> + '_ {
|
||||
self.ships.values()
|
||||
}
|
||||
|
||||
/// Iterate over all ships in this physics system
|
||||
pub fn iter_projectiles(&'a self) -> impl Iterator<Item = &ProjectileWorldObject> + '_ {
|
||||
pub fn iter_projectiles(&self) -> impl Iterator<Item = &ProjectileWorldObject> + '_ {
|
||||
self.projectiles.values()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue