Compare commits
2 Commits
9f1db1c7b6
...
3b7ac5bc9a
Author | SHA1 | Date |
---|---|---|
Mark | 3b7ac5bc9a | |
Mark | c920ebefbc |
|
@ -702,6 +702,7 @@ dependencies = [
|
||||||
"cgmath",
|
"cgmath",
|
||||||
"galactica-content",
|
"galactica-content",
|
||||||
"galactica-packer",
|
"galactica-packer",
|
||||||
|
"galactica-playeragent",
|
||||||
"galactica-system",
|
"galactica-system",
|
||||||
"galactica-util",
|
"galactica-util",
|
||||||
"glyphon",
|
"glyphon",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# TODO: big objects in one config
|
||||||
|
|
||||||
[system."12 Autumn Above"]
|
[system."12 Autumn Above"]
|
||||||
|
|
||||||
object.star.sprite = "star::star"
|
object.star.sprite = "star::star"
|
||||||
|
|
|
@ -37,6 +37,15 @@ impl PartialEq for SpriteHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A lightweight representation of system body
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SystemObjectHandle {
|
||||||
|
/// TODO: pub in crate
|
||||||
|
pub system_handle: SystemHandle,
|
||||||
|
/// The index of this object in system.objects
|
||||||
|
pub body_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// A lightweight representation of an outfit
|
/// A lightweight representation of an outfit
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct OutfitHandle {
|
pub struct OutfitHandle {
|
||||||
|
|
|
@ -18,9 +18,7 @@ use std::{
|
||||||
use toml;
|
use toml;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
pub use handle::{
|
pub use handle::*;
|
||||||
EffectHandle, FactionHandle, GunHandle, OutfitHandle, ShipHandle, SpriteHandle, SystemHandle,
|
|
||||||
};
|
|
||||||
pub use part::*;
|
pub use part::*;
|
||||||
|
|
||||||
mod syntax {
|
mod syntax {
|
||||||
|
@ -333,6 +331,11 @@ impl Content {
|
||||||
return &self.systems[h.index];
|
return &self.systems[h.index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a system object from a handle
|
||||||
|
pub fn get_system_object(&self, h: SystemObjectHandle) -> &SystemObject {
|
||||||
|
return &self.get_system(h.system_handle).objects[h.body_index];
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a faction from a handle
|
/// Get a faction from a handle
|
||||||
pub fn get_faction(&self, h: FactionHandle) -> &Faction {
|
pub fn get_faction(&self, h: FactionHandle) -> &Faction {
|
||||||
return &self.factions[h.index];
|
return &self.factions[h.index];
|
||||||
|
|
|
@ -19,4 +19,4 @@ pub use ship::{
|
||||||
ShipCollapse,
|
ShipCollapse,
|
||||||
};
|
};
|
||||||
pub use sprite::{RepeatMode, Sprite};
|
pub use sprite::{RepeatMode, Sprite};
|
||||||
pub use system::{Object, System};
|
pub use system::{System, SystemObject};
|
||||||
|
|
|
@ -2,7 +2,10 @@ use anyhow::{bail, Context, Result};
|
||||||
use cgmath::{Deg, Point3, Rad};
|
use cgmath::{Deg, Point3, Rad};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::{handle::SpriteHandle, util::Polar, Content, ContentBuildContext};
|
use crate::{
|
||||||
|
handle::SpriteHandle, util::Polar, Content, ContentBuildContext, SystemHandle,
|
||||||
|
SystemObjectHandle,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -83,8 +86,11 @@ pub struct System {
|
||||||
/// This star system's name
|
/// This star system's name
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
|
/// This star system's handle
|
||||||
|
pub handle: SystemHandle,
|
||||||
|
|
||||||
/// Objects in this system
|
/// Objects in this system
|
||||||
pub objects: Vec<Object>,
|
pub objects: Vec<SystemObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an orbiting body in a star system
|
/// Represents an orbiting body in a star system
|
||||||
|
@ -92,10 +98,13 @@ pub struct System {
|
||||||
/// These may be landable and may be decorative.
|
/// These may be landable and may be decorative.
|
||||||
/// System objects to not interact with the physics engine.
|
/// System objects to not interact with the physics engine.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Object {
|
pub struct SystemObject {
|
||||||
/// This object's sprite
|
/// This object's sprite
|
||||||
pub sprite: SpriteHandle,
|
pub sprite: SpriteHandle,
|
||||||
|
|
||||||
|
/// This object's handle
|
||||||
|
pub handle: SystemObjectHandle,
|
||||||
|
|
||||||
/// This object's size.
|
/// This object's size.
|
||||||
/// Measured as height in game units.
|
/// Measured as height in game units.
|
||||||
/// This value is scaled for distance
|
/// This value is scaled for distance
|
||||||
|
@ -185,11 +194,15 @@ impl crate::Build for System {
|
||||||
for (system_name, system) in system {
|
for (system_name, system) in system {
|
||||||
let mut objects = Vec::new();
|
let mut objects = Vec::new();
|
||||||
|
|
||||||
|
let system_handle = SystemHandle {
|
||||||
|
index: content.systems.len(),
|
||||||
|
};
|
||||||
|
|
||||||
for (label, obj) in &system.object {
|
for (label, obj) in &system.object {
|
||||||
let mut cycle_detector = HashSet::new();
|
let mut cycle_detector = HashSet::new();
|
||||||
cycle_detector.insert(label.clone());
|
cycle_detector.insert(label.clone());
|
||||||
|
|
||||||
let handle = match content.sprite_index.get(&obj.sprite) {
|
let sprite_handle = match content.sprite_index.get(&obj.sprite) {
|
||||||
None => bail!(
|
None => bail!(
|
||||||
"In system `{}`: sprite `{}` doesn't exist",
|
"In system `{}`: sprite `{}` doesn't exist",
|
||||||
system_name,
|
system_name,
|
||||||
|
@ -198,17 +211,33 @@ impl crate::Build for System {
|
||||||
Some(t) => *t,
|
Some(t) => *t,
|
||||||
};
|
};
|
||||||
|
|
||||||
objects.push(Object {
|
objects.push(SystemObject {
|
||||||
sprite: handle,
|
sprite: sprite_handle,
|
||||||
pos: 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(),
|
||||||
|
handle: SystemObjectHandle {
|
||||||
|
system_handle,
|
||||||
|
body_index: 0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort by z-distance. This is important, since these are
|
||||||
|
// rendered in this order. We need far objects to be behind
|
||||||
|
// near objects!
|
||||||
objects.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
|
objects.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
|
||||||
|
|
||||||
|
// Update object handles
|
||||||
|
let mut i = 0;
|
||||||
|
for o in &mut objects {
|
||||||
|
o.handle.body_index = i;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
content.systems.push(Self {
|
content.systems.push(Self {
|
||||||
|
handle: system_handle,
|
||||||
name: system_name,
|
name: system_name,
|
||||||
objects,
|
objects,
|
||||||
});
|
});
|
||||||
|
|
|
@ -104,8 +104,10 @@ impl<'a> Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_player_controls(&mut self, player: &PlayerAgent) {
|
pub fn update_player_controls(&mut self, player: &mut PlayerAgent) {
|
||||||
self.state.systemsim.update_player_controls(player)
|
self.state
|
||||||
|
.systemsim
|
||||||
|
.update_player_controls(&self.ct, player)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_state(&self) -> &GameState {
|
pub fn get_state(&self) -> &GameState {
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
mod game;
|
mod game;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use cgmath::Point2;
|
||||||
use galactica_content::{Content, SystemHandle};
|
use galactica_content::{Content, SystemHandle};
|
||||||
use galactica_playeragent::{PlayerAgent, PlayerStatus};
|
use galactica_playeragent::{PlayerAgent, PlayerStatus};
|
||||||
use galactica_render::RenderInput;
|
use galactica_render::RenderInput;
|
||||||
use galactica_system::phys::{util, PhysSimShipHandle};
|
use galactica_system::{
|
||||||
|
data::ShipState,
|
||||||
|
phys::{util, PhysSimShipHandle},
|
||||||
|
};
|
||||||
use galactica_util::constants::ASSET_CACHE;
|
use galactica_util::constants::ASSET_CACHE;
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
|
@ -56,7 +60,7 @@ fn main() -> Result<()> {
|
||||||
ct: &content,
|
ct: &content,
|
||||||
systemsim: &game.get_state().systemsim,
|
systemsim: &game.get_state().systemsim,
|
||||||
particles: game.get_particles(),
|
particles: game.get_particles(),
|
||||||
player_ship: PhysSimShipHandle(player.ship.unwrap()),
|
player: &player,
|
||||||
current_system: SystemHandle { index: 0 },
|
current_system: SystemHandle { index: 0 },
|
||||||
timing: game.get_state().timing.clone(),
|
timing: game.get_state().timing.clone(),
|
||||||
};
|
};
|
||||||
|
@ -71,9 +75,10 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::MainEventsCleared => {
|
Event::MainEventsCleared => {
|
||||||
game.update_player_controls(&player);
|
game.update_player_controls(&mut player);
|
||||||
game.update();
|
game.update();
|
||||||
|
|
||||||
|
// TODO: clean up
|
||||||
let player_status = {
|
let player_status = {
|
||||||
let pos = {
|
let pos = {
|
||||||
let o = &game
|
let o = &game
|
||||||
|
@ -81,11 +86,23 @@ fn main() -> Result<()> {
|
||||||
.systemsim
|
.systemsim
|
||||||
.get_ship(&PhysSimShipHandle(player.ship.unwrap()));
|
.get_ship(&PhysSimShipHandle(player.ship.unwrap()));
|
||||||
if let Some(o) = o {
|
if let Some(o) = o {
|
||||||
let r = &game.get_state().systemsim.get_rigid_body(o.rigid_body);
|
match o.data.get_state() {
|
||||||
if let Some(r) = r {
|
ShipState::Collapsing { .. } | ShipState::Flying => {
|
||||||
Some(util::rigidbody_position(r))
|
let r =
|
||||||
} else {
|
&game.get_state().systemsim.get_rigid_body(o.rigid_body);
|
||||||
None
|
if let Some(r) = r {
|
||||||
|
Some(util::rigidbody_position(r))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ShipState::Landed { target } => {
|
||||||
|
let b = content.get_system_object(*target);
|
||||||
|
Some(Point2 {
|
||||||
|
x: b.pos.x,
|
||||||
|
y: b.pos.y,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -98,6 +115,7 @@ fn main() -> Result<()> {
|
||||||
// This must be updated BEFORE rendering!
|
// This must be updated BEFORE rendering!
|
||||||
player.step(&content, player_status);
|
player.step(&content, player_status);
|
||||||
|
|
||||||
|
player.input.clear_inputs();
|
||||||
gpu.window().request_redraw();
|
gpu.window().request_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||||
|
|
||||||
pub struct InputStatus {
|
pub struct InputStatus {
|
||||||
|
// Parameters
|
||||||
scroll_speed: f32,
|
scroll_speed: f32,
|
||||||
pub key_left: bool,
|
|
||||||
pub key_right: bool,
|
// Continuous keys
|
||||||
pub key_thrust: bool,
|
key_left: bool,
|
||||||
pub key_guns: bool,
|
key_right: bool,
|
||||||
pub v_scroll: f32,
|
key_thrust: bool,
|
||||||
|
key_guns: bool,
|
||||||
|
|
||||||
|
// One-shot keys (audomatically released at the end of each frame)
|
||||||
|
key_land: bool,
|
||||||
|
v_scroll: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputStatus {
|
impl InputStatus {
|
||||||
|
@ -16,6 +22,7 @@ impl InputStatus {
|
||||||
key_right: false,
|
key_right: false,
|
||||||
key_thrust: false,
|
key_thrust: false,
|
||||||
key_guns: false,
|
key_guns: false,
|
||||||
|
key_land: false,
|
||||||
v_scroll: 0.0,
|
v_scroll: 0.0,
|
||||||
scroll_speed: 10.0,
|
scroll_speed: 10.0,
|
||||||
}
|
}
|
||||||
|
@ -26,15 +33,34 @@ impl InputStatus {
|
||||||
self.key_right = false;
|
self.key_right = false;
|
||||||
self.key_thrust = false;
|
self.key_thrust = false;
|
||||||
self.key_guns = false;
|
self.key_guns = false;
|
||||||
|
self.key_land = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called at the end of every frame,
|
||||||
|
/// resets one-shot keys.
|
||||||
|
pub fn clear_inputs(&mut self) {
|
||||||
|
self.key_land = false;
|
||||||
|
self.v_scroll = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) {
|
pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) {
|
||||||
let down = state == &ElementState::Pressed;
|
let down = state == &ElementState::Pressed;
|
||||||
match key {
|
match key {
|
||||||
VirtualKeyCode::Left => self.key_left = down,
|
VirtualKeyCode::Left => {
|
||||||
VirtualKeyCode::Right => self.key_right = down,
|
self.key_left = down;
|
||||||
|
if down {
|
||||||
|
self.key_right = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VirtualKeyCode::Right => {
|
||||||
|
self.key_right = down;
|
||||||
|
if down {
|
||||||
|
self.key_left = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
VirtualKeyCode::Up => self.key_thrust = down,
|
VirtualKeyCode::Up => self.key_thrust = down,
|
||||||
VirtualKeyCode::Space => self.key_guns = down,
|
VirtualKeyCode::Space => self.key_guns = down,
|
||||||
|
VirtualKeyCode::L => self.key_land = down,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,3 +84,41 @@ impl InputStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Public get-state methods
|
||||||
|
impl InputStatus {
|
||||||
|
/// Has the player applied vertical scroll?
|
||||||
|
/// This is measured in lines, scaled by scroll_speed
|
||||||
|
///
|
||||||
|
/// A positive value means scroll up, a negative value means scroll down.
|
||||||
|
/// This is reset to zero at the end of each frame.
|
||||||
|
pub fn get_v_scroll(&self) -> f32 {
|
||||||
|
self.v_scroll
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the player pressing the "turn left" key?
|
||||||
|
pub fn pressed_left(&self) -> bool {
|
||||||
|
self.key_left
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the player pressing the "turn right" key?
|
||||||
|
pub fn pressed_right(&self) -> bool {
|
||||||
|
self.key_right
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the player pressing the "fowards" key?
|
||||||
|
pub fn pressed_thrust(&self) -> bool {
|
||||||
|
self.key_thrust
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the player pressing the "fire guns" key?
|
||||||
|
pub fn pressed_guns(&self) -> bool {
|
||||||
|
self.key_guns
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Has the player pressed the "land" key?
|
||||||
|
/// (One-shot, reset to false at the start of each frame)
|
||||||
|
pub fn pressed_land(&self) -> bool {
|
||||||
|
self.key_land
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,43 @@
|
||||||
use galactica_content::Content;
|
use galactica_content::{Content, SystemHandle, SystemObjectHandle};
|
||||||
use rapier2d::geometry::ColliderHandle;
|
use rapier2d::geometry::ColliderHandle;
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||||
|
|
||||||
use crate::{camera::Camera, inputstatus::InputStatus, PlayerStatus};
|
use crate::{camera::Camera, inputstatus::InputStatus, PlayerStatus};
|
||||||
|
|
||||||
|
/// What the player has selected
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum PlayerSelection {
|
||||||
|
/// We have nothing selected
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// We have a system body selected
|
||||||
|
OrbitingBody(SystemObjectHandle),
|
||||||
|
|
||||||
|
/// We have a ship selected
|
||||||
|
Ship(ColliderHandle),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerSelection {
|
||||||
|
pub fn get_planet(&self) -> Option<SystemObjectHandle> {
|
||||||
|
match self {
|
||||||
|
Self::OrbitingBody(h) => Some(*h),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PlayerAgent {
|
pub struct PlayerAgent {
|
||||||
/// Which ship this player is controlling
|
/// Which ship this player is controlling
|
||||||
pub ship: Option<ColliderHandle>,
|
pub ship: Option<ColliderHandle>,
|
||||||
|
|
||||||
|
/// What the player has selected
|
||||||
|
pub selection: PlayerSelection,
|
||||||
|
|
||||||
/// This player's camera
|
/// This player's camera
|
||||||
pub camera: Camera,
|
pub camera: Camera,
|
||||||
|
|
||||||
/// What buttons this player is pressing
|
/// What buttons this player is pressing
|
||||||
pub input: InputStatus,
|
pub input: InputStatus,
|
||||||
|
|
||||||
/// What the player currently has selected
|
|
||||||
pub selection: Option<()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerAgent {
|
impl PlayerAgent {
|
||||||
|
@ -24,7 +46,10 @@ impl PlayerAgent {
|
||||||
input: InputStatus::new(),
|
input: InputStatus::new(),
|
||||||
camera: Camera::new(),
|
camera: Camera::new(),
|
||||||
ship: Some(ship),
|
ship: Some(ship),
|
||||||
selection: None,
|
selection: PlayerSelection::OrbitingBody(SystemObjectHandle {
|
||||||
|
system_handle: SystemHandle { index: 0 },
|
||||||
|
body_index: 1,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,10 +70,9 @@ impl PlayerAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, ct: &Content, status: PlayerStatus) {
|
pub fn step(&mut self, ct: &Content, status: PlayerStatus) {
|
||||||
if self.input.v_scroll != 0.0 {
|
if self.input.get_v_scroll() != 0.0 {
|
||||||
self.camera.zoom = (self.camera.zoom + self.input.v_scroll)
|
self.camera.zoom = (self.camera.zoom + self.input.get_v_scroll())
|
||||||
.clamp(ct.get_config().zoom_min, ct.get_config().zoom_max);
|
.clamp(ct.get_config().zoom_min, ct.get_config().zoom_max);
|
||||||
self.input.v_scroll = 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.pos.is_some() {
|
if status.pos.is_some() {
|
||||||
|
|
|
@ -21,6 +21,7 @@ galactica-content = { workspace = true }
|
||||||
galactica-util = { workspace = true }
|
galactica-util = { workspace = true }
|
||||||
galactica-packer = { workspace = true }
|
galactica-packer = { workspace = true }
|
||||||
galactica-system = { workspace = true }
|
galactica-system = { workspace = true }
|
||||||
|
galactica-playeragent = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
cgmath = { workspace = true }
|
cgmath = { workspace = true }
|
||||||
|
|
|
@ -38,6 +38,7 @@ fn vertex_main(
|
||||||
instance: InstanceInput,
|
instance: InstanceInput,
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
|
// TODO: adjust position
|
||||||
out.position = vec4(vertex.position, 1.0);
|
out.position = vec4(vertex.position, 1.0);
|
||||||
out.diameter = instance.diameter;
|
out.diameter = instance.diameter;
|
||||||
out.stroke = instance.stroke;
|
out.stroke = instance.stroke;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use galactica_content::{Content, SystemHandle};
|
use galactica_content::{Content, SystemHandle};
|
||||||
use galactica_system::phys::{ParticleBuilder, PhysSim, PhysSimShipHandle};
|
use galactica_playeragent::PlayerAgent;
|
||||||
|
use galactica_system::phys::{ParticleBuilder, PhysSim};
|
||||||
use galactica_util::timing::Timing;
|
use galactica_util::timing::Timing;
|
||||||
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -15,7 +16,7 @@ pub struct RenderInput<'a> {
|
||||||
pub camera_pos: Point2<f32>,
|
pub camera_pos: Point2<f32>,
|
||||||
|
|
||||||
/// Player ship data
|
/// Player ship data
|
||||||
pub player_ship: PhysSimShipHandle,
|
pub player: &'a PlayerAgent,
|
||||||
|
|
||||||
/// The system we're currently in
|
/// The system we're currently in
|
||||||
pub current_system: SystemHandle,
|
pub current_system: SystemHandle,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
|
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
|
||||||
use galactica_system::phys::util;
|
use galactica_system::{data::ShipState, phys::util};
|
||||||
use galactica_util::constants::OBJECT_SPRITE_INSTANCE_LIMIT;
|
use galactica_util::constants::OBJECT_SPRITE_INSTANCE_LIMIT;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -19,6 +19,11 @@ impl GPUState {
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
for ship in state.systemsim.iter_ships() {
|
for ship in state.systemsim.iter_ships() {
|
||||||
|
match ship.data.get_state() {
|
||||||
|
ShipState::Collapsing { .. } | ShipState::Flying => {}
|
||||||
|
ShipState::Landed { .. } => continue,
|
||||||
|
}
|
||||||
|
|
||||||
let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap();
|
let r = state.systemsim.get_rigid_body(ship.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);
|
||||||
|
|
|
@ -31,9 +31,21 @@ impl Radar {
|
||||||
|
|
||||||
// TODO: maybe a cleaner solution for last posititon?
|
// TODO: maybe a cleaner solution for last posititon?
|
||||||
// This is necessary because the player may be dead or landed
|
// This is necessary because the player may be dead or landed
|
||||||
let player_ship = input.systemsim.get_ship(&input.player_ship).unwrap();
|
let player_ship = input
|
||||||
|
.systemsim
|
||||||
|
.get_ship(&galactica_system::phys::PhysSimShipHandle(
|
||||||
|
input.player.ship.unwrap(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
match player_ship.data.get_state() {
|
match player_ship.data.get_state() {
|
||||||
|
ShipState::Landed { target } => {
|
||||||
|
let landed_body = input.ct.get_system_object(*target);
|
||||||
|
self.last_player_position = Point2 {
|
||||||
|
x: landed_body.pos.x,
|
||||||
|
y: landed_body.pos.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
ShipState::Flying | ShipState::Collapsing { .. } => {
|
ShipState::Flying | ShipState::Collapsing { .. } => {
|
||||||
let player_body = input
|
let player_body = input
|
||||||
.systemsim
|
.systemsim
|
||||||
|
@ -44,6 +56,7 @@ impl Radar {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: don't hard-code these, add config options
|
||||||
let planet_sprite = input.ct.get_sprite_handle("ui::planetblip");
|
let planet_sprite = input.ct.get_sprite_handle("ui::planetblip");
|
||||||
let ship_sprite = input.ct.get_sprite_handle("ui::shipblip");
|
let ship_sprite = input.ct.get_sprite_handle("ui::shipblip");
|
||||||
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
|
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
|
||||||
|
@ -120,6 +133,9 @@ impl Radar {
|
||||||
// This will be None if this ship is dead.
|
// This will be None if this ship is dead.
|
||||||
// Stays around while the physics system runs a collapse sequence
|
// Stays around while the physics system runs a collapse sequence
|
||||||
let color = match s.data.get_state() {
|
let color = match s.data.get_state() {
|
||||||
|
ShipState::Landed { .. } => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ShipState::Collapsing { .. } => {
|
ShipState::Collapsing { .. } => {
|
||||||
// TODO: configurable
|
// TODO: configurable
|
||||||
[0.2, 0.2, 0.2, 1.0]
|
[0.2, 0.2, 0.2, 1.0]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::f32::consts::TAU;
|
|
||||||
|
|
||||||
use galactica_system::data::ShipState;
|
use galactica_system::data::ShipState;
|
||||||
use galactica_util::constants::{RADIALBAR_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT};
|
use galactica_util::constants::{RADIALBAR_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT};
|
||||||
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
datastructs::RenderState,
|
datastructs::RenderState,
|
||||||
|
@ -30,10 +29,15 @@ impl Status {
|
||||||
let current_shields;
|
let current_shields;
|
||||||
let current_hull;
|
let current_hull;
|
||||||
let max_hull;
|
let max_hull;
|
||||||
let player_ship = input.systemsim.get_ship(&input.player_ship).unwrap();
|
let player_ship = input
|
||||||
|
.systemsim
|
||||||
|
.get_ship(&galactica_system::phys::PhysSimShipHandle(
|
||||||
|
input.player.ship.unwrap(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
match player_ship.data.get_state() {
|
match player_ship.data.get_state() {
|
||||||
ShipState::Collapsing { .. } | ShipState::Flying => {
|
ShipState::Landed { .. } | ShipState::Collapsing { .. } | ShipState::Flying => {
|
||||||
max_shields = player_ship.data.get_outfits().get_shield_strength();
|
max_shields = player_ship.data.get_outfits().get_shield_strength();
|
||||||
current_shields = player_ship.data.get_shields();
|
current_shields = player_ship.data.get_shields();
|
||||||
current_hull = player_ship.data.get_hull();
|
current_hull = player_ship.data.get_hull();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{collections::HashMap, time::Instant};
|
use std::{collections::HashMap, time::Instant};
|
||||||
|
|
||||||
use super::{OutfitSet, ShipPersonality};
|
use super::{OutfitSet, ShipPersonality};
|
||||||
use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle};
|
use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle, SystemObjectHandle};
|
||||||
use rand::{rngs::ThreadRng, Rng};
|
use rand::{rngs::ThreadRng, Rng};
|
||||||
|
|
||||||
/// Ship state machine.
|
/// Ship state machine.
|
||||||
|
@ -21,9 +21,38 @@ pub enum ShipState {
|
||||||
/// How many seconds of the collapse sequence we've played
|
/// How many seconds of the collapse sequence we've played
|
||||||
elapsed: f32,
|
elapsed: f32,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// This ship is landed on a planet
|
||||||
|
Landed {
|
||||||
|
/// The planet this ship is landed on
|
||||||
|
target: SystemObjectHandle,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShipState {
|
impl ShipState {
|
||||||
|
/// Is this ship playing its collapse sequence?
|
||||||
|
pub fn is_collapsing(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Collapsing { .. } => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Is this ship flying in open space?
|
||||||
|
pub fn is_flying(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Flying => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this ship landed on a planet?
|
||||||
|
pub fn is_landed(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Landed { .. } => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// True if this ship has been destroyed and has finished it's collapse sequence.
|
/// True if this ship has been destroyed and has finished it's collapse sequence.
|
||||||
/// Ships are deleted once this is true.
|
/// Ships are deleted once this is true.
|
||||||
pub fn should_be_removed(&self) -> bool {
|
pub fn should_be_removed(&self) -> bool {
|
||||||
|
@ -33,11 +62,11 @@ impl ShipState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this ship playing its collapse sequence?
|
/// What planet is this ship landed on?
|
||||||
pub fn is_collapsing(&self) -> bool {
|
pub fn landed_on(&self) -> Option<SystemObjectHandle> {
|
||||||
match self {
|
match self {
|
||||||
Self::Collapsing { .. } => true,
|
Self::Landed { target } => Some(*target),
|
||||||
_ => false,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,14 +81,6 @@ impl ShipState {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this ship flying in open space?
|
|
||||||
pub fn is_flying(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Flying => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents all attributes of a single ship
|
/// Represents all attributes of a single ship
|
||||||
|
@ -115,6 +136,28 @@ impl ShipData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Land this ship on `target`
|
||||||
|
pub fn land_on(&mut self, target: SystemObjectHandle) -> bool {
|
||||||
|
if self.state.is_flying() {
|
||||||
|
self.state = ShipState::Landed { target };
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take off from `target`
|
||||||
|
pub fn unland(&mut self) -> bool {
|
||||||
|
if self.state.is_landed() {
|
||||||
|
self.state = ShipState::Flying;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Add an outfit to this ship
|
/// Add an outfit to this ship
|
||||||
pub fn add_outfit(&mut self, o: &Outfit) -> super::OutfitAddResult {
|
pub fn add_outfit(&mut self, o: &Outfit) -> super::OutfitAddResult {
|
||||||
let r = self.outfits.add(o);
|
let r = self.outfits.add(o);
|
||||||
|
@ -181,6 +224,20 @@ impl ShipData {
|
||||||
*elapsed += t;
|
*elapsed += t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShipState::Landed { .. } => {
|
||||||
|
// Cooldown guns
|
||||||
|
for (_, c) in &mut self.gun_cooldowns {
|
||||||
|
if *c > 0.0 {
|
||||||
|
*c = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regenerate shields
|
||||||
|
if self.shields != self.outfits.get_shield_strength() {
|
||||||
|
self.shields = self.outfits.get_shield_strength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ShipState::Flying => {
|
ShipState::Flying => {
|
||||||
// Cooldown guns
|
// Cooldown guns
|
||||||
for (_, c) in &mut self.gun_cooldowns {
|
for (_, c) in &mut self.gun_cooldowns {
|
||||||
|
|
|
@ -99,6 +99,7 @@ impl PhysSimShip {
|
||||||
ShipState::Flying => {
|
ShipState::Flying => {
|
||||||
return self.step_live(res, rigid_body, collider);
|
return self.step_live(res, rigid_body, collider);
|
||||||
}
|
}
|
||||||
|
ShipState::Landed { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
|
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
|
||||||
use galactica_content::{
|
use galactica_content::{
|
||||||
Content, FactionHandle, GunPoint, OutfitHandle, ProjectileCollider, Relationship, ShipHandle,
|
Content, FactionHandle, GunPoint, OutfitHandle, ProjectileCollider, Relationship, ShipHandle,
|
||||||
SystemHandle,
|
SystemHandle, SystemObjectHandle,
|
||||||
};
|
};
|
||||||
use galactica_playeragent::PlayerAgent;
|
use galactica_playeragent::PlayerAgent;
|
||||||
use nalgebra::{point, vector};
|
use nalgebra::{point, vector};
|
||||||
|
@ -13,7 +13,7 @@ use rapier2d::{
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, f32::consts::PI};
|
use std::{collections::HashMap, f32::consts::PI};
|
||||||
|
|
||||||
use crate::data::ShipPersonality;
|
use crate::data::{ShipPersonality, ShipState};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
controller::ShipController,
|
controller::ShipController,
|
||||||
|
@ -67,6 +67,40 @@ impl<'a> PhysSim {
|
||||||
return Some((r, p));
|
return Some((r, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn land_ship(&mut self, collider: ColliderHandle, target: SystemObjectHandle) {
|
||||||
|
let ship = self.ships.get_mut(&collider).unwrap();
|
||||||
|
self.rigid_body_set
|
||||||
|
.get_mut(ship.rigid_body)
|
||||||
|
.unwrap()
|
||||||
|
.set_enabled(false);
|
||||||
|
self.collider_set
|
||||||
|
.get_mut(ship.collider)
|
||||||
|
.unwrap()
|
||||||
|
.set_enabled(false);
|
||||||
|
|
||||||
|
ship.data.land_on(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unland_ship(&mut self, ct: &Content, collider: ColliderHandle) {
|
||||||
|
let ship = self.ships.get_mut(&collider).unwrap();
|
||||||
|
let obj = ship.data.get_state().landed_on().unwrap();
|
||||||
|
let obj = ct.get_system_object(obj);
|
||||||
|
ship.data.unland();
|
||||||
|
|
||||||
|
self.rigid_body_set
|
||||||
|
.get_mut(ship.rigid_body)
|
||||||
|
.unwrap()
|
||||||
|
.set_position(point![obj.pos.x, obj.pos.y].into(), true);
|
||||||
|
self.rigid_body_set
|
||||||
|
.get_mut(ship.rigid_body)
|
||||||
|
.unwrap()
|
||||||
|
.set_enabled(true);
|
||||||
|
self.collider_set
|
||||||
|
.get_mut(ship.collider)
|
||||||
|
.unwrap()
|
||||||
|
.set_enabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_ship(&mut self, res: &mut PhysStepResources, colliderhandle: ColliderHandle) {
|
fn remove_ship(&mut self, res: &mut PhysStepResources, colliderhandle: ColliderHandle) {
|
||||||
let ship = match self.ships.get(&colliderhandle) {
|
let ship = match self.ships.get(&colliderhandle) {
|
||||||
None => return,
|
None => return,
|
||||||
|
@ -213,16 +247,31 @@ impl PhysSim {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a player ship's controls
|
/// Update a player ship's controls
|
||||||
pub fn update_player_controls(&mut self, player: &PlayerAgent) {
|
pub fn update_player_controls(&mut self, ct: &Content, player: &PlayerAgent) {
|
||||||
if player.ship.is_none() {
|
if player.ship.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ship_object = self.ships.get_mut(&player.ship.unwrap());
|
let ship_object = self.ships.get_mut(&player.ship.unwrap());
|
||||||
if let Some(ship_object) = ship_object {
|
if let Some(ship_object) = ship_object {
|
||||||
ship_object.controls.guns = player.input.key_guns;
|
ship_object.controls.guns = player.input.pressed_guns();
|
||||||
ship_object.controls.left = player.input.key_left;
|
ship_object.controls.left = player.input.pressed_left();
|
||||||
ship_object.controls.right = player.input.key_right;
|
ship_object.controls.right = player.input.pressed_right();
|
||||||
ship_object.controls.thrust = player.input.key_thrust;
|
ship_object.controls.thrust = player.input.pressed_thrust();
|
||||||
|
|
||||||
|
if player.input.pressed_land() {
|
||||||
|
match ship_object.data.get_state() {
|
||||||
|
ShipState::Flying => {
|
||||||
|
self.land_ship(
|
||||||
|
player.ship.unwrap(),
|
||||||
|
player.selection.get_planet().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ShipState::Landed { .. } => {
|
||||||
|
self.unland_ship(ct, player.ship.unwrap());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,48 +296,54 @@ impl PhysSim {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This ship is playing a collapse sequence
|
match ship.data.get_state() {
|
||||||
// and needs no additional logic
|
ShipState::Landed { .. } => {}
|
||||||
if ship.data.get_state().is_collapsing() {
|
|
||||||
let ship = self.ships.get_mut(&collider).unwrap();
|
|
||||||
ship.step(
|
|
||||||
res,
|
|
||||||
&mut self.rigid_body_set[ship.rigid_body],
|
|
||||||
&mut self.collider_set[ship.collider],
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute new controls
|
ShipState::Collapsing { .. } => {
|
||||||
let controls;
|
let ship = self.ships.get_mut(&collider).unwrap();
|
||||||
let b = self.ship_behaviors.get_mut(&collider).unwrap();
|
ship.step(
|
||||||
controls = b.update_controls(&res, &self.rigid_body_set, &self.ships, ship.collider);
|
res,
|
||||||
|
&mut self.rigid_body_set[ship.rigid_body],
|
||||||
|
&mut self.collider_set[ship.collider],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let ship = self.ships.get_mut(&collider).unwrap();
|
ShipState::Flying => {
|
||||||
|
// Compute new controls
|
||||||
|
// This is why we borrow immutably first
|
||||||
|
let controls;
|
||||||
|
let b = self.ship_behaviors.get_mut(&collider).unwrap();
|
||||||
|
controls =
|
||||||
|
b.update_controls(&res, &self.rigid_body_set, &self.ships, ship.collider);
|
||||||
|
|
||||||
if let Some(controls) = controls {
|
// Re-borrow mutably to apply changes
|
||||||
ship.controls = controls;
|
let ship = self.ships.get_mut(&collider).unwrap();
|
||||||
}
|
|
||||||
ship.step(
|
|
||||||
res,
|
|
||||||
&mut self.rigid_body_set[ship.rigid_body],
|
|
||||||
&mut self.collider_set[ship.collider],
|
|
||||||
);
|
|
||||||
|
|
||||||
// If we're firing, try to fire each gun
|
if let Some(controls) = controls {
|
||||||
if ship.controls.guns {
|
ship.controls = controls;
|
||||||
// TODO: don't allocate here. This is a hack to satisfy the borrow checker,
|
}
|
||||||
// convert this to a refcell or do the replace dance.
|
ship.step(
|
||||||
let pairs: Vec<(GunPoint, Option<OutfitHandle>)> = ship
|
res,
|
||||||
.data
|
&mut self.rigid_body_set[ship.rigid_body],
|
||||||
.get_outfits()
|
&mut self.collider_set[ship.collider],
|
||||||
.iter_gun_points()
|
);
|
||||||
.map(|(p, o)| (p.clone(), o.clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for (gun, outfit) in pairs {
|
// If we're firing, try to fire each gun
|
||||||
if ship.data.fire_gun(res.ct, &gun) {
|
if ship.controls.guns {
|
||||||
projectiles.push((ship.collider, gun.clone(), outfit.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.collider, gun.clone(), outfit.unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue