Compare commits

...

2 Commits

Author SHA1 Message Date
Mark 3b7ac5bc9a
Added basic landing 2024-01-12 14:34:31 -08:00
Mark c920ebefbc
Comments 2024-01-12 14:34:22 -08:00
19 changed files with 393 additions and 100 deletions

1
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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 {

View File

@ -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];

View File

@ -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};

View File

@ -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,
}); });

View File

@ -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 {

View File

@ -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();
} }

View File

@ -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
}
}

View File

@ -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() {

View File

@ -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 }

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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]

View File

@ -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();

View File

@ -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 {

View File

@ -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 { .. } => {}
} }
} }

View File

@ -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()));
}
}
} }
} }
} }