You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

596 lines
19 KiB

  1. use std::mem::take;
  2. use std::time::Instant;
  3. use glc::{
  4. math::{clamp, linear_step},
  5. matrix::Matrix4f,
  6. misc::BindGuard,
  7. shader::{Program, Type, Uniform},
  8. vector::{Vector2f, Vector4f},
  9. };
  10. use shrev::{EventChannel, ReaderId};
  11. use space_crush_common::{
  12. components::{Fleet, Orbit, OrbitOwned, Player, Position, ShipCount},
  13. constants::VECTOR_2F_POS_X,
  14. continue_if_none,
  15. misc::{LogResult, WorldHelper as _},
  16. resources::Global,
  17. return_if_none,
  18. };
  19. use specs::{prelude::*, ReadExpect, ReadStorage, System, World};
  20. use crate::{
  21. constants::{
  22. FLEET_SELECT_ANIMATION_TIME, FLEET_SELECT_DETAIL_TIMEOUT, FLEET_SELECT_OFFSET,
  23. FLEET_SELECT_TEXT_OFFSET, FLEET_SELECT_TEXT_SIZE, FLEET_SELECT_WIDTH,
  24. UNIFORM_BUFFER_INDEX_CAMERA,
  25. },
  26. misc::{
  27. HorizontalAlign, MouseEvent, Text, TextCache, TextManager, VerticalAlign, WorldHelper as _,
  28. },
  29. resources::{Camera, Config, Geometry, InputState, PlayerState, Selection},
  30. Error,
  31. };
  32. pub struct FleetSelect {
  33. program_simple: Program,
  34. program_detail: Program,
  35. cache: TextCache,
  36. text_total: Text,
  37. text_fighter: Text,
  38. text_bomber: Text,
  39. text_transporter: Text,
  40. mouse_event_id: ReaderId<MouseEvent>,
  41. select_mode: SelectMode,
  42. camera_counter: usize,
  43. need_value_update: bool,
  44. values_changed_once: bool,
  45. is_new_selection: bool,
  46. mouse_pos: Vector2f,
  47. count: ShipCount,
  48. values: Vector4f,
  49. marker: Vector2f,
  50. shape_size: f32,
  51. ring0: f32,
  52. ring1: f32,
  53. zoom: f32,
  54. }
  55. #[derive(Copy, Clone, Debug)]
  56. enum SelectMode {
  57. None,
  58. Init(Instant),
  59. Simple(f32),
  60. Detail(f32),
  61. SimpleClose(f32),
  62. DetailClose(f32),
  63. }
  64. #[derive(SystemData)]
  65. pub struct FleetSelectData<'a> {
  66. player_state: WriteExpect<'a, PlayerState>,
  67. camera: ReadExpect<'a, Camera>,
  68. mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>,
  69. input_state: ReadExpect<'a, InputState>,
  70. geometry: ReadExpect<'a, Geometry>,
  71. global: ReadExpect<'a, Global>,
  72. config: ReadExpect<'a, Config>,
  73. orbit_owned: ReadStorage<'a, OrbitOwned>,
  74. positions: ReadStorage<'a, Position>,
  75. players: ReadStorage<'a, Player>,
  76. orbits: ReadStorage<'a, Orbit>,
  77. fleets: ReadStorage<'a, Fleet>,
  78. }
  79. macro_rules! selection {
  80. (&$data:expr) => {
  81. return_if_none!(&$data.player_state.selection)
  82. };
  83. (&mut $data:expr) => {
  84. return_if_none!(&mut $data.player_state.selection)
  85. };
  86. }
  87. macro_rules! player {
  88. (&$data:expr, $id:expr) => {
  89. return_if_none!($data.players.get($id))
  90. };
  91. }
  92. macro_rules! fleet {
  93. (&$data:expr, $id:expr) => {
  94. return_if_none!($data.fleets.get($id))
  95. };
  96. }
  97. macro_rules! position {
  98. (&$data:expr, $id:expr) => {
  99. return_if_none!($data.positions.get($id))
  100. };
  101. }
  102. macro_rules! orbit_owned {
  103. (&$data:expr, $id:expr) => {
  104. return_if_none!($data.orbit_owned.get($id))
  105. };
  106. }
  107. impl FleetSelect {
  108. pub fn new(world: &World, text_manager: &TextManager) -> Result<Self, Error> {
  109. let program_simple = world.load_program(vec![
  110. (Type::Vertex, "resources/shader/fleet_select/vert.glsl"),
  111. (
  112. Type::Fragment,
  113. "resources/shader/fleet_select/simple_frag.glsl",
  114. ),
  115. ])?;
  116. program_simple.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;
  117. let program_detail = world.load_program(vec![
  118. (Type::Vertex, "resources/shader/fleet_select/vert.glsl"),
  119. (
  120. Type::Fragment,
  121. "resources/shader/fleet_select/detail_frag.glsl",
  122. ),
  123. ])?;
  124. program_detail.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;
  125. let cache = text_manager.create_cache()?;
  126. let text_total = new_text(&cache)?;
  127. let text_fighter = new_text(&cache)?;
  128. let text_bomber = new_text(&cache)?;
  129. let text_transporter = new_text(&cache)?;
  130. let select_mode = SelectMode::None;
  131. let mouse_event_id = world.register_event_reader::<MouseEvent>()?;
  132. Ok(Self {
  133. program_simple,
  134. program_detail,
  135. cache,
  136. text_total,
  137. text_fighter,
  138. text_bomber,
  139. text_transporter,
  140. mouse_event_id,
  141. select_mode,
  142. camera_counter: 0,
  143. need_value_update: true,
  144. values_changed_once: false,
  145. is_new_selection: true,
  146. mouse_pos: Default::default(),
  147. count: Default::default(),
  148. values: Default::default(),
  149. marker: Default::default(),
  150. shape_size: Default::default(),
  151. ring0: Default::default(),
  152. ring1: Default::default(),
  153. zoom: Default::default(),
  154. })
  155. }
  156. fn update_camera(&mut self, d: &FleetSelectData<'_>) {
  157. let camera_counter = d.camera.update_counter();
  158. if self.camera_counter != camera_counter {
  159. self.camera_counter = camera_counter;
  160. self.need_value_update = true;
  161. }
  162. }
  163. fn handle_events(&mut self, d: &mut FleetSelectData<'_>) {
  164. let events = d.mouse_events.read(&mut self.mouse_event_id);
  165. for event in events {
  166. match event {
  167. MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => {
  168. let pos = d.camera.view_to_world(d.input_state.mouse_pos);
  169. let selection = d.player_state.selection.take();
  170. for (position, orbit) in (&d.positions, &d.orbits).join() {
  171. let r = orbit.max * orbit.max;
  172. if (position.pos - pos).length_sqr() <= r {
  173. let player_id = d.player_state.player_id;
  174. let player = player!(&d, player_id);
  175. let player_index = player.index;
  176. let fleet_id = continue_if_none!(orbit.fleets.get(player_index));
  177. let fleet_id = *continue_if_none!(fleet_id);
  178. d.player_state.selection = match selection {
  179. Some(s) if s.fleet == fleet_id => {
  180. self.is_new_selection = false;
  181. Some(s)
  182. }
  183. _ => {
  184. self.is_new_selection = true;
  185. Some(Selection {
  186. fleet: fleet_id,
  187. count: ShipCount::none(),
  188. })
  189. }
  190. };
  191. let selection = selection!(&d);
  192. let fleet = fleet!(&d, selection.fleet);
  193. let timeout = Instant::now() + FLEET_SELECT_DETAIL_TIMEOUT;
  194. self.mouse_pos = d.input_state.mouse_pos;
  195. self.select_mode = SelectMode::Init(timeout);
  196. self.values_changed_once = false;
  197. self.set_count(selection.count, &fleet.count);
  198. break;
  199. }
  200. }
  201. }
  202. MouseEvent::ButtonUp(button) if button == &d.config.input.fleet_select_button => {
  203. self.select_mode = match self.select_mode {
  204. SelectMode::Simple(progress) => {
  205. selection!(&mut d).count = self.count;
  206. self.mouse_pos = d.input_state.mouse_pos;
  207. SelectMode::SimpleClose(progress)
  208. }
  209. SelectMode::Detail(progress) => {
  210. selection!(&mut d).count = self.count;
  211. self.mouse_pos = d.input_state.mouse_pos;
  212. SelectMode::DetailClose(progress)
  213. }
  214. SelectMode::Init(_) if self.is_new_selection => {
  215. selection!(&mut d).count = ShipCount::all();
  216. SelectMode::None
  217. }
  218. _ => SelectMode::None,
  219. }
  220. }
  221. MouseEvent::Move(_, _) if self.select_mode.is_init() => {
  222. self.need_value_update = true;
  223. self.values_changed_once = false;
  224. self.mouse_pos = d.input_state.mouse_pos;
  225. self.select_mode = SelectMode::Simple(0.0);
  226. }
  227. MouseEvent::Move(_, _) if self.select_mode.is_active() => {
  228. self.need_value_update = true;
  229. self.mouse_pos = d.input_state.mouse_pos;
  230. }
  231. _ => (),
  232. }
  233. }
  234. }
  235. fn update(&mut self, d: &FleetSelectData<'_>) {
  236. if !self.select_mode.is_active() {
  237. return;
  238. }
  239. if !take(&mut self.need_value_update) {
  240. return;
  241. }
  242. /* calculate values */
  243. let selection = selection!(&d);
  244. let orbit_owned = orbit_owned!(&d, selection.fleet);
  245. let position = position!(&d, orbit_owned.owner);
  246. let fleet = fleet!(&d, selection.fleet);
  247. self.marker = d.camera.view_to_world(self.mouse_pos) - position.pos;
  248. self.zoom = d.camera.view().axis_x.as_vec3().length();
  249. self.shape_size = position.shape.radius();
  250. self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom;
  251. self.ring1 = self.ring0 + FLEET_SELECT_WIDTH / self.zoom;
  252. let is_simple = self.select_mode.is_simple();
  253. let angle = self
  254. .marker
  255. .angle2(&VECTOR_2F_POS_X)
  256. .normalize()
  257. .into_deg()
  258. .into_inner();
  259. let sector = match angle {
  260. x if (135.0 >= x && x > 45.0) || is_simple => 3,
  261. x if 45.0 >= x && x > -45.0 => 0,
  262. x if -45.0 >= x && x > -135.0 => 1,
  263. _ => 2,
  264. };
  265. /* calulate ship value */
  266. let value = self.marker.length();
  267. if value > self.ring1 {
  268. self.values_changed_once = true;
  269. match sector {
  270. x @ 0..=2 => {
  271. let mut count = selection.count;
  272. count[x] = usize::MAX;
  273. self.set_count(count, &fleet.count);
  274. self.values[x] = 1.0;
  275. self.update_values(&fleet.count);
  276. }
  277. _ => {
  278. self.count = ShipCount::all();
  279. self.values = Vector4f::new(1.0, 1.0, 1.0, 1.0);
  280. }
  281. }
  282. } else if value > self.shape_size {
  283. let value = linear_step(self.ring0, self.ring1, value);
  284. self.values_changed_once = true;
  285. match sector {
  286. x @ 0..=2 => {
  287. let mut count = selection.count;
  288. count[x] = (fleet.count[x] as f32 * value).round() as usize;
  289. self.set_count(count, &fleet.count);
  290. self.values[x] = value;
  291. self.update_values(&fleet.count);
  292. }
  293. _ => {
  294. self.count = fleet.count * value;
  295. self.values = value.into();
  296. }
  297. }
  298. } else if self.values_changed_once {
  299. match sector {
  300. x @ 0..=2 => {
  301. let mut count = selection.count;
  302. count[x] = 0;
  303. self.set_count(count, &fleet.count);
  304. self.values[x] = 0.0;
  305. self.update_values(&fleet.count);
  306. }
  307. _ => {
  308. self.count = Default::default();
  309. self.values = Default::default();
  310. }
  311. }
  312. }
  313. /* update texts */
  314. let c = self.count.merge(&fleet.count);
  315. let _guard = self.cache.begin_update();
  316. self.text_total
  317. .update(0, c.total().to_string())
  318. .error("Unable to update text for ship count (total)");
  319. self.text_fighter
  320. .update(0, c.fighter.to_string())
  321. .error("Unable to update text for ship count (fighter)");
  322. self.text_bomber
  323. .update(0, c.bomber.to_string())
  324. .error("Unable to update text for ship count (bomber)");
  325. self.text_transporter
  326. .update(0, c.transporter.to_string())
  327. .error("Unable to update text for ship count (transporter)");
  328. }
  329. fn progress(&mut self, delta: f32) -> Option<f32> {
  330. match &mut self.select_mode {
  331. SelectMode::Init(detail_timeout) if *detail_timeout < Instant::now() => {
  332. self.need_value_update = true;
  333. self.values_changed_once = false;
  334. self.select_mode = SelectMode::Detail(0.0);
  335. Some(0.0)
  336. }
  337. SelectMode::Simple(progress) => {
  338. *progress += delta / FLEET_SELECT_ANIMATION_TIME;
  339. *progress = clamp(0.0, 1.0, *progress);
  340. Some(*progress)
  341. }
  342. SelectMode::SimpleClose(progress) => {
  343. *progress += delta / FLEET_SELECT_ANIMATION_TIME;
  344. if *progress > 2.0 {
  345. self.select_mode = SelectMode::None;
  346. Some(2.0)
  347. } else {
  348. Some(*progress)
  349. }
  350. }
  351. SelectMode::Detail(progress) => {
  352. *progress += delta / FLEET_SELECT_ANIMATION_TIME;
  353. *progress = clamp(0.0, 1.0, *progress);
  354. Some(*progress)
  355. }
  356. SelectMode::DetailClose(progress) => {
  357. *progress += delta / FLEET_SELECT_ANIMATION_TIME;
  358. if *progress > 2.0 {
  359. self.select_mode = SelectMode::None;
  360. Some(2.0)
  361. } else {
  362. Some(*progress)
  363. }
  364. }
  365. _ => None,
  366. }
  367. }
  368. fn render(&mut self, progress: f32, d: &FleetSelectData<'_>) {
  369. /* select program */
  370. let is_simple = self.select_mode.is_simple();
  371. let program = if is_simple {
  372. &self.program_simple
  373. } else {
  374. &self.program_detail
  375. };
  376. /* extract system data */
  377. let selection = selection!(&d);
  378. let orbit_owned = orbit_owned!(&d, selection.fleet);
  379. let position = position!(&d, orbit_owned.owner);
  380. /* calculate shared values */
  381. let size = self.ring1 + 50.0;
  382. let rings = Vector2f::new(self.ring0 / size, self.ring1 / size);
  383. let px = position.pos.x;
  384. let py = position.pos.y;
  385. let m = Matrix4f::new(
  386. Vector4f::new(size, 0.0, 0.0, 0.0),
  387. Vector4f::new(0.0, size, 0.0, 0.0),
  388. Vector4f::new(0.0, 0.0, size, 0.0),
  389. Vector4f::new(px, py, 0.0, 1.0),
  390. );
  391. /* update uniforms */
  392. let guard = BindGuard::new(&program);
  393. program
  394. .uniform(0, Uniform::Matrix4f(&m))
  395. .error("Error while updating model matrix");
  396. program
  397. .uniform(1, Uniform::Vector2f(&rings))
  398. .error("Error while updating rings");
  399. program
  400. .uniform(2, Uniform::Vector2f(&self.marker))
  401. .error("Error while updating marker");
  402. program
  403. .uniform(3, Uniform::Vector4f(&self.values))
  404. .error("Error while updating value");
  405. program
  406. .uniform(4, Uniform::Float(progress))
  407. .error("Error while updating progress");
  408. program
  409. .uniform(5, Uniform::Float(self.shape_size / size))
  410. .error("Error while updating size");
  411. /* render selection menu */
  412. d.geometry.render_quad();
  413. drop(guard);
  414. /* render text */
  415. let alpha = if progress <= 1.0 {
  416. progress
  417. } else {
  418. 2.0 - progress
  419. };
  420. if is_simple {
  421. self.text_total
  422. .color(Vector4f::new(1.0, 1.0, 1.0, alpha))
  423. .render_offset(&self.text_pos(self.marker, position.pos, d));
  424. } else {
  425. self.text_total
  426. .color(Vector4f::new(1.0, 1.0, 1.0, alpha))
  427. .render_offset(&self.text_pos((0.0, 1.0), position.pos, d));
  428. self.text_fighter
  429. .color(Vector4f::new(1.0, 1.0, 1.0, alpha))
  430. .render_offset(&self.text_pos((1.0, 0.0), position.pos, d));
  431. self.text_bomber
  432. .color(Vector4f::new(1.0, 1.0, 1.0, alpha))
  433. .render_offset(&self.text_pos((0.0, -1.0), position.pos, d));
  434. self.text_transporter
  435. .color(Vector4f::new(1.0, 1.0, 1.0, alpha))
  436. .render_offset(&self.text_pos((-1.0, 0.0), position.pos, d));
  437. }
  438. }
  439. fn set_count(&mut self, count: ShipCount, fleet_count: &ShipCount) {
  440. let c = count.merge(fleet_count);
  441. let f = &fleet_count;
  442. self.count = count;
  443. self.values = Vector4f::new(
  444. clamp(0.0, 1.0, c.fighter as f32 / f.fighter as f32),
  445. clamp(0.0, 1.0, c.bomber as f32 / f.bomber as f32),
  446. clamp(0.0, 1.0, c.transporter as f32 / f.transporter as f32),
  447. clamp(0.0, 1.0, c.total() as f32 / f.total() as f32),
  448. );
  449. }
  450. fn update_values(&mut self, fleet_count: &ShipCount) {
  451. let total_max = fleet_count.total() as f32;
  452. let sum = fleet_count[0] as f32 * self.values[0]
  453. + fleet_count[1] as f32 * self.values[1]
  454. + fleet_count[2] as f32 * self.values[2];
  455. self.values[3] = sum / total_max;
  456. }
  457. fn text_pos<T: Into<Vector2f>>(
  458. &self,
  459. dir: T,
  460. fleet_pos: Vector2f,
  461. d: &FleetSelectData<'_>,
  462. ) -> Vector2f {
  463. let text_offset = self.ring1 + FLEET_SELECT_TEXT_OFFSET / self.zoom.sqrt();
  464. let pos = fleet_pos + T::into(dir).normalize() * text_offset;
  465. d.camera.world_to_window(pos)
  466. }
  467. }
  468. impl<'a> System<'a> for FleetSelect {
  469. type SystemData = FleetSelectData<'a>;
  470. fn run(&mut self, mut data: Self::SystemData) {
  471. self.update_camera(&data);
  472. self.handle_events(&mut data);
  473. self.update(&data);
  474. let progress = self.progress(data.global.delta);
  475. if let Some(progress) = progress {
  476. gl::enable(gl::BLEND);
  477. gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
  478. self.render(progress, &data);
  479. gl::disable(gl::BLEND);
  480. }
  481. }
  482. }
  483. impl SelectMode {
  484. pub fn is_init(&self) -> bool {
  485. matches!(self, Self::Init { .. })
  486. }
  487. pub fn is_simple(&self) -> bool {
  488. match self {
  489. Self::Simple { .. } => true,
  490. Self::SimpleClose { .. } => true,
  491. _ => false,
  492. }
  493. }
  494. pub fn is_active(&self) -> bool {
  495. match self {
  496. Self::Simple { .. } => true,
  497. Self::Detail { .. } => true,
  498. _ => false,
  499. }
  500. }
  501. }
  502. fn new_text(cache: &TextCache) -> Result<Text, Error> {
  503. cache
  504. .new_text()
  505. .scale(FLEET_SELECT_TEXT_SIZE)
  506. .font("resources/fonts/DroidSansMono.ttf")
  507. .color(1.0, 1.0, 1.0, 0.75)
  508. .nowrap()
  509. .vert_align(VerticalAlign::Center)
  510. .horz_align(HorizontalAlign::Center)
  511. .text("0")
  512. .build()
  513. }