Galactica/src/game/game.rs
2024-01-01 15:46:39 -08:00

235 lines
5.7 KiB
Rust

use cgmath::{Deg, InnerSpace, Point2};
use content::SystemHandle;
use std::time::Instant;
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
use super::camera::Camera;
use crate::{content, inputstatus::InputStatus};
use galactica_constants;
use galactica_gameobject as object;
use galactica_render::{AnchoredUiPosition, ObjectSprite, UiSprite};
use galactica_shipbehavior::{behavior, ShipBehavior};
use galactica_world::{util, ShipPhysicsHandle, World};
struct Ui {}
impl Ui {
fn new() -> Self {
Self {}
}
fn build_radar(
&self,
player: &ShipPhysicsHandle,
physics: &World,
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: ShipPhysicsHandle,
pub system: object::System,
pub camera: Camera,
paused: bool,
pub time_scale: f32,
ui: Ui,
physics: World,
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
content: content::Content,
}
impl Game {
pub fn new(ct: content::Content) -> Self {
let mut physics = World::new();
let ss = ct.get_ship(content::ShipHandle { index: 0 });
let mut o1 = object::OutfitSet::new(ss);
o1.add(&ct, content::OutfitHandle { index: 0 });
o1.add_gun(&ct, content::GunHandle { index: 0 });
o1.add_gun(&ct, content::GunHandle { index: 0 });
let s = object::Ship::new(
&ct,
content::ShipHandle { index: 0 },
content::FactionHandle { index: 0 },
o1,
);
let h1 = physics.add_ship(&ct, s, Point2 { x: 0.0, y: 0.0 });
let s = object::Ship::new(
&ct,
content::ShipHandle { index: 0 },
content::FactionHandle { index: 0 },
object::OutfitSet::new(ss),
);
let h2 = physics.add_ship(&ct, s, Point2 { x: 300.0, y: 300.0 });
let mut o1 = object::OutfitSet::new(ss);
o1.add(&ct, content::OutfitHandle { index: 0 });
o1.add_gun(&ct, content::GunHandle { index: 0 });
let s = object::Ship::new(
&ct,
content::ShipHandle { index: 0 },
content::FactionHandle { index: 0 },
o1,
);
let h3 = physics.add_ship(
&ct,
s,
Point2 {
x: -300.0,
y: 300.0,
},
);
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
shipbehaviors.push(behavior::Player::new(h1));
shipbehaviors.push(behavior::Dummy::new(h2));
shipbehaviors.push(behavior::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: object::System::new(&ct, SystemHandle { index: 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.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(galactica_constants::ZOOM_MIN, galactica_constants::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(&self.content));
// 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);
}
}