main.rs 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. use std::cmp;
  2. use std::collections::HashMap;
  3. use std::f32::consts::FRAC_PI_2;
  4. use ggez::event::MouseButton;
  5. use ggez::graphics::{DrawMode, MeshBuilder, StrokeOptions};
  6. use ggez::input::keyboard::KeyCode;
  7. use ggez::timer::check_update_time;
  8. use ggez::{event, graphics, input, Context, GameResult};
  9. use crate::behavior::animate::{digest_current_behavior, digest_current_order, digest_next_order};
  10. use crate::behavior::order::Order;
  11. use crate::behavior::ItemBehavior;
  12. use crate::config::{
  13. ANIMATE_EACH, DEBUG, DEFAULT_SELECTED_SQUARE_SIDE, DEFAULT_SELECTED_SQUARE_SIDE_HALF,
  14. DISPLAY_OFFSET_BY, DISPLAY_OFFSET_BY_SPEED, MAX_FRAME_I, META_EACH,
  15. MOVE_TO_REACHED_WHEN_DISTANCE_INFERIOR_AT, PHYSICS_EACH, SCENE_ITEMS_CHANGE_ERR_MSG,
  16. SPRITE_EACH, TARGET_FPS,
  17. };
  18. use crate::physics::util::scene_point_from_window_point;
  19. use crate::physics::util::window_point_from_scene_point;
  20. use crate::physics::GridPosition;
  21. use crate::physics::{util, MetaEvent, PhysicEvent};
  22. use crate::scene::item::{
  23. apply_scene_item_modifier, ItemState, SceneItem, SceneItemModifier, SceneItemType,
  24. };
  25. use crate::ui::vertical_menu::{vertical_menu_sprite_info, VerticalMenuSpriteInfo};
  26. use crate::ui::MenuItem;
  27. use crate::ui::{SceneItemPrepareOrder, UiComponent, UserEvent};
  28. use crate::{Offset, ScenePoint, WindowPoint};
  29. pub struct MainState {
  30. // time
  31. frame_i: u32,
  32. // display
  33. display_offset: Offset,
  34. sprite_sheet_batch: graphics::spritebatch::SpriteBatch,
  35. map_batch: graphics::spritebatch::SpriteBatch,
  36. ui_batch: graphics::spritebatch::SpriteBatch,
  37. // scene items
  38. scene_items: Vec<SceneItem>,
  39. scene_items_by_grid_position: HashMap<GridPosition, Vec<usize>>,
  40. // events
  41. physics_events: Vec<PhysicEvent>,
  42. // user interactions
  43. left_click_down: Option<WindowPoint>,
  44. right_click_down: Option<WindowPoint>,
  45. current_cursor_position: WindowPoint,
  46. user_events: Vec<UserEvent>,
  47. selected_scene_items: Vec<usize>, // scene_item usize
  48. scene_item_menu: Option<(usize, ScenePoint)>, // scene_item usize, display_at
  49. scene_item_prepare_order: Option<SceneItemPrepareOrder>,
  50. }
  51. impl MainState {
  52. pub fn new(ctx: &mut Context) -> GameResult<MainState> {
  53. let sprite_sheet = graphics::Image::new(ctx, "/sprite_sheet.png").unwrap();
  54. let sprite_sheet_batch = graphics::spritebatch::SpriteBatch::new(sprite_sheet);
  55. let map = graphics::Image::new(ctx, "/map1bg.png").unwrap();
  56. let map_batch = graphics::spritebatch::SpriteBatch::new(map);
  57. let ui = graphics::Image::new(ctx, "/ui.png").unwrap();
  58. let ui_batch = graphics::spritebatch::SpriteBatch::new(ui);
  59. let mut scene_items = vec![];
  60. for x in 0..1 {
  61. for y in 0..4 {
  62. // let current_behavior = if y % 2 == 0 {
  63. // ItemBehavior::WalkingTo(util::vec_from_angle(90.0))
  64. // } else {
  65. // ItemBehavior::CrawlingTo()
  66. // };
  67. scene_items.push(SceneItem::new(
  68. SceneItemType::Soldier,
  69. ScenePoint::new((x as f32 * 24.0) + 100.0, (y as f32 * 24.0) + 100.0),
  70. ItemState::new(ItemBehavior::Standing),
  71. ));
  72. }
  73. }
  74. let mut main_state = MainState {
  75. frame_i: 0,
  76. display_offset: Offset::new(0.0, 0.0),
  77. sprite_sheet_batch,
  78. map_batch,
  79. ui_batch,
  80. scene_items,
  81. scene_items_by_grid_position: HashMap::new(),
  82. physics_events: vec![],
  83. left_click_down: None,
  84. right_click_down: None,
  85. current_cursor_position: WindowPoint::new(0.0, 0.0),
  86. user_events: vec![],
  87. selected_scene_items: vec![],
  88. scene_item_menu: None,
  89. scene_item_prepare_order: None,
  90. };
  91. for (i, scene_item) in main_state.scene_items.iter().enumerate() {
  92. let grid_position = util::grid_position_from_scene_point(&scene_item.position);
  93. main_state
  94. .scene_items_by_grid_position
  95. .entry(grid_position)
  96. .or_default()
  97. .push(i);
  98. }
  99. Ok(main_state)
  100. }
  101. fn get_scene_item(&self, index: usize) -> &SceneItem {
  102. self.scene_items
  103. .get(index)
  104. .expect(SCENE_ITEMS_CHANGE_ERR_MSG)
  105. }
  106. fn get_scene_item_mut(&mut self, index: usize) -> &mut SceneItem {
  107. self.scene_items
  108. .get_mut(index)
  109. .expect(SCENE_ITEMS_CHANGE_ERR_MSG)
  110. }
  111. fn inputs(&mut self, ctx: &Context) {
  112. let display_offset_by =
  113. if input::keyboard::is_mod_active(ctx, input::keyboard::KeyMods::SHIFT) {
  114. DISPLAY_OFFSET_BY_SPEED
  115. } else {
  116. DISPLAY_OFFSET_BY
  117. };
  118. if input::keyboard::is_key_pressed(ctx, KeyCode::Left) {
  119. self.display_offset.x += display_offset_by;
  120. }
  121. if input::keyboard::is_key_pressed(ctx, KeyCode::Right) {
  122. self.display_offset.x -= display_offset_by;
  123. }
  124. if input::keyboard::is_key_pressed(ctx, KeyCode::Up) {
  125. self.display_offset.y += display_offset_by;
  126. }
  127. if input::keyboard::is_key_pressed(ctx, KeyCode::Down) {
  128. self.display_offset.y -= display_offset_by;
  129. }
  130. while let Some(user_event) = self.user_events.pop() {
  131. match user_event {
  132. UserEvent::Click(window_click_point) => self.digest_click(window_click_point),
  133. UserEvent::AreaSelection(window_from, window_to) => {
  134. self.digest_area_selection(window_from, window_to)
  135. }
  136. UserEvent::RightClick(window_right_click_point) => {
  137. self.digest_right_click(window_right_click_point)
  138. }
  139. }
  140. }
  141. }
  142. fn digest_click(&mut self, window_click_point: WindowPoint) {
  143. let scene_click_point =
  144. scene_point_from_window_point(&window_click_point, &self.display_offset);
  145. self.selected_scene_items.drain(..);
  146. if let Some(scene_item_usize) =
  147. self.get_first_scene_item_for_scene_point(&scene_click_point)
  148. {
  149. self.selected_scene_items.push(scene_item_usize);
  150. }
  151. if let Some(scene_item_prepare_order) = &self.scene_item_prepare_order {
  152. // TODO: Add order to scene_item
  153. match scene_item_prepare_order {
  154. SceneItemPrepareOrder::Move(scene_item_usize) => {
  155. let mut scene_item = self.get_scene_item_mut(*scene_item_usize);
  156. scene_item.next_order = Some(Order::MoveTo(scene_click_point));
  157. }
  158. }
  159. self.scene_item_prepare_order = None;
  160. }
  161. if let Some((scene_item_usize, scene_menu_point)) = self.scene_item_menu {
  162. let menu_sprite_info = vertical_menu_sprite_info(UiComponent::SceneItemMenu);
  163. if let Some(menu_item) =
  164. menu_sprite_info.item_clicked(&scene_menu_point, &scene_click_point)
  165. {
  166. match menu_item {
  167. MenuItem::Move => {
  168. self.scene_item_prepare_order =
  169. Some(SceneItemPrepareOrder::Move(scene_item_usize));
  170. self.scene_item_menu = None;
  171. }
  172. MenuItem::MoveFast => {}
  173. MenuItem::Hide => {}
  174. }
  175. };
  176. self.scene_item_menu = None;
  177. };
  178. }
  179. fn digest_right_click(&mut self, window_right_click_point: WindowPoint) {
  180. let scene_right_click_point =
  181. scene_point_from_window_point(&window_right_click_point, &self.display_offset);
  182. // TODO: aucune selection et right click sur un item: scene_item_menu sur un item
  183. // TODO: selection et right click sur un item de la selection: scene_item_menu sur un TOUS les item de la selection
  184. // TODO: selection et right click sur un item PAS dans la selection: scene_item_menu sur un item
  185. if let Some(scene_item_usize) =
  186. self.get_first_scene_item_for_scene_point(&scene_right_click_point)
  187. {
  188. if self.selected_scene_items.contains(&scene_item_usize) {
  189. let scene_item = self.get_scene_item(scene_item_usize);
  190. self.scene_item_menu = Some((scene_item_usize, scene_item.position))
  191. }
  192. }
  193. }
  194. fn digest_area_selection(&mut self, window_from: WindowPoint, window_to: WindowPoint) {
  195. let scene_from = scene_point_from_window_point(&window_from, &self.display_offset);
  196. let scene_to = scene_point_from_window_point(&window_to, &self.display_offset);
  197. self.selected_scene_items.drain(..);
  198. self.selected_scene_items
  199. .extend(self.get_scene_items_for_scene_area(&scene_from, &scene_to));
  200. }
  201. // TODO: manage errors
  202. fn physics(&mut self) {
  203. // Scene items movements
  204. for scene_item in self.scene_items.iter_mut() {
  205. match scene_item.state.current_behavior {
  206. ItemBehavior::WalkingTo(scene_point) => {
  207. // FIXME BS NOW: velocity
  208. let move_vector = (scene_point - scene_item.position).normalize() * 1.0;
  209. // TODO ici il faut calculer le déplacement réél (en fonction des ticks, etc ...)
  210. scene_item.position.x += move_vector.x;
  211. scene_item.position.y += move_vector.y;
  212. scene_item.grid_position =
  213. util::grid_position_from_scene_point(&scene_item.position);
  214. }
  215. _ => {}
  216. }
  217. }
  218. // (FAKE) Drop a bomb to motivate stop move
  219. if self.frame_i % 600 == 0 && self.frame_i != 0 {
  220. self.physics_events.push(PhysicEvent::Explosion);
  221. }
  222. }
  223. fn metas(&mut self) {
  224. for physic_event in &self.physics_events {
  225. match physic_event {
  226. PhysicEvent::Explosion => {
  227. for scene_item in self.scene_items.iter_mut() {
  228. scene_item.meta_events.push(MetaEvent::FeelExplosion);
  229. }
  230. }
  231. }
  232. }
  233. }
  234. fn animate(&mut self) {
  235. for (i, scene_item) in self.scene_items.iter_mut().enumerate() {
  236. apply_scene_item_modifier(scene_item, digest_next_order(&scene_item));
  237. apply_scene_item_modifier(scene_item, digest_current_order(&scene_item));
  238. apply_scene_item_modifier(scene_item, digest_current_behavior(&scene_item));
  239. }
  240. }
  241. fn tick_sprites(&mut self) {
  242. for scene_item in self.scene_items.iter_mut() {
  243. scene_item.tick_sprite();
  244. }
  245. }
  246. fn get_first_scene_item_for_scene_point(&self, scene_position: &ScenePoint) -> Option<usize> {
  247. // TODO: if found multiple: select nearest
  248. for (i, scene_item) in self.scene_items.iter().enumerate() {
  249. let sprite_info = scene_item.sprite_info();
  250. if scene_item.position.x >= scene_position.x - sprite_info.tile_width
  251. && scene_item.position.x <= scene_position.x + sprite_info.tile_width
  252. && scene_item.position.y >= scene_position.y - sprite_info.tile_height
  253. && scene_item.position.y <= scene_position.y + sprite_info.tile_height
  254. {
  255. return Some(i);
  256. }
  257. }
  258. None
  259. }
  260. fn get_scene_items_for_scene_area(&self, from: &ScenePoint, to: &ScenePoint) -> Vec<usize> {
  261. let mut selection = vec![];
  262. for (i, scene_item) in self.scene_items.iter().enumerate() {
  263. if scene_item.position.x >= from.x
  264. && scene_item.position.x <= to.x
  265. && scene_item.position.y >= from.y
  266. && scene_item.position.y <= to.y
  267. {
  268. selection.push(i);
  269. }
  270. }
  271. selection
  272. }
  273. fn generate_scene_item_sprites(&mut self) -> GameResult {
  274. for scene_item in self.scene_items.iter() {
  275. self.sprite_sheet_batch.add(
  276. scene_item
  277. .as_draw_param(scene_item.current_frame as f32)
  278. .dest(scene_item.position.clone()),
  279. );
  280. }
  281. Ok(())
  282. }
  283. fn generate_scene_item_menu_sprites(&mut self) -> GameResult {
  284. if let Some((_, scene_point)) = self.scene_item_menu {
  285. for draw_param in vertical_menu_sprite_info(UiComponent::SceneItemMenu)
  286. .as_draw_params(&scene_point, &self.current_cursor_position)
  287. {
  288. self.ui_batch.add(draw_param);
  289. }
  290. }
  291. Ok(())
  292. }
  293. fn generate_map_sprites(&mut self) -> GameResult {
  294. self.map_batch.add(
  295. graphics::DrawParam::new()
  296. .src(graphics::Rect::new(0.0, 0.0, 1.0, 1.0))
  297. .dest(ScenePoint::new(0.0, 0.0)),
  298. );
  299. Ok(())
  300. }
  301. fn update_mesh_builder_with_debug(
  302. &self,
  303. mut mesh_builder: MeshBuilder,
  304. ) -> GameResult<MeshBuilder> {
  305. if DEBUG {
  306. // Draw circle on each scene item position
  307. for scene_item in self.scene_items.iter() {
  308. mesh_builder.circle(
  309. DrawMode::fill(),
  310. scene_item.position.clone(),
  311. 2.0,
  312. 2.0,
  313. graphics::WHITE,
  314. )?;
  315. }
  316. // Draw circle where left click down
  317. if let Some(window_left_click_down_point) = self.left_click_down {
  318. let scene_left_click_down_point = scene_point_from_window_point(
  319. &window_left_click_down_point,
  320. &self.display_offset,
  321. );
  322. mesh_builder.circle(
  323. DrawMode::fill(),
  324. scene_left_click_down_point,
  325. 2.0,
  326. 2.0,
  327. graphics::YELLOW,
  328. )?;
  329. }
  330. // Draw circle at cursor position
  331. mesh_builder.circle(
  332. DrawMode::fill(),
  333. scene_point_from_window_point(&self.current_cursor_position, &self.display_offset),
  334. 2.0,
  335. 2.0,
  336. graphics::BLUE,
  337. )?;
  338. }
  339. GameResult::Ok(mesh_builder)
  340. }
  341. fn update_mesh_builder_with_selected_items(
  342. &self,
  343. mut mesh_builder: MeshBuilder,
  344. ) -> GameResult<MeshBuilder> {
  345. for i in &self.selected_scene_items {
  346. let selected_scene_item = self.get_scene_item(*i);
  347. mesh_builder.rectangle(
  348. DrawMode::Stroke(StrokeOptions::default()),
  349. graphics::Rect::new(
  350. selected_scene_item.position.x - DEFAULT_SELECTED_SQUARE_SIDE_HALF,
  351. selected_scene_item.position.y - DEFAULT_SELECTED_SQUARE_SIDE_HALF,
  352. DEFAULT_SELECTED_SQUARE_SIDE,
  353. DEFAULT_SELECTED_SQUARE_SIDE,
  354. ),
  355. graphics::GREEN,
  356. )?;
  357. }
  358. GameResult::Ok(mesh_builder)
  359. }
  360. fn update_mesh_builder_with_selection_area(
  361. &self,
  362. mut mesh_builder: MeshBuilder,
  363. ) -> GameResult<MeshBuilder> {
  364. if let Some(window_left_click_down_point) = self.left_click_down {
  365. let scene_left_click_down_point =
  366. scene_point_from_window_point(&window_left_click_down_point, &self.display_offset);
  367. let scene_current_cursor_position =
  368. scene_point_from_window_point(&self.current_cursor_position, &self.display_offset);
  369. if scene_left_click_down_point != scene_current_cursor_position {
  370. mesh_builder.rectangle(
  371. DrawMode::stroke(1.0),
  372. graphics::Rect::new(
  373. scene_left_click_down_point.x,
  374. scene_left_click_down_point.y,
  375. scene_current_cursor_position.x - scene_left_click_down_point.x,
  376. scene_current_cursor_position.y - scene_left_click_down_point.y,
  377. ),
  378. graphics::GREEN,
  379. )?;
  380. }
  381. }
  382. GameResult::Ok(mesh_builder)
  383. }
  384. fn update_mesh_builder_with_prepare_order(
  385. &self,
  386. mut mesh_builder: MeshBuilder,
  387. ) -> GameResult<MeshBuilder> {
  388. if let Some(scene_item_prepare_order) = &self.scene_item_prepare_order {
  389. match scene_item_prepare_order {
  390. SceneItemPrepareOrder::Move(scene_item_usize) => {
  391. let scene_item = self.get_scene_item(*scene_item_usize);
  392. mesh_builder.line(
  393. &vec![
  394. scene_item.position.clone(),
  395. scene_point_from_window_point(
  396. &self.current_cursor_position,
  397. &self.display_offset,
  398. ),
  399. ],
  400. 2.0,
  401. graphics::WHITE,
  402. )?;
  403. }
  404. }
  405. }
  406. GameResult::Ok(mesh_builder)
  407. }
  408. }
  409. impl event::EventHandler for MainState {
  410. fn update(&mut self, ctx: &mut Context) -> GameResult {
  411. while check_update_time(ctx, TARGET_FPS) {
  412. self.inputs(ctx);
  413. // TODO: meta: calculer par ex qui voit qui (soldat voit un ennemi: ajouter l'event a vu
  414. // ennemi, dans animate il se mettra a tirer)
  415. let tick_sprite = self.frame_i % SPRITE_EACH == 0;
  416. let tick_animate = self.frame_i % ANIMATE_EACH == 0;
  417. let tick_physics = self.frame_i % PHYSICS_EACH == 0;
  418. let tick_meta = self.frame_i % META_EACH == 0;
  419. // Apply moves, explosions, etc
  420. if tick_physics {
  421. self.physics();
  422. }
  423. // Generate meta events according to physics events and current physic state
  424. if tick_meta {
  425. self.metas();
  426. }
  427. // Animate scene items according to meta events
  428. if tick_animate {
  429. self.animate();
  430. };
  431. // Change scene items tiles
  432. if tick_sprite {
  433. self.tick_sprites();
  434. }
  435. // Increment frame counter
  436. self.frame_i += 1;
  437. if self.frame_i >= MAX_FRAME_I {
  438. self.frame_i = 0;
  439. }
  440. // Empty physics event
  441. self.physics_events.drain(..);
  442. }
  443. Ok(())
  444. }
  445. fn draw(&mut self, ctx: &mut Context) -> GameResult {
  446. graphics::clear(ctx, graphics::BLACK);
  447. let mut scene_mesh_builder = MeshBuilder::new();
  448. self.generate_scene_item_sprites()?;
  449. self.generate_scene_item_menu_sprites()?;
  450. self.generate_map_sprites()?;
  451. scene_mesh_builder = self.update_mesh_builder_with_debug(scene_mesh_builder)?;
  452. scene_mesh_builder = self.update_mesh_builder_with_selected_items(scene_mesh_builder)?;
  453. scene_mesh_builder = self.update_mesh_builder_with_selection_area(scene_mesh_builder)?;
  454. scene_mesh_builder = self.update_mesh_builder_with_prepare_order(scene_mesh_builder)?;
  455. let scene_mesh = scene_mesh_builder.build(ctx)?;
  456. let window_draw_param = graphics::DrawParam::new().dest(window_point_from_scene_point(
  457. &ScenePoint::new(0.0, 0.0),
  458. &self.display_offset,
  459. ));
  460. graphics::draw(ctx, &self.map_batch, window_draw_param)?;
  461. graphics::draw(ctx, &self.sprite_sheet_batch, window_draw_param)?;
  462. graphics::draw(ctx, &scene_mesh, window_draw_param)?;
  463. graphics::draw(ctx, &self.ui_batch, window_draw_param)?;
  464. self.sprite_sheet_batch.clear();
  465. self.map_batch.clear();
  466. self.ui_batch.clear();
  467. graphics::present(ctx)?;
  468. // println!("FPS: {}", ggez::timer::fps(ctx));
  469. Ok(())
  470. }
  471. fn mouse_button_down_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
  472. match button {
  473. MouseButton::Left => {
  474. self.left_click_down = Some(WindowPoint::new(x, y));
  475. }
  476. MouseButton::Right => {
  477. self.right_click_down = Some(WindowPoint::new(x, y));
  478. }
  479. MouseButton::Middle => {}
  480. MouseButton::Other(_) => {}
  481. }
  482. }
  483. fn mouse_button_up_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
  484. match button {
  485. MouseButton::Left => {
  486. if let Some(left_click_down) = self.left_click_down {
  487. if left_click_down == WindowPoint::new(x, y) {
  488. self.user_events.push(UserEvent::Click(left_click_down));
  489. } else {
  490. let from = WindowPoint::new(
  491. cmp::min(left_click_down.x as i32, x as i32) as f32,
  492. cmp::min(left_click_down.y as i32, y as i32) as f32,
  493. );
  494. let to = WindowPoint::new(
  495. cmp::max(left_click_down.x as i32, x as i32) as f32,
  496. cmp::max(left_click_down.y as i32, y as i32) as f32,
  497. );
  498. self.user_events.push(UserEvent::AreaSelection(from, to));
  499. }
  500. }
  501. self.left_click_down = None;
  502. }
  503. MouseButton::Right => {
  504. if let Some(right_click_down) = self.right_click_down {
  505. self.user_events
  506. .push(UserEvent::RightClick(right_click_down));
  507. }
  508. }
  509. MouseButton::Middle => {}
  510. MouseButton::Other(_) => {}
  511. }
  512. }
  513. fn mouse_motion_event(&mut self, _ctx: &mut Context, x: f32, y: f32, _dx: f32, _dy: f32) {
  514. self.current_cursor_position = WindowPoint::new(x, y);
  515. }
  516. }