main.rs 22KB

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