Plantes and asteroids are now recognized as obstacles for ships.master
| @@ -323,6 +323,20 @@ where | |||
| Vector3::new(mul!(2, 0), mul!(2, 1), mul!(2, 2)), | |||
| ) | |||
| } | |||
| #[inline] | |||
| pub fn transform<V: Into<Vector2<T>>>(&self, v: V) -> Vector2<T> { | |||
| let m = self; | |||
| let v = V::into(v); | |||
| macro_rules! mul { | |||
| ($i:tt) => { | |||
| m[0][$i] * v[0] + m[1][$i] * v[1] + m[2][$i] * T::one() | |||
| }; | |||
| } | |||
| Vector2::new(mul!(0), mul!(1)) | |||
| } | |||
| } | |||
| impl<T> Matrix3<T> | |||
| @@ -5,7 +5,7 @@ use glc::vector::Vector4f; | |||
| pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0; | |||
| pub const UNIFORM_BUFFER_INDEX_UNIFORM: gl::GLuint = 1; | |||
| pub const SHIP_SIZE: f32 = 15.0; | |||
| pub const SHIP_SIZE: f32 = 25.0; | |||
| pub const PLANET_SIZE: f32 = 200.0; | |||
| pub const ASTEROID_SIZE: f32 = 100.0; | |||
| @@ -1,5 +1,8 @@ | |||
| use glc::vector::Vector4f; | |||
| use space_crush_common::components::{Position, Ship, Velocity}; | |||
| use space_crush_common::{ | |||
| components::{Position, Ship, ShipObstacle, Velocity}, | |||
| continue_if_none, | |||
| }; | |||
| use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | |||
| use crate::resources::Geometry; | |||
| @@ -10,9 +13,9 @@ pub struct Ships; | |||
| #[derive(SystemData)] | |||
| pub struct ShipsData<'a> { | |||
| geometry: WriteExpect<'a, Geometry>, | |||
| position: ReadStorage<'a, Position>, | |||
| velocity: ReadStorage<'a, Velocity>, | |||
| ship: ReadStorage<'a, Ship>, | |||
| positions: ReadStorage<'a, Position>, | |||
| velocities: ReadStorage<'a, Velocity>, | |||
| ships: ReadStorage<'a, Ship>, | |||
| } | |||
| impl<'a> System<'a> for Ships { | |||
| @@ -21,24 +24,35 @@ impl<'a> System<'a> for Ships { | |||
| fn run(&mut self, data: Self::SystemData) { | |||
| let ShipsData { | |||
| mut geometry, | |||
| position, | |||
| velocity, | |||
| ship, | |||
| positions, | |||
| velocities, | |||
| ships, | |||
| } = data; | |||
| gl::enable(gl::BLEND); | |||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||
| for (p, v, s) in (&position, &velocity, &ship).join() { | |||
| for (position, velocity, ship) in (&positions, &velocities, &ships).join() { | |||
| let ship_pos = position.pos; | |||
| geometry.render_lines( | |||
| Vector4f::new(0.0, 0.0, 1.0, 0.2), | |||
| &[p.pos, p.pos + v.dir * v.speed], | |||
| &[ship_pos, ship_pos + velocity.dir * velocity.speed], | |||
| ); | |||
| geometry.render_lines( | |||
| Vector4f::new(1.0, 0.0, 0.0, 0.2), | |||
| &[p.pos, p.pos + s.target_dir * 100.0], | |||
| &[ship_pos, ship_pos + ship.target_dir * 100.0], | |||
| ); | |||
| geometry.render_lines( | |||
| Vector4f::new(1.0, 1.0, 1.0, 0.2), | |||
| &[ship_pos, ship.target_pos], | |||
| ); | |||
| geometry.render_lines(Vector4f::new(1.0, 1.0, 1.0, 0.2), &[p.pos, s.target_pos]); | |||
| if let ShipObstacle::Known(obstacle) = ship.obstacle { | |||
| let obstacle_pos = continue_if_none!(positions.get(obstacle)).pos; | |||
| geometry.render_lines(Vector4f::new(0.0, 1.0, 0.0, 0.2), &[ship_pos, obstacle_pos]); | |||
| } | |||
| } | |||
| gl::disable(gl::BLEND); | |||
| @@ -7,6 +7,7 @@ pub mod render; | |||
| pub mod resources; | |||
| pub mod systems; | |||
| use glc::matrix::Matrix4f; | |||
| use specs::{Dispatcher, DispatcherBuilder, Entity, World}; | |||
| pub use error::Error; | |||
| @@ -30,12 +31,14 @@ impl<'a, 'b> App<'a, 'b> { | |||
| let events = Events::new(world)?; | |||
| let window = Window::new(events.handle(), &config)?; | |||
| let camera = Camera::new()?; | |||
| let mut camera = Camera::new()?; | |||
| let uniform = Uniform::new()?; | |||
| let geometry = Geometry::new(world)?; | |||
| let input_state = InputState::default(); | |||
| let player_state = PlayerState::new(player_id); | |||
| camera.update(Matrix4f::scale(0.25))?; | |||
| world.insert(config); | |||
| world.insert(camera); | |||
| world.insert(uniform); | |||
| @@ -60,8 +60,8 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
| use space_crush_app::components::FleetInfo; | |||
| use space_crush_common::{ | |||
| components::{ | |||
| Asteroid, AsteroidType, Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, | |||
| Shape, Ship, ShipType, Velocity, | |||
| Asteroid, AsteroidType, Fleet, FleetOwned, Obstacle, Planet, Player, PlayerOwned, | |||
| Position, Shape, Ship, ShipType, Velocity, | |||
| }, | |||
| misc::{PersistWorld, Persistence}, | |||
| }; | |||
| @@ -79,88 +79,80 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
| ) | |||
| .unwrap(); | |||
| let planet = world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(PlayerOwned { owner: player_id }) | |||
| .with(Position { | |||
| pos: Vector2f::default(), | |||
| shape: Shape::Circle(250.0), | |||
| let planets = (0..3) | |||
| .map(|i| { | |||
| let x = 2000.0 * (i as f32 - 1.0); | |||
| let y = 0.0; | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(PlayerOwned { owner: player_id }) | |||
| .with(Position { | |||
| pos: Vector2f::new(x, y), | |||
| shape: Shape::Circle(250.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Obstacle {}) | |||
| .with(Planet {}) | |||
| .build() | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Planet {}) | |||
| .build(); | |||
| .collect::<Vec<_>>(); | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Position { | |||
| pos: Vector2f::new(2000.0, 0.0), | |||
| shape: Shape::Circle(250.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Planet {}) | |||
| .build(); | |||
| for i in 0..4 { | |||
| let i_x: isize = if i & 1 == 0 { -1 } else { 1 }; | |||
| let i_y: isize = if i & 2 == 0 { -1 } else { 1 }; | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Position { | |||
| pos: Vector2f::new(1000.0, -1000.0), | |||
| shape: Shape::Circle(100.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Metal, | |||
| }) | |||
| .build(); | |||
| let x = 1000.0 * i_x as f32; | |||
| let y = 1000.0 * i_y as f32; | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Position { | |||
| pos: Vector2f::new(1000.0, 1000.0), | |||
| shape: Shape::Circle(100.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Asteroid { | |||
| type_: AsteroidType::Crystal, | |||
| }) | |||
| .build(); | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Position { | |||
| pos: Vector2f::new(x, y), | |||
| shape: Shape::Circle(100.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 125.0, | |||
| orbit_max: 175.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Obstacle {}) | |||
| .with(Asteroid { | |||
| type_: match i_x * i_y { | |||
| -1 => AsteroidType::Metal, | |||
| 1 => AsteroidType::Crystal, | |||
| _ => unreachable!(), | |||
| }, | |||
| }) | |||
| .build(); | |||
| } | |||
| for i in 0..999 { | |||
| let r = 325.0 + 100.0 * random::<f32>(); | |||
| let a = Angle::Deg(360.0 * random::<f32>()); | |||
| let x = r * a.cos(); | |||
| let y = r * a.sin(); | |||
| for i in 0..30 { | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(PlayerOwned { owner: player_id }) | |||
| .with(Position { | |||
| pos: Vector2f::new( | |||
| 500.0 * random::<f32>() - 250.0, | |||
| 500.0 * random::<f32>() - 250.0, | |||
| ), | |||
| pos: Vector2f::new(x, y), | |||
| shape: Shape::Dot, | |||
| }) | |||
| .with(Velocity { | |||
| dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | |||
| speed: 100.0, | |||
| }) | |||
| .with(FleetOwned { owner: planet }) | |||
| .with(FleetOwned { owner: planets[1] }) | |||
| .with(Ship { | |||
| type_: match i % 3 { | |||
| 0 => ShipType::Fighter, | |||
| @@ -171,6 +163,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
| agility: Angle::Deg(360.0), | |||
| target_pos: Default::default(), | |||
| target_dir: Default::default(), | |||
| obstacle: Default::default(), | |||
| }) | |||
| .build(); | |||
| } | |||
| @@ -267,7 +267,7 @@ impl FleetSelect { | |||
| self.marker = d.camera.view_to_world(self.mouse_pos) - position.pos; | |||
| self.zoom = d.camera.view().axis_x.as_vec3().length(); | |||
| self.shape_size = position.shape.radius().unwrap_or(0.0); | |||
| self.shape_size = position.shape.radius(); | |||
| self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom; | |||
| self.ring1 = self.ring0 + FLEET_SELECT_WIDTH / self.zoom; | |||
| @@ -1,5 +1,6 @@ | |||
| mod asteroid; | |||
| mod fleet; | |||
| mod obstacle; | |||
| mod planet; | |||
| mod player; | |||
| mod position; | |||
| @@ -8,8 +9,9 @@ mod velocity; | |||
| pub use asteroid::{Asteroid, Type as AsteroidType}; | |||
| pub use fleet::{Fleet, Owned as FleetOwned}; | |||
| pub use obstacle::Obstacle; | |||
| pub use planet::Planet; | |||
| pub use player::{Owned as PlayerOwned, Player}; | |||
| pub use position::{Position, Shape}; | |||
| pub use ship::{Count as ShipCount, Ship, Type as ShipType}; | |||
| pub use ship::{Count as ShipCount, Obstacle as ShipObstacle, Ship, Type as ShipType}; | |||
| pub use velocity::Velocity; | |||
| @@ -0,0 +1,9 @@ | |||
| use serde::{Deserialize, Serialize}; | |||
| use specs::{Component, NullStorage}; | |||
| #[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
| pub struct Obstacle {} | |||
| impl Component for Obstacle { | |||
| type Storage = NullStorage<Self>; | |||
| } | |||
| @@ -19,10 +19,10 @@ impl Component for Position { | |||
| } | |||
| impl Shape { | |||
| pub fn radius(&self) -> Option<f32> { | |||
| pub fn radius(&self) -> f32 { | |||
| match self { | |||
| Self::Dot => Some(0.0), | |||
| Self::Circle(r) => Some(*r), | |||
| Self::Dot => 0.0, | |||
| Self::Circle(r) => *r, | |||
| } | |||
| } | |||
| @@ -3,7 +3,7 @@ use std::ops::{Index, IndexMut, Mul}; | |||
| use glc::{matrix::Angle, vector::Vector2f}; | |||
| use serde::{Deserialize, Serialize}; | |||
| use specs::{Component, VecStorage}; | |||
| use specs::{Component, Entity, VecStorage}; | |||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||
| pub struct Ship { | |||
| @@ -11,6 +11,15 @@ pub struct Ship { | |||
| pub agility: Angle<f32>, | |||
| pub target_pos: Vector2f, | |||
| pub target_dir: Vector2f, | |||
| #[serde(skip)] | |||
| pub obstacle: Obstacle, | |||
| } | |||
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |||
| pub enum Obstacle { | |||
| Known(Entity), | |||
| Search, | |||
| Done, | |||
| } | |||
| #[derive(Copy, Clone, Debug, Default)] | |||
| @@ -31,6 +40,12 @@ impl Component for Ship { | |||
| type Storage = VecStorage<Self>; | |||
| } | |||
| impl Default for Obstacle { | |||
| fn default() -> Self { | |||
| Self::Search | |||
| } | |||
| } | |||
| impl Count { | |||
| pub fn all() -> Self { | |||
| Self { | |||
| @@ -4,10 +4,10 @@ use glc::{matrix::Angle, vector::Vector2f}; | |||
| pub const SHIP_ORBIT_DISTANCE_MAX: f32 = 1.10; | |||
| /// Minimum angle between old and new target position in orbit | |||
| pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle<f32> = Angle::Deg(7000.0); | |||
| pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle<f32> = Angle::Deg(5000.0); | |||
| /// Random angle between old and new target position in orbit | |||
| pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(4000.0); | |||
| pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(5000.0); | |||
| /// Agility of ships inside orbit | |||
| pub const SHIP_ORBIT_AGILITY: Angle<f32> = Angle::Deg(90.0); | |||
| @@ -17,3 +17,13 @@ macro_rules! break_if_none { | |||
| } | |||
| }; | |||
| } | |||
| #[macro_export] | |||
| macro_rules! continue_if_none { | |||
| ($value:expr) => { | |||
| match $value { | |||
| Some(value) => value, | |||
| None => continue, | |||
| } | |||
| }; | |||
| } | |||
| @@ -1,5 +1,15 @@ | |||
| #[derive(Default)] | |||
| pub struct Global { | |||
| pub fps: usize, | |||
| pub delta: f32, | |||
| pub world_speed: f32, | |||
| } | |||
| impl Default for Global { | |||
| fn default() -> Self { | |||
| Self { | |||
| fps: 0, | |||
| delta: 0.0, | |||
| world_speed: 1.0, | |||
| } | |||
| } | |||
| } | |||
| @@ -30,7 +30,9 @@ impl<'a> System<'a> for Movement { | |||
| (&mut position, &velocity) | |||
| .par_join() | |||
| .for_each(|(position, velocity)| { | |||
| position.pos = position.pos + velocity.dir * velocity.speed * global.delta; | |||
| let delta = global.delta * global.world_speed; | |||
| position.pos = position.pos + velocity.dir * velocity.speed * delta; | |||
| }); | |||
| } | |||
| } | |||
| @@ -1,16 +1,17 @@ | |||
| use glc::{ | |||
| math::{linear_step, sqr}, | |||
| matrix::Matrix3f, | |||
| vector::{Vector2f, Vector3f}, | |||
| vector::{Angle, Vector2f, Vector3f}, | |||
| }; | |||
| use rand::random; | |||
| use shrev::ReaderId; | |||
| use specs::{ | |||
| hibitset::BitSet, prelude::*, world::Index, ParJoin, Read, ReadStorage, System, WriteStorage, | |||
| hibitset::BitSet, prelude::*, world::Index, Entities, ParJoin, Read, ReadStorage, System, | |||
| WriteStorage, | |||
| }; | |||
| use crate::{ | |||
| components::{Fleet, FleetOwned, Position, Ship, Velocity}, | |||
| components::{Fleet, FleetOwned, Obstacle, Position, Ship, ShipObstacle, Velocity}, | |||
| constants::{ | |||
| SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND, | |||
| SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X, | |||
| @@ -28,16 +29,20 @@ pub struct Ships { | |||
| #[derive(SystemData)] | |||
| pub struct ShipsData<'a> { | |||
| global: Read<'a, Global>, | |||
| entities: Entities<'a>, | |||
| ships: WriteStorage<'a, Ship>, | |||
| velocities: WriteStorage<'a, Velocity>, | |||
| fleet_owned: ReadStorage<'a, FleetOwned>, | |||
| positions: ReadStorage<'a, Position>, | |||
| obstacles: ReadStorage<'a, Obstacle>, | |||
| fleets: ReadStorage<'a, Fleet>, | |||
| } | |||
| struct Processor<'a> { | |||
| need_update: &'a BitSet, | |||
| entities: &'a Entities<'a>, | |||
| positions: &'a ReadStorage<'a, Position>, | |||
| obstacles: &'a ReadStorage<'a, Obstacle>, | |||
| fleets: &'a ReadStorage<'a, Fleet>, | |||
| delta: f32, | |||
| } | |||
| @@ -85,10 +90,12 @@ impl<'a> System<'a> for Ships { | |||
| fn run(&mut self, data: Self::SystemData) { | |||
| let ShipsData { | |||
| global, | |||
| entities, | |||
| mut ships, | |||
| mut velocities, | |||
| fleet_owned, | |||
| positions, | |||
| obstacles, | |||
| fleets, | |||
| } = data; | |||
| @@ -97,18 +104,22 @@ impl<'a> System<'a> for Ships { | |||
| /* update ships */ | |||
| let processor = Processor { | |||
| need_update: &self.need_update, | |||
| entities: &entities, | |||
| positions: &positions, | |||
| obstacles: &obstacles, | |||
| fleets: &fleets, | |||
| delta: global.delta, | |||
| delta: global.delta * global.world_speed, | |||
| }; | |||
| ( | |||
| let data = ( | |||
| positions.mask(), | |||
| &mut ships, | |||
| &mut velocities, | |||
| &positions, | |||
| &fleet_owned, | |||
| ) | |||
| .par_join() | |||
| ); | |||
| data.par_join() | |||
| .for_each(|(id, ship, velocity, position, fleet_owned)| { | |||
| processor.progress_ship(id, ship, velocity, position, fleet_owned); | |||
| }); | |||
| @@ -132,8 +143,8 @@ impl Processor<'_> { | |||
| let target_dir = ship.target_dir; | |||
| let orbit_to_target = target_pos - orbit_pos; | |||
| let orbit_to_ship = position.pos - orbit_pos; | |||
| let ship_to_target = target_pos - position.pos; | |||
| let orbit_to_ship = ship_pos - orbit_pos; | |||
| let mut ship_to_target = target_pos - position.pos; | |||
| let r_ship = orbit_to_ship.length_sqr(); | |||
| let r_target = orbit_to_target.length_sqr(); | |||
| @@ -160,27 +171,110 @@ impl Processor<'_> { | |||
| -1.0 | |||
| }; | |||
| let add = SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random(); | |||
| let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X); | |||
| let angle = angle | |||
| + (SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random()) * dir | |||
| / fleet.orbit_max; | |||
| let radius = | |||
| fleet.orbit_min + (fleet.orbit_max - fleet.orbit_min) * random::<f32>(); | |||
| let angle = angle + add * dir / orbit_max; | |||
| let radius = orbit_min + (orbit_max - orbit_min) * random::<f32>(); | |||
| Vector2f::new( | |||
| orbit_pos.x + radius * angle.cos(), | |||
| orbit_pos.y + radius * angle.sin(), | |||
| ) | |||
| } else { | |||
| ship.obstacle = ShipObstacle::Search; | |||
| orbit_pos | |||
| }; | |||
| ship.target_pos = target_pos; | |||
| ship.target_dir = (target_pos - ship_pos).normalize(); | |||
| ship_to_target = target_pos - ship_pos; | |||
| } | |||
| /* check if obstacle is still valid */ | |||
| if ship_in_orbit { | |||
| ship.obstacle = ShipObstacle::Done; | |||
| } else if let ShipObstacle::Known(obstacle) = ship.obstacle { | |||
| if let Some(position) = self.positions.get(obstacle) { | |||
| let obstacle_fleet = self.fleets.get(obstacle).unwrap(); | |||
| let obstacle_pos = position.pos; | |||
| let ship_to_obstacle = obstacle_pos - ship_pos; | |||
| let obstacle_angle = ship_to_target | |||
| .angle2(&ship_to_obstacle) | |||
| .into_deg() | |||
| .into_inner() | |||
| .abs(); | |||
| let orbit_sqr = obstacle_fleet.orbit_max * obstacle_fleet.orbit_max; | |||
| if (obstacle_angle > 90.0 && ship_to_obstacle.length_sqr() > orbit_sqr) | |||
| || obstacle_angle > 170.0 | |||
| { | |||
| ship.obstacle = ShipObstacle::Search; | |||
| } | |||
| } else { | |||
| ship.obstacle = ShipObstacle::Search; | |||
| } | |||
| } | |||
| /* find obstacle */ | |||
| if !ship_in_orbit && ship.obstacle == ShipObstacle::Search { | |||
| let mut dist_sqr = f32::MAX; | |||
| for (e, position, _) in (self.entities, self.positions, self.obstacles).join() { | |||
| let obstacle_pos = position.pos; | |||
| let ship_to_obstacle = obstacle_pos - ship_pos; | |||
| if ship_to_target * ship_to_obstacle < 0.0 { | |||
| continue; // obstacle is behind the ship | |||
| } | |||
| let len_sqr = ship_to_obstacle.length_sqr(); | |||
| if len_sqr < dist_sqr { | |||
| dist_sqr = len_sqr; | |||
| ship.obstacle = ShipObstacle::Known(e); | |||
| } | |||
| } | |||
| if let ShipObstacle::Known(e) = ship.obstacle { | |||
| if e == fleet_owned.owner { | |||
| ship.obstacle = ShipObstacle::Done; | |||
| } | |||
| } | |||
| } | |||
| /* check the obstacle */ | |||
| let mut expected_dir = ship_to_target; | |||
| if let ShipObstacle::Known(obstacle) = ship.obstacle { | |||
| let obstacle_pos = self.positions.get(obstacle).unwrap(); | |||
| let obstacle_fleet = self.fleets.get(obstacle).unwrap(); | |||
| let ship_to_obstacle = obstacle_pos.pos - ship_pos; | |||
| let orbit = ship_to_obstacle.length(); | |||
| if orbit < obstacle_fleet.orbit_max { | |||
| let orbit_min = obstacle_fleet.orbit_min; | |||
| let orbit_max = obstacle_fleet.orbit_max; | |||
| let mut tangent = Vector2f::new(-ship_to_obstacle.y, ship_to_obstacle.x); | |||
| let radius = obstacle_pos.shape.radius(); | |||
| let mut adjust_low = linear_step(orbit_min, radius, orbit); | |||
| let adjust_high = 1.0 - linear_step(orbit_max, orbit_min, orbit); | |||
| if ship_to_target * tangent < 0.0 { | |||
| tangent = -tangent; | |||
| } else { | |||
| adjust_low = -adjust_low; | |||
| } | |||
| let a_low = Angle::Deg(45.0); | |||
| let a_high = tangent.angle2(&ship_to_target); | |||
| let mat = Matrix3f::rotate(a_low * adjust_low + a_high * adjust_high); | |||
| expected_dir = mat.transform(tangent); | |||
| } | |||
| } | |||
| /* update ship direction */ | |||
| let angle = ship_to_target.angle2(&velocity.dir); | |||
| let angle = expected_dir.angle2(&velocity.dir); | |||
| if angle.into_inner().abs() > 0.0001 { | |||
| let dir = angle.into_inner() / angle.abs().into_inner(); | |||
| let agility = if ship_in_orbit { | |||
| @@ -190,14 +284,7 @@ impl Processor<'_> { | |||
| }; | |||
| let rot_speed = agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner()); | |||
| let m = Matrix3f::new( | |||
| Vector3f::new(velocity.dir.y, -velocity.dir.x, 0.0), | |||
| Vector3f::new(velocity.dir.x, velocity.dir.y, 0.0), | |||
| Vector3f::new(0.0, 0.0, 1.0), | |||
| ); | |||
| let m = m * Matrix3f::rotate(rot_speed * -dir * self.delta); | |||
| velocity.dir = Vector2f::new(m.axis_y.x, m.axis_y.y); | |||
| velocity.dir = Matrix3f::rotate(rot_speed * -dir * self.delta).transform(velocity.dir); | |||
| } | |||
| } | |||
| } | |||