224 lines
5.3 KiB
Rust
224 lines
5.3 KiB
Rust
use cgmath::{Deg, InnerSpace, Point2};
|
|
use galactica_render::{AnchoredUiPosition, ObjectSprite, UiSprite};
|
|
use std::time::Instant;
|
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
|
|
|
use super::{camera::Camera, outfits, system::System};
|
|
use crate::{
|
|
consts, content,
|
|
inputstatus::InputStatus,
|
|
physics::{util, Physics, ShipHandle},
|
|
shipbehavior::{self, ShipBehavior},
|
|
};
|
|
|
|
struct Ui {}
|
|
|
|
impl Ui {
|
|
fn new() -> Self {
|
|
Self {}
|
|
}
|
|
|
|
fn build_radar(
|
|
&self,
|
|
player: &ShipHandle,
|
|
physics: &Physics,
|
|
ct: &content::Content,
|
|
) -> Vec<UiSprite> {
|
|
let mut out = Vec::new();
|
|
|
|
let radar_range = 2000.0;
|
|
let radar_size = 300.0;
|
|
|
|
out.push(UiSprite {
|
|
texture: ct.get_texture_handle("ui::radar"),
|
|
pos: AnchoredUiPosition::NorthWest(Point2 { x: 10.0, y: -10.0 }),
|
|
dimensions: Point2 {
|
|
x: radar_size,
|
|
y: radar_size,
|
|
},
|
|
angle: Deg(0.0),
|
|
});
|
|
|
|
let (_, pr) = physics.get_ship_body(player).unwrap();
|
|
let pr = util::rigidbody_position(pr);
|
|
for (s, r) in physics.iter_ship_body() {
|
|
if s.physics_handle == *player {
|
|
continue;
|
|
}
|
|
let r = util::rigidbody_position(r);
|
|
let d = r - pr;
|
|
let m = d.magnitude() / radar_range;
|
|
if m < 0.8 {
|
|
out.push(UiSprite {
|
|
texture: ct.get_texture_handle("ui::blip"),
|
|
pos: AnchoredUiPosition::NorthWest(
|
|
Point2 {
|
|
x: radar_size / 2.0 + 10.0,
|
|
y: radar_size / -2.0 - 10.0,
|
|
} + (d / radar_range * 150.0),
|
|
),
|
|
dimensions: Point2 { x: 1.0, y: 1.0 } * 5.0f32.min((0.8 - m) * 50.0),
|
|
angle: Deg(0.0),
|
|
});
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
}
|
|
|
|
pub struct Game {
|
|
pub input: InputStatus,
|
|
pub last_update: Instant,
|
|
pub player: ShipHandle,
|
|
pub system: System,
|
|
pub camera: Camera,
|
|
paused: bool,
|
|
pub time_scale: f32,
|
|
|
|
ui: Ui,
|
|
physics: Physics,
|
|
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
|
|
content: content::Content,
|
|
}
|
|
|
|
impl Game {
|
|
pub fn new(ct: content::Content) -> Self {
|
|
let mut physics = Physics::new();
|
|
|
|
let mut o1 = outfits::ShipOutfits::new(&ct.ships[0]);
|
|
o1.add(ct.outfits[0].clone());
|
|
o1.add_gun(ct.guns[0].clone());
|
|
o1.add_gun(ct.guns[0].clone());
|
|
|
|
let h1 = physics.add_ship(
|
|
&ct.ships[0],
|
|
o1,
|
|
Point2 { x: 0.0, y: 0.0 },
|
|
content::FactionHandle { index: 0 },
|
|
);
|
|
|
|
let h2 = physics.add_ship(
|
|
&ct.ships[0],
|
|
outfits::ShipOutfits::new(&ct.ships[0]),
|
|
Point2 { x: 300.0, y: 300.0 },
|
|
content::FactionHandle { index: 0 },
|
|
);
|
|
|
|
let mut o2 = outfits::ShipOutfits::new(&ct.ships[0]);
|
|
o2.add(ct.outfits[0].clone());
|
|
o2.add_gun(ct.guns[0].clone());
|
|
let h3 = physics.add_ship(
|
|
&ct.ships[0],
|
|
o2,
|
|
Point2 {
|
|
x: -300.0,
|
|
y: 300.0,
|
|
},
|
|
content::FactionHandle { index: 1 },
|
|
);
|
|
|
|
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
|
|
shipbehaviors.push(shipbehavior::Player::new(h1));
|
|
shipbehaviors.push(shipbehavior::Dummy::new(h2));
|
|
shipbehaviors.push(shipbehavior::Point::new(h3));
|
|
|
|
Game {
|
|
last_update: Instant::now(),
|
|
input: InputStatus::new(),
|
|
player: h1,
|
|
|
|
camera: Camera {
|
|
pos: (0.0, 0.0).into(),
|
|
zoom: 500.0,
|
|
},
|
|
system: System::new(&ct.systems[0]),
|
|
|
|
paused: false,
|
|
time_scale: 1.0,
|
|
physics,
|
|
shipbehaviors,
|
|
content: ct,
|
|
ui: Ui::new(),
|
|
}
|
|
}
|
|
|
|
pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) {
|
|
self.input.process_key(state, key)
|
|
}
|
|
|
|
pub fn process_click(&mut self, state: &ElementState, key: &MouseButton) {
|
|
self.input.process_click(state, key)
|
|
}
|
|
|
|
pub fn process_scroll(&mut self, delta: &MouseScrollDelta, phase: &TouchPhase) {
|
|
self.input.process_scroll(delta, phase)
|
|
}
|
|
|
|
pub fn set_paused(&mut self, pause: bool) {
|
|
if pause {
|
|
self.paused = true;
|
|
self.input.release_all()
|
|
} else {
|
|
self.paused = false;
|
|
}
|
|
}
|
|
|
|
pub fn update(&mut self) {
|
|
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
|
|
|
|
self.shipbehaviors.retain_mut(|b| {
|
|
if self.physics.get_ship_mut(&b.get_handle()).is_none() {
|
|
false
|
|
} else {
|
|
b.update_controls(&mut self.physics, &self.input, &self.content);
|
|
true
|
|
}
|
|
});
|
|
|
|
self.physics.step(t, &self.content);
|
|
|
|
if self.input.v_scroll != 0.0 {
|
|
self.camera.zoom =
|
|
(self.camera.zoom + self.input.v_scroll).clamp(consts::ZOOM_MIN, consts::ZOOM_MAX);
|
|
self.input.v_scroll = 0.0;
|
|
}
|
|
|
|
// TODO: Camera physics
|
|
let r = self
|
|
.physics
|
|
.get_ship_mut(&self.player)
|
|
.unwrap()
|
|
.physics_handle;
|
|
let r = self.physics.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
|
|
let ship_pos = util::rigidbody_position(r);
|
|
self.camera.pos = ship_pos;
|
|
self.last_update = Instant::now();
|
|
}
|
|
|
|
pub fn get_object_sprites(&self) -> Vec<ObjectSprite> {
|
|
let mut sprites: Vec<ObjectSprite> = Vec::new();
|
|
|
|
sprites.append(&mut self.system.get_sprites());
|
|
sprites.extend(self.physics.get_ship_sprites());
|
|
|
|
// Make sure sprites are drawn in the correct order
|
|
// (note the reversed a, b in the comparator)
|
|
//
|
|
// TODO: maybe use a gpu depth buffer instead?
|
|
// I've tried this, but it doesn't seem to work with transparent textures.
|
|
sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
|
|
|
|
// Don't waste time sorting these, they should always be on top.
|
|
sprites.extend(self.physics.get_projectile_sprites());
|
|
|
|
return sprites;
|
|
}
|
|
|
|
pub fn get_ui_sprites(&self) -> Vec<UiSprite> {
|
|
return self
|
|
.ui
|
|
.build_radar(&self.player, &self.physics, &self.content);
|
|
}
|
|
}
|