Compare commits

...

2 Commits

Author SHA1 Message Date
Mark eaa8a7383c
Renamed a few objects 2024-01-09 21:45:30 -08:00
Mark 5ceff9f039
Updated TODO 2024-01-09 21:45:13 -08:00
36 changed files with 399 additions and 420 deletions

14
Cargo.lock generated
View File

@ -581,9 +581,9 @@ dependencies = [
"cgmath",
"galactica-constants",
"galactica-content",
"galactica-gameobject",
"galactica-galaxy",
"galactica-render",
"galactica-world",
"galactica-systemsim",
"pollster",
"wgpu",
"winit",
@ -608,7 +608,7 @@ dependencies = [
]
[[package]]
name = "galactica-gameobject"
name = "galactica-galaxy"
version = "0.0.0"
dependencies = [
"cgmath",
@ -637,9 +637,9 @@ dependencies = [
"cgmath",
"galactica-constants",
"galactica-content",
"galactica-gameobject",
"galactica-galaxy",
"galactica-packer",
"galactica-world",
"galactica-systemsim",
"image",
"rand",
"wgpu",
@ -647,13 +647,13 @@ dependencies = [
]
[[package]]
name = "galactica-world"
name = "galactica-systemsim"
version = "0.0.0"
dependencies = [
"cgmath",
"crossbeam",
"galactica-content",
"galactica-gameobject",
"galactica-galaxy",
"nalgebra",
"rand",
"rapier2d",

View File

@ -45,8 +45,8 @@ readme = ""
galactica-constants = { path = "crates/constants" }
galactica-content = { path = "crates/content" }
galactica-render = { path = "crates/render" }
galactica-world = { path = "crates/world" }
galactica-gameobject = { path = "crates/gameobject" }
galactica-systemsim = { path = "crates/systemsim" }
galactica-galaxy = { path = "crates/galaxy" }
galactica-packer = { path = "crates/packer" }
galactica = { path = "crates/galactica" }

View File

@ -1,12 +1,7 @@
## Specific Jobs
- Start documenting
- Check for handle leaks
- Rename and crtl-f comments
- gameobject and world
- behavior and personality
- ship (content) / ship (data) / ship (world)
- Don't allocate each frame
- UI: text arranger
- Sound system
- Ship death debris

View File

@ -24,8 +24,8 @@ workspace = true
galactica-content = { workspace = true }
galactica-render = { workspace = true }
galactica-constants = { workspace = true }
galactica-world = { workspace = true }
galactica-gameobject = { workspace = true }
galactica-systemsim = { workspace = true }
galactica-galaxy = { workspace = true }
winit = { workspace = true }
wgpu = { workspace = true }

View File

@ -1,4 +1,5 @@
use object::{ship::ShipPersonality, GameData, GameShipHandle};
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle};
use std::time::Instant;
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
@ -6,70 +7,66 @@ use crate::camera::Camera;
use crate::inputstatus::InputStatus;
use galactica_constants;
use galactica_content as content;
use galactica_gameobject as object;
use galactica_render::RenderState;
use galactica_world::{objects::ShipControls, util, ParticleBuilder, StepResources, World};
use galactica_systemsim::{objects::ShipControls, util, ParticleBuilder, StepResources, SystemSim};
pub struct Game {
input: InputStatus,
last_update: Instant,
player: GameShipHandle,
player: GxShipHandle,
paused: bool,
time_scale: f32,
start_instant: Instant,
camera: Camera,
// TODO: include system in world
//system: object::System,
gamedata: GameData,
galaxy: Galaxy,
content: Content,
systemsim: SystemSim,
content: content::Content,
world: World,
new_particles: Vec<ParticleBuilder>,
}
impl Game {
pub fn new(ct: content::Content) -> Self {
let mut gamedata = GameData::new(&ct);
pub fn new(ct: Content) -> Self {
let mut galaxy = Galaxy::new(&ct);
let player = gamedata.create_ship(
let player = galaxy.create_ship(
&ct,
content::ShipHandle { index: 0 },
content::FactionHandle { index: 0 },
ShipHandle { index: 0 },
FactionHandle { index: 0 },
ShipPersonality::Player,
&content::SystemHandle { index: 0 },
&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 }));
let s = galaxy.get_ship_mut(player).unwrap();
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 }));
let a = gamedata.create_ship(
let a = galaxy.create_ship(
&ct,
content::ShipHandle { index: 0 },
content::FactionHandle { index: 1 },
ShipHandle { index: 0 },
FactionHandle { index: 1 },
ShipPersonality::Dummy,
&content::SystemHandle { index: 0 },
&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 s = galaxy.get_ship_mut(a).unwrap();
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 }));
let a = gamedata.create_ship(
let a = galaxy.create_ship(
&ct,
content::ShipHandle { index: 0 },
content::FactionHandle { index: 0 },
ShipHandle { index: 0 },
FactionHandle { index: 0 },
ShipPersonality::Point,
&content::SystemHandle { index: 0 },
&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 s = galaxy.get_ship_mut(a).unwrap();
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 0 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 1 }));
s.add_outfit(&ct.get_outfit(OutfitHandle { index: 2 }));
let physics = World::new(&ct, &gamedata, content::SystemHandle { index: 0 });
let physics = SystemSim::new(&ct, &galaxy, SystemHandle { index: 0 });
Game {
last_update: Instant::now(),
@ -85,8 +82,8 @@ impl Game {
//system: object::System::new(&ct, SystemHandle { index: 0 }),
paused: false,
time_scale: 1.0,
world: physics,
gamedata,
systemsim: physics,
galaxy,
content: ct,
new_particles: Vec::new(),
}
@ -120,9 +117,9 @@ impl Game {
pub fn update(&mut self) {
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
self.gamedata.step(t);
self.galaxy.step(t);
self.world.step(StepResources {
self.systemsim.step(StepResources {
player: self.player,
player_controls: ShipControls {
left: self.input.key_left,
@ -131,7 +128,7 @@ impl Game {
guns: self.input.key_guns,
},
ct: &self.content,
dt: &mut self.gamedata,
gx: &mut self.galaxy,
particles: &mut self.new_particles,
t,
});
@ -143,8 +140,8 @@ impl Game {
}
self.camera.pos = {
let o = self.world.get_ship(self.player).unwrap();
let r = self.world.get_rigid_body(o.rigid_body).unwrap();
let o = self.systemsim.get_ship(self.player).unwrap();
let r = self.systemsim.get_rigid_body(o.rigid_body).unwrap();
util::rigidbody_position(r)
};
@ -157,11 +154,11 @@ impl Game {
camera_zoom: self.camera.zoom,
current_time: self.start_instant.elapsed().as_secs_f32(),
content: &self.content,
world: &self.world, // TODO: maybe system should be stored here?
systemsim: &self.systemsim, // TODO: maybe system should be stored here?
particles: &mut self.new_particles,
player_data: self.player,
data: &self.gamedata,
current_system: content::SystemHandle { index: 0 },
data: &self.galaxy,
current_system: SystemHandle { index: 0 },
}
}
}

View File

@ -2,10 +2,9 @@ mod camera;
mod game;
mod inputstatus;
pub use galactica_content as content;
use anyhow::{bail, Result};
use galactica_constants::{self, ASSET_CACHE};
use galactica_constants::{ASSET_CACHE, CONTENT_ROOT, IMAGE_ROOT, STARFIELD_SPRITE_NAME};
use galactica_content::Content;
use std::{
fs,
path::{Path, PathBuf},
@ -27,11 +26,11 @@ fn main() -> Result<()> {
}
// TODO: pretty error if missing
let content = content::Content::load_dir(
PathBuf::from(galactica_constants::CONTENT_ROOT),
PathBuf::from(galactica_constants::IMAGE_ROOT),
let content = Content::load_dir(
PathBuf::from(CONTENT_ROOT),
PathBuf::from(IMAGE_ROOT),
atlas_index,
galactica_constants::STARFIELD_SPRITE_NAME.to_owned(),
STARFIELD_SPRITE_NAME.to_owned(),
)?;
let event_loop = EventLoop::new();

View File

@ -1,5 +1,5 @@
[package]
name = "galactica-gameobject"
name = "galactica-galaxy"
description = "Galactica's game data manager"
categories = { workspace = true }
keywords = { workspace = true }

View File

@ -1,30 +1,30 @@
use std::collections::HashMap;
use crate::{handles::GameShipHandle, ship::Ship, ship::ShipPersonality};
use galactica_content as content;
use crate::{handles::GxShipHandle, ship::GxShip, ship::ShipPersonality};
use galactica_content::{Content, FactionHandle, ShipHandle, SystemHandle};
/// Keeps track of all objects in the galaxy.
/// This struct does NO physics, it keeps track of data exclusively.
#[derive(Debug)]
pub struct GameData {
pub struct Galaxy {
/// Universal counter.
/// Used to create unique handles for game objects.
index: u64,
/// All ships in the galaxy
ships: HashMap<GameShipHandle, Ship>,
ships: HashMap<GxShipHandle, GxShip>,
/// Ships indexed by the system they're in.
/// A ship must always be in exactly one system.
system_ship_table: HashMap<content::SystemHandle, Vec<GameShipHandle>>,
system_ship_table: HashMap<SystemHandle, Vec<GxShipHandle>>,
/// Systems indexed by which ships they contain.
/// A ship must always be in exactly one system.
ship_system_table: HashMap<GameShipHandle, content::SystemHandle>,
ship_system_table: HashMap<GxShipHandle, SystemHandle>,
}
impl GameData {
pub fn new(ct: &content::Content) -> Self {
impl Galaxy {
pub fn new(ct: &Content) -> Self {
Self {
system_ship_table: ct.iter_systems().map(|s| (s, Vec::new())).collect(),
ship_system_table: HashMap::new(),
@ -36,20 +36,20 @@ impl GameData {
/// Spawn a ship
pub fn create_ship(
&mut self,
ct: &content::Content,
ship: content::ShipHandle,
faction: content::FactionHandle,
ct: &Content,
ship: ShipHandle,
faction: FactionHandle,
personality: ShipPersonality,
system: &content::SystemHandle,
) -> GameShipHandle {
let handle = GameShipHandle {
system: &SystemHandle,
) -> GxShipHandle {
let handle = GxShipHandle {
index: self.index,
content: ship,
};
self.index += 1;
self.ships
.insert(handle, Ship::new(ct, handle, ship, faction, personality));
.insert(handle, GxShip::new(ct, handle, ship, faction, personality));
self.system_ship_table.get_mut(system).unwrap().push(handle);
self.ship_system_table.insert(handle, *system);
@ -59,7 +59,7 @@ impl GameData {
pub fn step(&mut self, t: f32) {
// TODO: don't allocate on step, need a better
// way to satisfy the borrow checker.
// Same needs to be done in the `world` crate.
// Same needs to be done in the `systemsim` crate.
let mut to_remove = Vec::new();
for (_, s) in &mut self.ships {
s.step(t);
@ -83,16 +83,16 @@ impl GameData {
}
// Public getters
impl GameData {
pub fn get_ship(&self, handle: GameShipHandle) -> Option<&Ship> {
impl Galaxy {
pub fn get_ship(&self, handle: GxShipHandle) -> Option<&GxShip> {
self.ships.get(&handle)
}
pub fn get_ship_mut(&mut self, handle: GameShipHandle) -> Option<&mut Ship> {
pub fn get_ship_mut(&mut self, handle: GxShipHandle) -> Option<&mut GxShip> {
self.ships.get_mut(&handle)
}
pub fn iter_ships(&self) -> impl Iterator<Item = &Ship> {
pub fn iter_ships(&self) -> impl Iterator<Item = &GxShip> {
self.ships.values()
}
}

View File

@ -4,7 +4,7 @@ use galactica_content::ShipHandle;
/// A lightweight representation of a ship in the galaxy
#[derive(Debug, Clone, Copy)]
pub struct GameShipHandle {
pub struct GxShipHandle {
/// This ship's unique index
pub(crate) index: u64,
@ -13,20 +13,20 @@ pub struct GameShipHandle {
pub(crate) content: ShipHandle,
}
impl GameShipHandle {
impl GxShipHandle {
pub fn content_handle(&self) -> ShipHandle {
self.content
}
}
impl Hash for GameShipHandle {
impl Hash for GxShipHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.index.hash(state)
}
}
impl Eq for GameShipHandle {}
impl PartialEq for GameShipHandle {
impl Eq for GxShipHandle {}
impl PartialEq for GxShipHandle {
fn eq(&self, other: &Self) -> bool {
self.index.eq(&other.index)
}

11
crates/galaxy/src/lib.rs Normal file
View File

@ -0,0 +1,11 @@
//! This module keeps track of the galaxy state.
//! It is also responsible for ship stats, outfits, etc.
//!
//! `galaxy` does not provide any simulation logic---that is done in seperate crates.
mod galaxy;
mod handles;
pub mod ship;
pub use galaxy::*;
pub use handles::*;

View File

@ -1,7 +1,6 @@
use std::collections::HashMap;
use content::{GunPoint, OutfitHandle, OutfitSpace, SpriteHandle};
use galactica_content as content;
use galactica_content::{Content, GunPoint, Outfit, OutfitHandle, OutfitSpace, SpriteHandle};
/// Possible outcomes when adding an outfit
pub enum OutfitAddResult {
@ -89,7 +88,7 @@ impl OutfitSet {
}
}
pub(super) fn add(&mut self, o: &content::Outfit) -> OutfitAddResult {
pub(super) fn add(&mut self, o: &Outfit) -> OutfitAddResult {
if !(self.total_space - self.used_space).can_contain(&o.space) {
return OutfitAddResult::NotEnoughSpace("TODO".to_string());
}
@ -129,7 +128,7 @@ impl OutfitSet {
return OutfitAddResult::Ok;
}
pub(super) fn remove(&mut self, o: &content::Outfit) -> OutfitRemoveResult {
pub(super) fn remove(&mut self, o: &Outfit) -> OutfitRemoveResult {
if !self.outfits.contains_key(&o.handle) {
return OutfitRemoveResult::NotExist;
} else {
@ -161,7 +160,7 @@ impl OutfitSet {
}
// TODO: pick these better
pub fn get_flare_sprite(&self, ct: &content::Content) -> Option<SpriteHandle> {
pub fn get_flare_sprite(&self, ct: &Content) -> Option<SpriteHandle> {
for i in self.outfits.keys() {
let c = ct.get_outfit(*i);
if c.engine_flare_sprite.is_some() {

View File

@ -1,6 +1,5 @@
/// Computer-controlled ship behavior variants.
/// This is just a list, actual physics-aware
/// behaviors are implemented in [`galactica-behavior`]
/// This is just a list, actual state-aware behaviors are implemented in each simulation crate.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShipPersonality {
/// This ship is controlled by a player

View File

@ -1,18 +1,17 @@
use std::{collections::HashMap, time::Instant};
use crate::GameShipHandle;
use crate::GxShipHandle;
use super::{OutfitSet, ShipPersonality};
use content::GunPoint;
use galactica_content as content;
use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle};
use rand::{rngs::ThreadRng, Rng};
#[derive(Debug)]
pub struct Ship {
pub struct GxShip {
// Metadata values
handle: GameShipHandle,
ct_handle: content::ShipHandle,
faction: content::FactionHandle,
handle: GxShipHandle,
ct_handle: ShipHandle,
faction: FactionHandle,
outfits: OutfitSet,
personality: ShipPersonality,
@ -29,16 +28,16 @@ pub struct Ship {
last_hit: Instant,
}
impl Ship {
impl GxShip {
pub(crate) fn new(
ct: &content::Content,
handle: GameShipHandle,
ct_handle: content::ShipHandle,
faction: content::FactionHandle,
ct: &Content,
handle: GxShipHandle,
ct_handle: ShipHandle,
faction: FactionHandle,
personality: ShipPersonality,
) -> Self {
let s = ct.get_ship(ct_handle);
Ship {
GxShip {
handle,
ct_handle,
faction,
@ -55,14 +54,14 @@ impl 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: &Outfit) -> super::OutfitAddResult {
let r = self.outfits.add(o);
self.shields = self.outfits.get_shield_strength();
return r;
}
/// Remove an outfit from this ship
pub fn remove_outfit(&mut self, o: &content::Outfit) -> super::OutfitRemoveResult {
pub fn remove_outfit(&mut self, o: &Outfit) -> super::OutfitRemoveResult {
self.outfits.remove(o)
}
@ -75,7 +74,7 @@ impl Ship {
/// 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 {
pub fn fire_gun(&mut self, ct: &Content, which: &GunPoint) -> bool {
let c = self.gun_cooldowns.get_mut(which).unwrap();
if *c > 0.0 {
@ -134,14 +133,14 @@ impl Ship {
}
// Misc getters, so internal state is untouchable
impl Ship {
impl GxShip {
/// Get a handle to this ship game object
pub fn get_handle(&self) -> GameShipHandle {
pub fn get_handle(&self) -> GxShipHandle {
self.handle
}
/// Get a handle to this ship's content
pub fn get_content(&self) -> content::ShipHandle {
pub fn get_content(&self) -> ShipHandle {
self.ct_handle
}
@ -168,12 +167,12 @@ impl Ship {
}
/// Get this ship's faction
pub fn get_faction(&self) -> content::FactionHandle {
pub fn get_faction(&self) -> FactionHandle {
self.faction
}
/// Get this ship's content handle
pub fn get_ship(&self) -> content::ShipHandle {
pub fn get_ship(&self) -> ShipHandle {
self.ct_handle
}
}

View File

@ -1,12 +0,0 @@
//! This module handles all game data: ship damage, outfit stats, etc.
//!
//! The code here handles game *data* exclusively: it keeps track of the status
//! of every ship in the game, but it has no understanding of physics.
//! That is done in `galactica_world`.
mod gamedata;
mod handles;
pub mod ship;
pub use gamedata::*;
pub use handles::*;

View File

@ -20,8 +20,8 @@ workspace = true
galactica-content = { workspace = true }
galactica-constants = { workspace = true }
galactica-packer = { workspace = true }
galactica-world = { workspace = true }
galactica-gameobject = { workspace = true }
galactica-systemsim = { workspace = true }
galactica-galaxy = { workspace = true }
anyhow = { workspace = true }
cgmath = { workspace = true }

View File

@ -3,7 +3,7 @@
use std::f32::consts::TAU;
use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2};
use galactica_world::util;
use galactica_systemsim::util;
use crate::{
vertexbuffer::{
@ -22,9 +22,9 @@ impl GPUState {
let system_object_scale = 1.0 / 600.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.systemsim.get_ship(state.player_data).unwrap();
let player_body = state
.world
.systemsim
.get_rigid_body(player_world_object.rigid_body)
.unwrap();
@ -103,7 +103,7 @@ impl GPUState {
}
// Draw ships
for (s, r) in state.world.iter_ship_body() {
for (s, r) in state.systemsim.iter_ship_body() {
// This will be None if this ship is dead.
// Stays around while the physics system runs a collapse sequence
let color = match state.data.get_ship(s.data_handle) {
@ -291,7 +291,7 @@ impl GPUState {
panic!("UI limit exceeded!")
}
let player_world_object = state.world.get_ship(state.player_data).unwrap();
let player_world_object = state.systemsim.get_ship(state.player_data).unwrap();
let data = state
.data

View File

@ -3,13 +3,13 @@ use bytemuck;
use cgmath::Point2;
use galactica_constants;
use galactica_content::Content;
use rand::seq::SliceRandom;
use std::{iter, rc::Rc};
use wgpu::{self, BufferAddress};
use winit::{self, window::Window};
use crate::{
content,
globaluniform::{GlobalDataContent, GlobalUniform},
pipeline::PipelineBuilder,
starfield::Starfield,
@ -27,7 +27,7 @@ use crate::{
// Additional implementaitons for GPUState
mod hud;
mod world;
mod systemsim;
/// A high-level GPU wrapper. Consumes game state,
/// produces pretty pictures.
@ -108,7 +108,7 @@ fn preprocess_shader(
impl GPUState {
/// Make a new GPUState that draws on `window`
pub async fn new(window: Window, ct: &content::Content) -> Result<Self> {
pub async fn new(window: Window, ct: &Content) -> Result<Self> {
let window_size = window.inner_size();
let window_aspect = window_size.width as f32 / window_size.height as f32;
@ -364,9 +364,9 @@ impl GPUState {
// Order matters, it determines what is drawn on top.
// The order inside ships and projectiles doesn't matter,
// but ships should always be under projectiles.
self.world_push_system(state, (clip_ne, clip_sw));
self.world_push_ship(state, (clip_ne, clip_sw));
self.world_push_projectile(state, (clip_ne, clip_sw));
self.sysim_push_system(state, (clip_ne, clip_sw));
self.sysim_push_ship(state, (clip_ne, clip_sw));
self.sysim_push_projectile(state, (clip_ne, clip_sw));
self.hud_add_radar(state);
self.hud_add_status(state);

View File

@ -1,8 +1,8 @@
//! GPUState routines for drawing the world
//! GPUState routines for drawing items in a systemsim
use bytemuck;
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
use galactica_world::util;
use galactica_systemsim::util;
use crate::{
globaluniform::ObjectData,
@ -11,14 +11,14 @@ use crate::{
};
impl GPUState {
pub(super) fn world_push_ship(
pub(super) fn sysim_push_ship(
&mut self,
state: &RenderState,
// NE and SW corners of screen
screen_clip: (Point2<f32>, Point2<f32>),
) {
for s in state.world.iter_ships() {
let r = state.world.get_rigid_body(s.rigid_body).unwrap();
for s in state.systemsim.iter_ships() {
let r = state.systemsim.get_rigid_body(s.rigid_body).unwrap();
let ship_pos = util::rigidbody_position(&r);
let ship_rot = util::rigidbody_rotation(r);
let ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix!
@ -129,14 +129,14 @@ impl GPUState {
}
}
pub(super) fn world_push_projectile(
pub(super) fn sysim_push_projectile(
&mut self,
state: &RenderState,
// NE and SW corners of screen
screen_clip: (Point2<f32>, Point2<f32>),
) {
for p in state.world.iter_projectiles() {
let r = state.world.get_rigid_body(p.rigid_body).unwrap();
for p in state.systemsim.iter_projectiles() {
let r = state.systemsim.get_rigid_body(p.rigid_body).unwrap();
let proj_pos = util::rigidbody_position(&r);
let proj_rot = util::rigidbody_rotation(r);
let proj_ang = -proj_rot.angle(Vector2 { x: 1.0, y: 0.0 });
@ -201,7 +201,7 @@ impl GPUState {
}
}
pub(super) fn world_push_system(
pub(super) fn sysim_push_system(
&mut self,
state: &RenderState,
// NE and SW corners of screen

View File

@ -17,7 +17,6 @@ mod texturearray;
mod vertexbuffer;
pub use anchoredposition::PositionAnchor;
use galactica_content as content;
pub use gpustate::GPUState;
pub use renderstate::RenderState;

View File

@ -1,7 +1,7 @@
use cgmath::Point2;
use galactica_content::{Content, SystemHandle};
use galactica_gameobject::{GameData, GameShipHandle};
use galactica_world::{ParticleBuilder, World};
use galactica_galaxy::{Galaxy, GxShipHandle};
use galactica_systemsim::{ParticleBuilder, SystemSim};
/// Bundles parameters passed to a single call to GPUState::render
pub struct RenderState<'a> {
@ -9,7 +9,7 @@ pub struct RenderState<'a> {
pub camera_pos: Point2<f32>,
/// Player ship data
pub player_data: GameShipHandle,
pub player_data: GxShipHandle,
/// The system we're currently in
pub current_system: SystemHandle,
@ -18,7 +18,7 @@ pub struct RenderState<'a> {
pub camera_zoom: f32,
/// The world state to render
pub world: &'a World,
pub systemsim: &'a SystemSim,
// TODO: handle overflow. is it a problem?
/// The current time, in seconds
@ -28,7 +28,7 @@ pub struct RenderState<'a> {
pub content: &'a Content,
/// Game data
pub data: &'a GameData,
pub data: &'a Galaxy,
/// Particles to spawn during this frame
pub particles: &'a mut Vec<ParticleBuilder>,

View File

@ -1,10 +1,8 @@
use crate::{
content,
globaluniform::{AtlasArray, AtlasImageLocation, SpriteData, SpriteDataArray},
};
use crate::globaluniform::{AtlasArray, AtlasImageLocation, SpriteData, SpriteDataArray};
use anyhow::Result;
use bytemuck::Zeroable;
use galactica_constants::ASSET_CACHE;
use galactica_content::Content;
use galactica_packer::SpriteAtlasImage;
use image::GenericImageView;
use std::{fs::File, io::Read, num::NonZeroU32, path::Path};
@ -51,7 +49,7 @@ impl RawTexture {
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let view = texture.create_view(&Default::default());
queue.write_texture(
wgpu::ImageCopyTexture {
@ -91,7 +89,7 @@ pub struct TextureArray {
}
impl TextureArray {
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &content::Content) -> Result<Self> {
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &Content) -> Result<Self> {
// Load all textures
let mut texture_data = Vec::new();

View File

@ -1,6 +1,6 @@
[package]
name = "galactica-world"
description = "World interactions for Galactica"
name = "galactica-systemsim"
description = "Physics interactions for Galactica"
categories = { workspace = true }
keywords = { workspace = true }
version = { workspace = true }
@ -18,7 +18,7 @@ workspace = true
[dependencies]
galactica-content = { workspace = true }
galactica-gameobject = { workspace = true }
galactica-galaxy = { workspace = true }
rapier2d = { workspace = true }
nalgebra = { workspace = true }

View File

@ -3,30 +3,30 @@
mod null;
mod point;
use std::collections::HashMap;
use galactica_gameobject::GameShipHandle;
pub use null::*;
pub use point::Point;
use galactica_galaxy::GxShipHandle;
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
use std::collections::HashMap;
use crate::{
objects::{ShipControls, ShipWorldObject},
objects::{ShipControls, SySimShip},
StepResources,
};
/// Main behavior trait. Any struct that implements this
/// Ship controller trait. Any struct that implements this
/// may be used to control a ship.
pub trait ShipBehavior {
/// Update a ship's controls based on world state.
pub trait ShipController {
/// Update a ship's controls based on system state.
/// This method does not return anything, it modifies
/// the ship's controls in-place.
fn update_controls(
&mut self,
res: &StepResources,
rigid_bodies: &RigidBodySet,
ships: &HashMap<GameShipHandle, ShipWorldObject>,
ships: &HashMap<GxShipHandle, SySimShip>,
this_ship: RigidBodyHandle,
this_data: GameShipHandle,
this_data: GxShipHandle,
) -> ShipControls;
}

View File

@ -1,16 +1,15 @@
use galactica_galaxy::GxShipHandle;
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
use std::collections::HashMap;
use galactica_gameobject::GameShipHandle;
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
use super::ShipBehavior;
use super::ShipController;
use crate::{
objects::{ShipControls, ShipWorldObject},
objects::{ShipControls, SySimShip},
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.
/// The Null controller is assigned to objects that are static or not controlled by the computer.
/// Most notably, the player's ship has a Null controller.
pub struct Null {}
impl Null {
@ -20,14 +19,14 @@ impl Null {
}
}
impl ShipBehavior for Null {
impl ShipController for Null {
fn update_controls(
&mut self,
_res: &StepResources,
_rigid_bodies: &RigidBodySet,
_ships: &HashMap<GameShipHandle, ShipWorldObject>,
_ships: &HashMap<GxShipHandle, SySimShip>,
_this_ship: RigidBodyHandle,
_this_data: GameShipHandle,
_this_data: GxShipHandle,
) -> ShipControls {
ShipControls::new()
}

View File

@ -1,17 +1,16 @@
use cgmath::{Deg, InnerSpace};
use galactica_content::Relationship;
use galactica_galaxy::GxShipHandle;
use rapier2d::dynamics::{RigidBodyHandle, RigidBodySet};
use std::collections::HashMap;
use super::ShipController;
use crate::{
objects::{ShipControls, ShipWorldObject},
objects::{ShipControls, SySimShip},
util, StepResources,
};
use galactica_content as content;
use galactica_gameobject::GameShipHandle;
use super::ShipBehavior;
/// "Point" ship behavior.
/// "Point" ship controller.
/// Point and shoot towards the nearest enemy.
pub struct Point {}
@ -22,19 +21,19 @@ impl Point {
}
}
impl ShipBehavior for Point {
impl ShipController for Point {
fn update_controls(
&mut self,
res: &StepResources,
rigid_bodies: &RigidBodySet,
ships: &HashMap<GameShipHandle, ShipWorldObject>,
ships: &HashMap<GxShipHandle, SySimShip>,
this_ship: RigidBodyHandle,
this_data: GameShipHandle,
this_data: GxShipHandle,
) -> ShipControls {
let mut controls = ShipControls::new();
let this_rigidbody = rigid_bodies.get(this_ship).unwrap();
let my_data = res.dt.get_ship(this_data).unwrap();
let my_data = res.gx.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();
@ -44,10 +43,10 @@ impl ShipBehavior for Point {
let mut hostile_ships = ships
.values()
.filter(|s| {
let data = res.dt.get_ship(s.data_handle);
let data = res.gx.get_ship(s.data_handle);
if let Some(data) = data {
match my_faction.relationships.get(&data.get_faction()).unwrap() {
content::Relationship::Hostile => true,
Relationship::Hostile => true,
_ => false,
}
} else {

View File

@ -1,16 +1,15 @@
#![warn(missing_docs)]
//! This module keeps track of the visible world.
//! Ships, projectiles, collisions, etc.
//! This module provides a physics-based simulation of one galaxy system.
pub mod behavior;
pub mod controller;
pub mod objects;
mod particlebuilder;
mod stepresources;
mod systemsim;
pub mod util;
mod world;
mod wrapper;
pub use particlebuilder::*;
pub use stepresources::*;
pub use world::World;
pub use systemsim::SystemSim;

View File

@ -0,0 +1,143 @@
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
use galactica_content::{CollapseEvent, Ship, ShipHandle};
use nalgebra::point;
use rand::{rngs::ThreadRng, Rng};
use rapier2d::{dynamics::RigidBody, geometry::Collider};
use crate::{util, ParticleBuilder, StepResources};
#[derive(Debug)]
pub(super) struct ShipCollapseSequence {
total_length: f32,
time_elapsed: f32,
rng: ThreadRng,
}
impl ShipCollapseSequence {
pub(super) fn new(total_length: f32) -> Self {
Self {
total_length,
time_elapsed: 0.0,
rng: rand::thread_rng(),
}
}
/// Has this sequence been fully played out?
pub(super) 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: &Ship, collider: &Collider) -> Vector2<f32> {
let mut y = 0.0;
let mut x = 0.0;
let mut a = false;
while !a {
y = self.rng.gen_range(-1.0..=1.0) * ship_content.size / 2.0;
x = self.rng.gen_range(-1.0..=1.0) * ship_content.size * ship_content.sprite.aspect
/ 2.0;
a = collider.shape().contains_local_point(&point![x, y]);
}
Vector2 { x, y }
}
/// Step this sequence `t` seconds
pub(super) fn step(
&mut self,
res: &mut StepResources,
ship_handle: ShipHandle,
rigid_body: &mut RigidBody,
collider: &mut Collider,
) {
let ship_content = res.ct.get_ship(ship_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 });
// The fraction of this collapse sequence that has been played
let frac_done = self.time_elapsed / self.total_length;
// Trigger collapse events
for event in &ship_content.collapse.events {
match event {
CollapseEvent::Effect(event) => {
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 = res.ct.get_effect(spawner.effect);
for _ in 0..spawner.count as usize {
let pos = if let Some(pos) = spawner.pos {
pos.to_vec()
} else {
self.random_in_ship(ship_content, collider)
};
let pos = ship_pos
+ (Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos);
let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
res.particles.push(ParticleBuilder::from_content(
effect,
pos,
Rad::zero(),
Vector2 {
x: velocity.x,
y: velocity.y,
},
Vector2::zero(),
))
}
}
}
}
}
}
// Create collapse effects
for spawner in &ship_content.collapse.effects {
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.
let pdf = |x: f32| {
let f = 0.2;
let y = if x < (1.0 - f) {
let x = x / (1.0 - f);
(x * x + 0.2) * 1.8 - f
} else {
1.0
};
return y;
};
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 {
pos.to_vec()
} else {
self.random_in_ship(ship_content, collider)
};
// Position, adjusted for ship rotation
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]);
res.particles.push(ParticleBuilder {
sprite: effect.sprite,
pos,
velocity: Vector2 { x: vel.x, y: vel.y },
angle: Rad::zero(),
angvel: Rad::zero(),
lifetime: effect.lifetime,
size: effect.size,
fade: 0.0,
});
}
}
self.time_elapsed += res.t;
}
}

View File

@ -1,6 +1,7 @@
//! This module contains game objects that may interact with the physics engine.
mod collapse;
mod projectile;
mod ship;
pub use projectile::ProjectileWorldObject;
pub use ship::{ShipControls, ShipWorldObject};
pub use projectile::SySimProjectile;
pub use ship::{ShipControls, SySimShip};

View File

@ -1,19 +1,18 @@
use galactica_content::{FactionHandle, Projectile};
use rand::Rng;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
use galactica_content as content;
/// A single projectile in the world
/// A single projectile in this sim
#[derive(Debug)]
pub struct ProjectileWorldObject {
pub struct SySimProjectile {
/// This projectile's game data
pub content: content::Projectile,
pub content: Projectile,
/// The remaining lifetime of this projectile, in seconds
pub lifetime: f32,
/// The faction this projectile belongs to
pub faction: content::FactionHandle,
pub faction: FactionHandle,
/// This projectile's rigidbody
pub rigid_body: RigidBodyHandle,
@ -25,18 +24,18 @@ pub struct ProjectileWorldObject {
pub size_rng: f32,
}
impl ProjectileWorldObject {
impl SySimProjectile {
/// Create a new projectile
pub fn new(
content: content::Projectile, // TODO: use a handle
content: Projectile, // TODO: use a handle
rigid_body: RigidBodyHandle,
faction: content::FactionHandle,
faction: FactionHandle,
collider: ColliderHandle,
) -> Self {
let mut rng = rand::thread_rng();
let size_rng = content.size_rng;
let lifetime = content.lifetime;
ProjectileWorldObject {
SySimProjectile {
rigid_body,
collider,
content,

View File

@ -1,16 +1,16 @@
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
use content::{FactionHandle, ShipHandle};
use galactica_content::{Content, FactionHandle};
use galactica_galaxy::GxShipHandle;
use nalgebra::{point, vector};
use object::GameShipHandle;
use rand::{rngs::ThreadRng, Rng};
use rand::Rng;
use rapier2d::{
dynamics::{RigidBody, RigidBodyHandle},
geometry::{Collider, ColliderHandle},
};
use crate::{util, ParticleBuilder, StepResources};
use galactica_content as content;
use galactica_gameobject as object;
use super::collapse::ShipCollapseSequence;
/// A ship's controls
#[derive(Debug, Clone)]
@ -40,149 +40,9 @@ impl ShipControls {
}
}
#[derive(Debug)]
struct ShipCollapseSequence {
total_length: f32,
time_elapsed: f32,
rng: ThreadRng,
}
impl ShipCollapseSequence {
fn new(total_length: f32) -> Self {
Self {
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> {
let mut y = 0.0;
let mut x = 0.0;
let mut a = false;
while !a {
y = self.rng.gen_range(-1.0..=1.0) * ship_content.size / 2.0;
x = self.rng.gen_range(-1.0..=1.0) * ship_content.size * ship_content.sprite.aspect
/ 2.0;
a = collider.shape().contains_local_point(&point![x, y]);
}
Vector2 { x, y }
}
/// Step this sequence `t` seconds
fn step(
&mut self,
res: &mut StepResources,
ship_handle: ShipHandle,
rigid_body: &mut RigidBody,
collider: &mut Collider,
) {
let ship_content = res.ct.get_ship(ship_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 });
// The fraction of this collapse sequence that has been played
let frac_done = self.time_elapsed / self.total_length;
// Trigger collapse events
for event in &ship_content.collapse.events {
match event {
content::CollapseEvent::Effect(event) => {
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 = res.ct.get_effect(spawner.effect);
for _ in 0..spawner.count as usize {
let pos = if let Some(pos) = spawner.pos {
pos.to_vec()
} else {
self.random_in_ship(ship_content, collider)
};
let pos = ship_pos
+ (Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos);
let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
res.particles.push(ParticleBuilder::from_content(
effect,
pos,
Rad::zero(),
Vector2 {
x: velocity.x,
y: velocity.y,
},
Vector2::zero(),
))
}
}
}
}
}
}
// Create collapse effects
for spawner in &ship_content.collapse.effects {
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.
let pdf = |x: f32| {
let f = 0.2;
let y = if x < (1.0 - f) {
let x = x / (1.0 - f);
(x * x + 0.2) * 1.8 - f
} else {
1.0
};
return y;
};
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 {
pos.to_vec()
} else {
self.random_in_ship(ship_content, collider)
};
// Position, adjusted for ship rotation
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]);
res.particles.push(ParticleBuilder {
sprite: effect.sprite,
pos,
velocity: Vector2 { x: vel.x, y: vel.y },
angle: Rad::zero(),
angvel: Rad::zero(),
lifetime: effect.lifetime,
size: effect.size,
fade: 0.0,
});
}
}
self.time_elapsed += res.t;
}
}
/// A ship instance in the physics system
#[derive(Debug)]
pub struct ShipWorldObject {
pub struct SySimShip {
/// This ship's physics handle
pub rigid_body: RigidBodyHandle,
@ -190,7 +50,7 @@ pub struct ShipWorldObject {
pub collider: ColliderHandle,
/// This ship's game data
pub data_handle: GameShipHandle,
pub data_handle: GxShipHandle,
/// This ship's controls
pub(crate) controls: ShipControls,
@ -205,17 +65,17 @@ pub struct ShipWorldObject {
faction: FactionHandle,
}
impl ShipWorldObject {
impl SySimShip {
/// Make a new ship
pub(crate) fn new(
ct: &content::Content,
data_handle: GameShipHandle,
ct: &Content,
data_handle: GxShipHandle,
faction: FactionHandle,
rigid_body: RigidBodyHandle,
collider: ColliderHandle,
) -> Self {
let ship_content = ct.get_ship(data_handle.content_handle());
ShipWorldObject {
SySimShip {
rigid_body,
collider,
data_handle,
@ -237,7 +97,7 @@ impl ShipWorldObject {
rigid_body: &mut RigidBody,
collider: &mut Collider,
) {
let ship_data = res.dt.get_ship(self.data_handle);
let ship_data = res.gx.get_ship(self.data_handle);
if ship_data.is_none() {
// If ship data is none, it has been removed because the ship has been destroyed.
// play collapse sequence.
@ -259,7 +119,7 @@ impl ShipWorldObject {
rigid_body: &mut RigidBody,
collider: &mut Collider,
) {
let ship = res.dt.get_ship(self.data_handle).unwrap();
let ship = res.gx.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);
@ -328,7 +188,7 @@ impl ShipWorldObject {
}
}
impl ShipWorldObject {
impl SySimShip {
/// Get this ship's control state
pub fn get_controls(&self) -> &ShipControls {
&self.controls

View File

@ -1,12 +1,12 @@
use cgmath::{Matrix2, Point2, Rad, Vector2};
use galactica_content as content;
use galactica_content::{Effect, SpriteHandle};
use rand::Rng;
/// Instructions to create a new particle
#[derive(Debug)]
pub struct ParticleBuilder {
/// The sprite to use for this particle
pub sprite: content::SpriteHandle,
pub sprite: SpriteHandle,
/// This object's center, in world coordinates.
pub pos: Point2<f32>,
@ -34,7 +34,7 @@ pub struct ParticleBuilder {
impl ParticleBuilder {
/// Create a ParticleBuilder from an Effect
pub fn from_content(
effect: &content::Effect,
effect: &Effect,
pos: Point2<f32>,
parent_angle: Rad<f32>,
parent_velocity: Vector2<f32>,

View File

@ -1,6 +1,6 @@
use crate::{objects::ShipControls, ParticleBuilder};
use galactica_content::Content;
use galactica_gameobject::{GameData, GameShipHandle};
use galactica_galaxy::{Galaxy, GxShipHandle};
/// External resources we need to compute time steps
#[derive(Debug)]
@ -9,7 +9,7 @@ pub struct StepResources<'a> {
pub ct: &'a Content,
/// Game data
pub dt: &'a mut GameData,
pub gx: &'a mut Galaxy,
/// Length of time step
pub t: f32,
@ -21,5 +21,5 @@ pub struct StepResources<'a> {
pub player_controls: ShipControls,
/// The ship that the player controls
pub player: GameShipHandle,
pub player: GxShipHandle,
}

View File

@ -1,8 +1,10 @@
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
use content::{GunPoint, OutfitHandle};
use crossbeam::channel::Receiver;
use galactica_content::{
Content, GunPoint, OutfitHandle, ProjectileCollider, Relationship, SystemHandle,
};
use galactica_galaxy::{ship::GxShip, Galaxy, GxShipHandle};
use nalgebra::{point, vector};
use object::{ship::Ship, GameData, GameShipHandle};
use rand::Rng;
use rapier2d::{
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
@ -12,37 +14,32 @@ use rapier2d::{
use std::{collections::HashMap, f32::consts::PI};
use crate::{
behavior::{self, ShipBehavior},
controller::{self, ShipController},
objects,
objects::{ProjectileWorldObject, ShipWorldObject},
objects::{SySimProjectile, SySimShip},
util,
wrapper::Wrapper,
ParticleBuilder, StepResources,
};
use galactica_content as content;
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,
pub struct SystemSim {
/// The system this sim is attached to
_system: SystemHandle,
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>,
projectiles: HashMap<ColliderHandle, objects::SySimProjectile>,
ships: HashMap<GxShipHandle, objects::SySimShip>,
ship_behaviors: HashMap<GxShipHandle, Box<dyn ShipController>>,
collider_ship_table: HashMap<ColliderHandle, GxShipHandle>,
collision_handler: ChannelEventCollector,
collision_queue: Receiver<CollisionEvent>,
}
// Private methods
impl<'a> World {
fn remove_projectile(
&mut self,
c: ColliderHandle,
) -> Option<(RigidBody, ProjectileWorldObject)> {
impl<'a> SystemSim {
fn remove_projectile(&mut self, c: ColliderHandle) -> Option<(RigidBody, SySimProjectile)> {
let p = match self.projectiles.remove(&c) {
Some(p) => p,
None => return None,
@ -104,9 +101,9 @@ impl<'a> World {
let f = res.ct.get_faction(projectile.faction);
let r = f.relationships.get(&ship.get_faction()).unwrap();
let destory_projectile = match r {
content::Relationship::Hostile => {
Relationship::Hostile => {
// We only apply damage if the target ship is alive
if let Some(ship_d) = res.dt.get_ship_mut(ship.data_handle) {
if let Some(ship_d) = res.gx.get_ship_mut(ship.data_handle) {
ship_d.apply_damage(projectile.content.damage);
}
true
@ -169,9 +166,9 @@ impl<'a> World {
}
// Public methods
impl World {
impl SystemSim {
/// Create a new physics system
pub fn new(ct: &content::Content, dt: &GameData, system: content::SystemHandle) -> Self {
pub fn new(ct: &Content, gx: &Galaxy, system: SystemHandle) -> Self {
let (collision_send, collision_queue) = crossbeam::channel::unbounded();
let (contact_force_send, _) = crossbeam::channel::unbounded();
@ -188,9 +185,9 @@ impl World {
// TODO: guarantee not touching
// TODO: add, remove ships each tick
// Maybe store position in gamedata?
// Maybe store position in galaxy crate?
let mut rng = rand::thread_rng();
for s in dt.iter_ships() {
for s in gx.iter_ships() {
w.add_ship(
ct,
s,
@ -205,7 +202,7 @@ impl World {
}
/// Add a ship to this physics system
pub fn add_ship(&mut self, ct: &content::Content, ship: &Ship, position: Point2<f32>) {
pub fn add_ship(&mut self, ct: &Content, ship: &GxShip, position: Point2<f32>) {
let ship_content = ct.get_ship(ship.get_content());
let cl = ColliderBuilder::convex_decomposition(
&ship_content.collision.points[..],
@ -232,10 +229,10 @@ impl World {
self.collider_ship_table.insert(c, ship.get_handle());
self.ship_behaviors
.insert(ship.get_handle(), Box::new(behavior::Point::new()));
.insert(ship.get_handle(), Box::new(controller::Point::new()));
self.ships.insert(
ship.get_handle(),
objects::ShipWorldObject::new(ct, ship.get_handle(), ship.get_faction(), r, c),
objects::SySimShip::new(ct, ship.get_handle(), ship.get_faction(), r, c),
);
}
@ -260,7 +257,7 @@ impl World {
// 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() {
if res.gx.get_ship(*handle).is_none() {
let ship_object = self.ships.get_mut(handle).unwrap();
ship_object.step(
res,
@ -296,7 +293,7 @@ impl World {
// 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();
let ship_data = res.gx.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.
@ -329,7 +326,7 @@ impl World {
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_dat = res.gx.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);
@ -361,7 +358,7 @@ impl World {
.build();
let collider = match &outfit.projectile.collider {
content::ProjectileCollider::Ball(b) => ColliderBuilder::ball(b.radius)
ProjectileCollider::Ball(b) => ColliderBuilder::ball(b.radius)
.sensor(true)
.active_events(ActiveEvents::COLLISION_EVENTS)
.build(),
@ -376,7 +373,7 @@ impl World {
self.projectiles.insert(
collider.clone(),
ProjectileWorldObject::new(
SySimProjectile::new(
outfit.projectile.clone(),
rigid_body,
ship_dat.get_faction(),
@ -464,9 +461,9 @@ impl World {
}
// Public getters
impl World {
impl SystemSim {
/// Get a ship physics object
pub fn get_ship(&self, ship: GameShipHandle) -> Option<&ShipWorldObject> {
pub fn get_ship(&self, ship: GxShipHandle) -> Option<&SySimShip> {
self.ships.get(&ship)
}
@ -481,21 +478,19 @@ impl World {
}
/// Iterate over all ships in this physics system
pub fn iter_ship_body(
&self,
) -> impl Iterator<Item = (&objects::ShipWorldObject, &RigidBody)> + '_ {
pub fn iter_ship_body(&self) -> impl Iterator<Item = (&objects::SySimShip, &RigidBody)> + '_ {
self.ships
.values()
.map(|x| (x, self.wrapper.rigid_body_set.get(x.rigid_body).unwrap()))
}
/// Iterate over all ships in this physics system
pub fn iter_ships(&self) -> impl Iterator<Item = &ShipWorldObject> + '_ {
pub fn iter_ships(&self) -> impl Iterator<Item = &SySimShip> + '_ {
self.ships.values()
}
/// Iterate over all ships in this physics system
pub fn iter_projectiles(&self) -> impl Iterator<Item = &ProjectileWorldObject> + '_ {
pub fn iter_projectiles(&self) -> impl Iterator<Item = &SySimProjectile> + '_ {
self.projectiles.values()
}
}