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 { 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>, 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> = 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 { let mut sprites: Vec = 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 { return self .ui .build_radar(&self.player, &self.physics, &self.content); } }