Compare commits
3 Commits
7854245a4b
...
33c96a93a5
Author | SHA1 | Date |
---|---|---|
Mark | 33c96a93a5 | |
Mark | 03b6ea5ed7 | |
Mark | 64885a8b6d |
2
TODO.md
2
TODO.md
|
@ -1,10 +1,10 @@
|
||||||
## Specific Jobs
|
## Specific Jobs
|
||||||
|
- Start documenting
|
||||||
- Check for handle leaks
|
- Check for handle leaks
|
||||||
- Rename and crtl-f comments
|
- Rename and crtl-f comments
|
||||||
- gameobject and world
|
- gameobject and world
|
||||||
- behavior and personality
|
- behavior and personality
|
||||||
- ship (content) / ship (data) / ship (world)
|
- ship (content) / ship (data) / ship (world)
|
||||||
- Fix ship controllers
|
|
||||||
- Don't allocate each frame
|
- Don't allocate each frame
|
||||||
|
|
||||||
- UI: text arranger
|
- UI: text arranger
|
||||||
|
|
|
@ -104,7 +104,7 @@ pub struct Object {
|
||||||
|
|
||||||
/// This object's position, in game coordinates,
|
/// This object's position, in game coordinates,
|
||||||
/// relative to the system's center (0, 0).
|
/// relative to the system's center (0, 0).
|
||||||
pub position: Point3<f32>,
|
pub pos: Point3<f32>,
|
||||||
|
|
||||||
/// This object's sprite's angle.
|
/// This object's sprite's angle.
|
||||||
pub angle: Rad<f32>,
|
pub angle: Rad<f32>,
|
||||||
|
@ -200,13 +200,14 @@ impl crate::Build for System {
|
||||||
|
|
||||||
objects.push(Object {
|
objects.push(Object {
|
||||||
sprite: handle,
|
sprite: handle,
|
||||||
position: resolve_position(&system.object, &obj, cycle_detector)
|
pos: resolve_position(&system.object, &obj, cycle_detector)
|
||||||
.with_context(|| format!("In object {:#?}", label))?,
|
.with_context(|| format!("In object {:#?}", label))?,
|
||||||
size: obj.size,
|
size: obj.size,
|
||||||
angle: Deg(obj.angle.unwrap_or(0.0)).into(),
|
angle: Deg(obj.angle.unwrap_or(0.0)).into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objects.sort_by(|a, b| a.pos.z.total_cmp(&b.pos.z));
|
||||||
content.systems.push(Self {
|
content.systems.push(Self {
|
||||||
name: system_name,
|
name: system_name,
|
||||||
objects,
|
objects,
|
||||||
|
|
|
@ -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: 1 }));
|
||||||
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 2 }));
|
s.add_outfit(&ct.get_outfit(content::OutfitHandle { index: 2 }));
|
||||||
|
|
||||||
gamedata.create_ship(
|
let a = gamedata.create_ship(
|
||||||
&ct,
|
&ct,
|
||||||
content::ShipHandle { index: 0 },
|
content::ShipHandle { index: 0 },
|
||||||
content::FactionHandle { index: 1 },
|
content::FactionHandle { index: 1 },
|
||||||
ShipPersonality::Dummy,
|
ShipPersonality::Dummy,
|
||||||
&content::SystemHandle { index: 0 },
|
&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(
|
let a = gamedata.create_ship(
|
||||||
&ct,
|
&ct,
|
||||||
content::ShipHandle { index: 0 },
|
content::ShipHandle { index: 0 },
|
||||||
content::FactionHandle { index: 1 },
|
content::FactionHandle { index: 0 },
|
||||||
ShipPersonality::Point,
|
ShipPersonality::Point,
|
||||||
&content::SystemHandle { index: 0 },
|
&content::SystemHandle { index: 0 },
|
||||||
);
|
);
|
||||||
let s = gamedata.get_ship_mut(a).unwrap();
|
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: 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 });
|
let physics = World::new(&ct, &gamedata, content::SystemHandle { index: 0 });
|
||||||
|
|
||||||
|
@ -151,10 +157,11 @@ impl Game {
|
||||||
camera_zoom: self.camera.zoom,
|
camera_zoom: self.camera.zoom,
|
||||||
current_time: self.start_instant.elapsed().as_secs_f32(),
|
current_time: self.start_instant.elapsed().as_secs_f32(),
|
||||||
content: &self.content,
|
content: &self.content,
|
||||||
world: &self.world,
|
world: &self.world, // TODO: maybe system should be stored here?
|
||||||
particles: &mut self.new_particles,
|
particles: &mut self.new_particles,
|
||||||
player_data: self.player,
|
player_data: self.player,
|
||||||
data: &self.gamedata,
|
data: &self.gamedata,
|
||||||
|
current_system: content::SystemHandle { index: 0 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
mod gamedata;
|
mod gamedata;
|
||||||
mod handles;
|
mod handles;
|
||||||
pub mod ship;
|
pub mod ship;
|
||||||
mod system;
|
|
||||||
|
|
||||||
pub use gamedata::*;
|
pub use gamedata::*;
|
||||||
pub use handles::*;
|
pub use handles::*;
|
||||||
pub use system::{System, SystemObject};
|
|
||||||
|
|
|
@ -56,7 +56,9 @@ impl Ship {
|
||||||
|
|
||||||
/// Add an outfit to this ship
|
/// Add an outfit to this ship
|
||||||
pub fn add_outfit(&mut self, o: &content::Outfit) -> super::OutfitAddResult {
|
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
|
/// Remove an outfit from this ship
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
use cgmath::{Point3, Rad};
|
|
||||||
use galactica_content as content;
|
|
||||||
|
|
||||||
// TODO: rework
|
|
||||||
|
|
||||||
pub struct System {
|
|
||||||
pub name: String,
|
|
||||||
pub bodies: Vec<SystemObject>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl System {
|
|
||||||
pub fn new(ct: &content::Content, handle: content::SystemHandle) -> Self {
|
|
||||||
let sys = ct.get_system(handle);
|
|
||||||
let mut s = System {
|
|
||||||
name: sys.name.clone(),
|
|
||||||
bodies: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for o in &sys.objects {
|
|
||||||
s.bodies.push(SystemObject {
|
|
||||||
pos: o.position,
|
|
||||||
sprite: o.sprite,
|
|
||||||
size: o.size,
|
|
||||||
angle: o.angle,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SystemObject {
|
|
||||||
pub sprite: content::SpriteHandle,
|
|
||||||
pub pos: Point3<f32>,
|
|
||||||
pub size: f32,
|
|
||||||
pub angle: Rad<f32>,
|
|
||||||
}
|
|
|
@ -19,7 +19,7 @@ impl GPUState {
|
||||||
let radar_size = 300.0;
|
let radar_size = 300.0;
|
||||||
let hide_range = 0.85;
|
let hide_range = 0.85;
|
||||||
let shrink_distance = 20.0;
|
let shrink_distance = 20.0;
|
||||||
//let system_object_scale = 1.0 / 600.0;
|
let system_object_scale = 1.0 / 600.0;
|
||||||
let ship_scale = 1.0 / 10.0;
|
let ship_scale = 1.0 / 10.0;
|
||||||
|
|
||||||
let player_world_object = state.world.get_ship(state.player_data).unwrap();
|
let player_world_object = state.world.get_ship(state.player_data).unwrap();
|
||||||
|
@ -29,7 +29,7 @@ impl GPUState {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let player_position = util::rigidbody_position(player_body);
|
let player_position = util::rigidbody_position(player_body);
|
||||||
//let planet_sprite = state.content.get_sprite_handle("ui::planetblip");
|
let planet_sprite = state.content.get_sprite_handle("ui::planetblip");
|
||||||
let ship_sprite = state.content.get_sprite_handle("ui::shipblip");
|
let ship_sprite = state.content.get_sprite_handle("ui::shipblip");
|
||||||
let arrow_sprite = state.content.get_sprite_handle("ui::centerarrow");
|
let arrow_sprite = state.content.get_sprite_handle("ui::centerarrow");
|
||||||
|
|
||||||
|
@ -54,9 +54,9 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
self.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
/*
|
|
||||||
// Draw system objects
|
// Draw system objects
|
||||||
for o in &system.bodies {
|
let system = state.content.get_system(state.current_system);
|
||||||
|
for o in &system.objects {
|
||||||
let size = (o.size / o.pos.z) / (radar_range * system_object_scale);
|
let size = (o.size / o.pos.z) / (radar_range * system_object_scale);
|
||||||
let p = Point2 {
|
let p = Point2 {
|
||||||
x: o.pos.x,
|
x: o.pos.x,
|
||||||
|
@ -72,24 +72,35 @@ impl GPUState {
|
||||||
// Don't draw super tiny sprites, they flicker
|
// Don't draw super tiny sprites, they flicker
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out.push(UiSprite {
|
|
||||||
sprite: planet_sprite,
|
// Enforce buffer limit
|
||||||
pos: AnchoredUiPosition::NwC(
|
if self.vertex_buffers.ui_counter as u64
|
||||||
Point2 {
|
> galactica_constants::UI_SPRITE_INSTANCE_LIMIT
|
||||||
|
{
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push this object's instance
|
||||||
|
self.queue.write_buffer(
|
||||||
|
&self.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
|
position: (Point2 {
|
||||||
x: radar_size / 2.0 + 10.0,
|
x: radar_size / 2.0 + 10.0,
|
||||||
y: radar_size / -2.0 - 10.0,
|
y: radar_size / -2.0 - 10.0,
|
||||||
} + (d * (radar_size / 2.0)),
|
} + (d * (radar_size / 2.0)))
|
||||||
),
|
.into(),
|
||||||
dimensions: Point2 {
|
angle: o.angle.0,
|
||||||
x: planet_sprite.aspect,
|
size,
|
||||||
y: 1.0,
|
color: [0.5, 0.5, 0.5, 1.0],
|
||||||
} * size,
|
sprite_index: planet_sprite.get_index(),
|
||||||
angle: o.angle,
|
}]),
|
||||||
color: Some([0.5, 0.5, 0.5, 1.0]),
|
);
|
||||||
});
|
self.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Draw ships
|
// Draw ships
|
||||||
for (s, r) in state.world.iter_ship_body() {
|
for (s, r) in state.world.iter_ship_body() {
|
||||||
|
@ -123,6 +134,7 @@ impl GPUState {
|
||||||
} + (d * (radar_size / 2.0));
|
} + (d * (radar_size / 2.0));
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
|
// TODO: cleaner solution. don't do this everywhere.
|
||||||
if self.vertex_buffers.ui_counter as u64
|
if self.vertex_buffers.ui_counter as u64
|
||||||
> galactica_constants::UI_SPRITE_INSTANCE_LIMIT
|
> galactica_constants::UI_SPRITE_INSTANCE_LIMIT
|
||||||
{
|
{
|
||||||
|
|
|
@ -364,13 +364,9 @@ impl GPUState {
|
||||||
// Order matters, it determines what is drawn on top.
|
// Order matters, it determines what is drawn on top.
|
||||||
// The order inside ships and projectiles doesn't matter,
|
// The order inside ships and projectiles doesn't matter,
|
||||||
// but ships should always be under projectiles.
|
// but ships should always be under projectiles.
|
||||||
for s in state.world.iter_ships() {
|
self.world_push_system(state, (clip_ne, clip_sw));
|
||||||
self.world_push_ship(state, (clip_ne, clip_sw), &s);
|
self.world_push_ship(state, (clip_ne, clip_sw));
|
||||||
}
|
self.world_push_projectile(state, (clip_ne, clip_sw));
|
||||||
|
|
||||||
for p in state.world.iter_projectiles() {
|
|
||||||
self.world_push_projectile(state, (clip_ne, clip_sw), &p);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.hud_add_radar(state);
|
self.hud_add_radar(state);
|
||||||
self.hud_add_status(state);
|
self.hud_add_status(state);
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
|
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
|
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
|
||||||
use galactica_world::{
|
use galactica_world::util;
|
||||||
objects::{ProjectileWorldObject, ShipWorldObject},
|
|
||||||
util,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
globaluniform::ObjectData,
|
globaluniform::ObjectData,
|
||||||
|
@ -19,8 +16,8 @@ impl GPUState {
|
||||||
state: &RenderState,
|
state: &RenderState,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
s: &ShipWorldObject,
|
|
||||||
) {
|
) {
|
||||||
|
for s in state.world.iter_ships() {
|
||||||
let r = state.world.get_rigid_body(s.rigid_body).unwrap();
|
let r = state.world.get_rigid_body(s.rigid_body).unwrap();
|
||||||
let ship_pos = util::rigidbody_position(&r);
|
let ship_pos = util::rigidbody_position(&r);
|
||||||
let ship_rot = util::rigidbody_rotation(r);
|
let ship_rot = util::rigidbody_rotation(r);
|
||||||
|
@ -45,7 +42,7 @@ impl GPUState {
|
||||||
|| pos.x > screen_clip.1.x + m
|
|| pos.x > screen_clip.1.x + m
|
||||||
|| pos.y < screen_clip.1.y - m
|
|| pos.y < screen_clip.1.y - m
|
||||||
{
|
{
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.vertex_buffers.object_counter;
|
let idx = self.vertex_buffers.object_counter;
|
||||||
|
@ -88,7 +85,7 @@ impl GPUState {
|
||||||
// (physics object stays around to complete the death animation)
|
// (physics object stays around to complete the death animation)
|
||||||
// If that is the case, we're done, no flares to draw anyway!
|
// If that is the case, we're done, no flares to draw anyway!
|
||||||
let ship = match state.data.get_ship(s.data_handle) {
|
let ship = match state.data.get_ship(s.data_handle) {
|
||||||
None => return,
|
None => continue,
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,14 +127,15 @@ impl GPUState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn world_push_projectile(
|
pub(super) fn world_push_projectile(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &RenderState,
|
state: &RenderState,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
p: &ProjectileWorldObject,
|
|
||||||
) {
|
) {
|
||||||
|
for p in state.world.iter_projectiles() {
|
||||||
let r = state.world.get_rigid_body(p.rigid_body).unwrap();
|
let r = state.world.get_rigid_body(p.rigid_body).unwrap();
|
||||||
let proj_pos = util::rigidbody_position(&r);
|
let proj_pos = util::rigidbody_position(&r);
|
||||||
let proj_rot = util::rigidbody_rotation(r);
|
let proj_rot = util::rigidbody_rotation(r);
|
||||||
|
@ -162,7 +160,7 @@ impl GPUState {
|
||||||
|| pos.x > screen_clip.1.x + m
|
|| pos.x > screen_clip.1.x + m
|
||||||
|| pos.y < screen_clip.1.y - m
|
|| pos.y < screen_clip.1.y - m
|
||||||
{
|
{
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.vertex_buffers.object_counter;
|
let idx = self.vertex_buffers.object_counter;
|
||||||
|
@ -202,3 +200,74 @@ impl GPUState {
|
||||||
self.vertex_buffers.object_counter += 1;
|
self.vertex_buffers.object_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn world_push_system(
|
||||||
|
&mut self,
|
||||||
|
state: &RenderState,
|
||||||
|
// NE and SW corners of screen
|
||||||
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
|
) {
|
||||||
|
let system = state.content.get_system(state.current_system);
|
||||||
|
|
||||||
|
for o in &system.objects {
|
||||||
|
// Position adjusted for parallax
|
||||||
|
let pos: Point2<f32> = (Point2 {
|
||||||
|
x: o.pos.x,
|
||||||
|
y: o.pos.y,
|
||||||
|
} - state.camera_pos.to_vec())
|
||||||
|
/ o.pos.z;
|
||||||
|
|
||||||
|
// Game dimensions of this sprite post-scale.
|
||||||
|
// Post-scale width or height, whichever is larger.
|
||||||
|
// This is in game units.
|
||||||
|
//
|
||||||
|
// We take the maximum to account for rotated sprites.
|
||||||
|
let m = (o.size / o.pos.z) * o.sprite.aspect.max(1.0);
|
||||||
|
|
||||||
|
// Don't draw sprites that are off the screen
|
||||||
|
if pos.x < screen_clip.0.x - m
|
||||||
|
|| pos.y > screen_clip.0.y + m
|
||||||
|
|| pos.x > screen_clip.1.x + m
|
||||||
|
|| pos.y < screen_clip.1.y - m
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = self.vertex_buffers.object_counter;
|
||||||
|
// Write this object's location data
|
||||||
|
self.queue.write_buffer(
|
||||||
|
&self.global_uniform.object_buffer,
|
||||||
|
ObjectData::SIZE * idx as u64,
|
||||||
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
|
xpos: o.pos.x,
|
||||||
|
ypos: o.pos.y,
|
||||||
|
zpos: o.pos.z,
|
||||||
|
angle: o.angle.0,
|
||||||
|
size: o.size,
|
||||||
|
parent: 0,
|
||||||
|
is_child: 0,
|
||||||
|
_padding: Default::default(),
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enforce buffer limit
|
||||||
|
if self.vertex_buffers.object_counter as u64
|
||||||
|
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT
|
||||||
|
{
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("Sprite limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push this object's instance
|
||||||
|
self.queue.write_buffer(
|
||||||
|
&self.vertex_buffers.object.instances,
|
||||||
|
ObjectInstance::SIZE * self.vertex_buffers.object_counter,
|
||||||
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
|
sprite_index: o.sprite.get_index(),
|
||||||
|
object_index: idx as u32,
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
self.vertex_buffers.object_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use galactica_content::Content;
|
use galactica_content::{Content, SystemHandle};
|
||||||
use galactica_gameobject::{GameData, GameShipHandle};
|
use galactica_gameobject::{GameData, GameShipHandle};
|
||||||
use galactica_world::{ParticleBuilder, World};
|
use galactica_world::{ParticleBuilder, World};
|
||||||
|
|
||||||
|
@ -11,6 +11,9 @@ pub struct RenderState<'a> {
|
||||||
/// Player ship data
|
/// Player ship data
|
||||||
pub player_data: GameShipHandle,
|
pub player_data: GameShipHandle,
|
||||||
|
|
||||||
|
/// The system we're currently in
|
||||||
|
pub current_system: SystemHandle,
|
||||||
|
|
||||||
/// Height of screen, in world units
|
/// Height of screen, in world units
|
||||||
pub camera_zoom: f32,
|
pub camera_zoom: f32,
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
//! Various implementations of [`crate::ShipBehavior`]
|
//! Various implementations of [`crate::ShipBehavior`]
|
||||||
|
|
||||||
mod null;
|
mod null;
|
||||||
//mod point;
|
mod point;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use galactica_gameobject::GameShipHandle;
|
||||||
pub use null::*;
|
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
|
/// Main behavior trait. Any struct that implements this
|
||||||
/// may be used to control a ship.
|
/// may be used to control a ship.
|
||||||
|
@ -14,5 +21,12 @@ pub trait ShipBehavior {
|
||||||
/// Update a ship's controls based on world state.
|
/// Update a ship's controls based on world state.
|
||||||
/// This method does not return anything, it modifies
|
/// This method does not return anything, it modifies
|
||||||
/// the ship's controls in-place.
|
/// 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 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.
|
/// The Null behaviors is assigned to objects that are not controlled by the computer.
|
||||||
/// Most notably, the player's ship has a Null behavior.
|
/// Most notably, the player's ship has a Null behavior.
|
||||||
|
@ -13,7 +21,14 @@ impl Null {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShipBehavior for 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()
|
ShipControls::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
use cgmath::{Deg, InnerSpace};
|
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 galactica_content as content;
|
||||||
|
use galactica_gameobject::GameShipHandle;
|
||||||
use crate::World;
|
|
||||||
|
|
||||||
use super::ShipBehavior;
|
use super::ShipBehavior;
|
||||||
|
|
||||||
|
@ -21,43 +25,47 @@ impl Point {
|
||||||
impl ShipBehavior for Point {
|
impl ShipBehavior for Point {
|
||||||
fn update_controls(
|
fn update_controls(
|
||||||
&mut self,
|
&mut self,
|
||||||
physics: &mut World,
|
res: &StepResources,
|
||||||
content: &content::Content,
|
rigid_bodies: &RigidBodySet,
|
||||||
data: &GameData,
|
ships: &HashMap<GameShipHandle, ShipWorldObject>,
|
||||||
) {
|
this_ship: RigidBodyHandle,
|
||||||
// Turn off all controls
|
this_data: GameShipHandle,
|
||||||
let s = physics.get_ship_mut(&self.handle).unwrap();
|
) -> ShipControls {
|
||||||
s.controls.left = false;
|
let mut controls = ShipControls::new();
|
||||||
s.controls.right = false;
|
|
||||||
s.controls.guns = false;
|
|
||||||
s.controls.thrust = false;
|
|
||||||
|
|
||||||
let (my_s, my_r) = physics.get_ship_body(self.handle).unwrap();
|
let this_rigidbody = rigid_bodies.get(this_ship).unwrap();
|
||||||
let my_data = data.get_ship(my_s.data_handle).unwrap();
|
let my_data = res.dt.get_ship(this_data).unwrap();
|
||||||
let my_position = util::rigidbody_position(my_r);
|
let my_position = util::rigidbody_position(this_rigidbody);
|
||||||
let my_rotation = util::rigidbody_rotation(my_r);
|
let my_rotation = util::rigidbody_rotation(this_rigidbody);
|
||||||
let my_angvel = my_r.angvel();
|
let my_angvel = this_rigidbody.angvel();
|
||||||
let my_faction = content.get_faction(my_data.get_faction());
|
let my_faction = res.ct.get_faction(my_data.get_faction());
|
||||||
|
|
||||||
// Iterate all possible targets
|
// Iterate all possible targets
|
||||||
let mut it = physics
|
let mut hostile_ships = ships
|
||||||
.iter_ship_body()
|
.values()
|
||||||
.filter(|(s, _)| {
|
.filter(|s| {
|
||||||
let data = data.get_ship(s.data_handle).unwrap();
|
let data = res.dt.get_ship(s.data_handle);
|
||||||
|
if let Some(data) = data {
|
||||||
match my_faction.relationships.get(&data.get_faction()).unwrap() {
|
match my_faction.relationships.get(&data.get_faction()).unwrap() {
|
||||||
content::Relationship::Hostile => true,
|
content::Relationship::Hostile => true,
|
||||||
_ => false,
|
_ => 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
|
// 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),
|
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();
|
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 p = util::rigidbody_position(r);
|
||||||
let new_d = (my_position - p).magnitude();
|
let new_d = (my_position - p).magnitude();
|
||||||
if new_d < d {
|
if new_d < d {
|
||||||
|
@ -70,17 +78,13 @@ impl ShipBehavior for Point {
|
||||||
.angle(closest_enemy_position - my_position)
|
.angle(closest_enemy_position - my_position)
|
||||||
.into();
|
.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 {
|
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 {
|
} else if angle_delta > Deg(0.0) && my_angvel < 0.3 {
|
||||||
s.controls.left = true;
|
controls.left = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.controls.guns = true;
|
controls.guns = true;
|
||||||
s.controls.thrust = false;
|
return controls;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
|
||||||
use content::{FactionHandle, ShipHandle};
|
use content::{FactionHandle, ShipHandle};
|
||||||
use nalgebra::{point, vector};
|
use nalgebra::{point, vector};
|
||||||
|
|
||||||
use object::GameShipHandle;
|
use object::GameShipHandle;
|
||||||
use rand::{rngs::ThreadRng, Rng};
|
use rand::{rngs::ThreadRng, Rng};
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
|
@ -9,7 +8,7 @@ use rapier2d::{
|
||||||
geometry::{Collider, ColliderHandle},
|
geometry::{Collider, ColliderHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{behavior::ShipBehavior, util, ParticleBuilder, StepResources};
|
use crate::{util, ParticleBuilder, StepResources};
|
||||||
use galactica_content as content;
|
use galactica_content as content;
|
||||||
use galactica_gameobject as object;
|
use galactica_gameobject as object;
|
||||||
|
|
||||||
|
@ -41,6 +40,7 @@ impl ShipControls {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct ShipCollapseSequence {
|
struct ShipCollapseSequence {
|
||||||
total_length: f32,
|
total_length: f32,
|
||||||
time_elapsed: f32,
|
time_elapsed: f32,
|
||||||
|
@ -181,6 +181,7 @@ impl ShipCollapseSequence {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A ship instance in the physics system
|
/// A ship instance in the physics system
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ShipWorldObject {
|
pub struct ShipWorldObject {
|
||||||
/// This ship's physics handle
|
/// This ship's physics handle
|
||||||
pub rigid_body: RigidBodyHandle,
|
pub rigid_body: RigidBodyHandle,
|
||||||
|
@ -194,9 +195,6 @@ pub struct ShipWorldObject {
|
||||||
/// This ship's controls
|
/// This ship's controls
|
||||||
pub(crate) controls: ShipControls,
|
pub(crate) controls: ShipControls,
|
||||||
|
|
||||||
/// This ship's behavior
|
|
||||||
behavior: Box<dyn ShipBehavior>,
|
|
||||||
|
|
||||||
/// This ship's collapse sequence
|
/// This ship's collapse sequence
|
||||||
collapse_sequence: ShipCollapseSequence,
|
collapse_sequence: ShipCollapseSequence,
|
||||||
|
|
||||||
|
@ -209,10 +207,9 @@ pub struct ShipWorldObject {
|
||||||
|
|
||||||
impl ShipWorldObject {
|
impl ShipWorldObject {
|
||||||
/// Make a new ship
|
/// Make a new ship
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
ct: &content::Content,
|
ct: &content::Content,
|
||||||
data_handle: GameShipHandle,
|
data_handle: GameShipHandle,
|
||||||
behavior: Box<dyn ShipBehavior>,
|
|
||||||
faction: FactionHandle,
|
faction: FactionHandle,
|
||||||
rigid_body: RigidBodyHandle,
|
rigid_body: RigidBodyHandle,
|
||||||
collider: ColliderHandle,
|
collider: ColliderHandle,
|
||||||
|
@ -222,18 +219,12 @@ impl ShipWorldObject {
|
||||||
rigid_body,
|
rigid_body,
|
||||||
collider,
|
collider,
|
||||||
data_handle,
|
data_handle,
|
||||||
behavior,
|
|
||||||
controls: ShipControls::new(),
|
controls: ShipControls::new(),
|
||||||
faction,
|
faction,
|
||||||
collapse_sequence: ShipCollapseSequence::new(ship_content.collapse.length),
|
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.
|
/// If this is true, remove this ship from the physics system.
|
||||||
pub fn should_be_removed(&self) -> bool {
|
pub fn should_be_removed(&self) -> bool {
|
||||||
self.collapse_sequence.is_done()
|
self.collapse_sequence.is_done()
|
||||||
|
|
|
@ -12,7 +12,8 @@ use rapier2d::{
|
||||||
use std::{collections::HashMap, f32::consts::PI};
|
use std::{collections::HashMap, f32::consts::PI};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
behavior, objects,
|
behavior::{self, ShipBehavior},
|
||||||
|
objects,
|
||||||
objects::{ProjectileWorldObject, ShipWorldObject},
|
objects::{ProjectileWorldObject, ShipWorldObject},
|
||||||
util,
|
util,
|
||||||
wrapper::Wrapper,
|
wrapper::Wrapper,
|
||||||
|
@ -29,6 +30,7 @@ pub struct World {
|
||||||
wrapper: Wrapper,
|
wrapper: Wrapper,
|
||||||
projectiles: HashMap<ColliderHandle, objects::ProjectileWorldObject>,
|
projectiles: HashMap<ColliderHandle, objects::ProjectileWorldObject>,
|
||||||
ships: HashMap<GameShipHandle, objects::ShipWorldObject>,
|
ships: HashMap<GameShipHandle, objects::ShipWorldObject>,
|
||||||
|
ship_behaviors: HashMap<GameShipHandle, Box<dyn ShipBehavior>>,
|
||||||
collider_ship_table: HashMap<ColliderHandle, GameShipHandle>,
|
collider_ship_table: HashMap<ColliderHandle, GameShipHandle>,
|
||||||
|
|
||||||
collision_handler: ChannelEventCollector,
|
collision_handler: ChannelEventCollector,
|
||||||
|
@ -79,6 +81,7 @@ impl<'a> World {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
let h = self.collider_ship_table.remove(&s.collider).unwrap();
|
let h = self.collider_ship_table.remove(&s.collider).unwrap();
|
||||||
|
self.ship_behaviors.remove(&h);
|
||||||
self.ships.remove(&h);
|
self.ships.remove(&h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +180,7 @@ impl World {
|
||||||
wrapper: Wrapper::new(),
|
wrapper: Wrapper::new(),
|
||||||
projectiles: HashMap::new(),
|
projectiles: HashMap::new(),
|
||||||
ships: HashMap::new(),
|
ships: HashMap::new(),
|
||||||
|
ship_behaviors: HashMap::new(),
|
||||||
collider_ship_table: HashMap::new(),
|
collider_ship_table: HashMap::new(),
|
||||||
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
|
collision_handler: ChannelEventCollector::new(collision_send, contact_force_send),
|
||||||
collision_queue,
|
collision_queue,
|
||||||
|
@ -227,16 +231,11 @@ impl World {
|
||||||
);
|
);
|
||||||
|
|
||||||
self.collider_ship_table.insert(c, ship.get_handle());
|
self.collider_ship_table.insert(c, ship.get_handle());
|
||||||
|
self.ship_behaviors
|
||||||
|
.insert(ship.get_handle(), Box::new(behavior::Point::new()));
|
||||||
self.ships.insert(
|
self.ships.insert(
|
||||||
ship.get_handle(),
|
ship.get_handle(),
|
||||||
objects::ShipWorldObject::new(
|
objects::ShipWorldObject::new(ct, ship.get_handle(), ship.get_faction(), r, c),
|
||||||
ct,
|
|
||||||
ship.get_handle(),
|
|
||||||
Box::new(behavior::Null::new()),
|
|
||||||
ship.get_faction(),
|
|
||||||
r,
|
|
||||||
c,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,25 +244,57 @@ impl World {
|
||||||
/// - applies ship controls
|
/// - applies ship controls
|
||||||
/// - creates projectiles
|
/// - creates projectiles
|
||||||
fn step_ships(&mut self, res: &mut StepResources) {
|
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 projectiles = Vec::new();
|
||||||
let mut to_remove = 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() {
|
if ship_object.should_be_removed() {
|
||||||
to_remove.push(ship_object.collider);
|
to_remove.push(ship_object.collider);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rigid_body = &mut self.wrapper.rigid_body_set[ship_object.rigid_body];
|
// Short-circuit continue if this ship isn't in game data
|
||||||
let collider = &mut self.wrapper.collider_set[ship_object.collider];
|
// (which means it's playing a collapse sequence)
|
||||||
|
if res.dt.get_ship(*handle).is_none() {
|
||||||
if ship_object.data_handle == res.player {
|
let ship_object = self.ships.get_mut(handle).unwrap();
|
||||||
ship_object.controls = res.player_controls.clone();
|
ship_object.step(
|
||||||
} else {
|
res,
|
||||||
ship_object.update_controls(&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
|
// Compute new controls
|
||||||
ship_object.step(res, rigid_body, collider);
|
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 {
|
if ship_object.controls.guns {
|
||||||
let ship_data = res.dt.get_ship_mut(ship_object.data_handle).unwrap();
|
let ship_data = res.dt.get_ship_mut(ship_object.data_handle).unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue