Browse Source

reorganize in modules

Bastien Sevajol 3 years ago
parent
commit
42f1fb8d14
10 changed files with 778 additions and 725 deletions
  1. 7 0
      src/behavior/mod.rs
  2. 22 725
      src/main.rs
  3. 12 0
      src/physics/mod.rs
  4. 11 0
      src/physics/position.rs
  5. 15 0
      src/physics/util.rs
  6. 111 0
      src/scene/item.rs
  7. 525 0
      src/scene/main.rs
  8. 8 0
      src/scene/mod.rs
  9. 64 0
      src/ui/mod.rs
  10. 3 0
      src/ui/scene_item_menu.rs

+ 7 - 0
src/behavior/mod.rs View File

@@ -0,0 +1,7 @@
1
+use crate::Vector2;
2
+
3
+pub enum ItemBehavior {
4
+    Standing(u32), // since
5
+    Crawling,
6
+    Walking(Vector2),
7
+}

+ 22 - 725
src/main.rs View File

@@ -1,15 +1,31 @@
1
+use std::cmp;
2
+use std::collections::HashMap;
3
+use std::env;
4
+use std::path;
5
+
1 6
 use ggez;
7
+use ggez::{event, input};
8
+use ggez::{Context, GameResult};
2 9
 use ggez::event::{KeyCode, MouseButton};
3 10
 use ggez::graphics;
4 11
 use ggez::graphics::{Color, DrawMode, FillOptions, MeshBuilder, StrokeOptions};
5 12
 use ggez::timer::check_update_time;
6
-use ggez::{event, input};
7
-use ggez::{Context, GameResult};
8 13
 use glam::Vec2;
9
-use std::cmp;
10
-use std::collections::HashMap;
11
-use std::env;
12
-use std::path;
14
+
15
+use behavior::ItemBehavior;
16
+use physics::{MetaEvent, PhysicEvent, util};
17
+use scene::item::{ItemState, SceneItem, SceneItemSpriteInfo, SceneItemType};
18
+use scene::main::MainState;
19
+use scene::SpriteType;
20
+use ui::{SceneItemPrepareOrder, UiItem, UiSpriteInfo, UserEvent};
21
+use ui::scene_item_menu::SceneItemMenuItem;
22
+
23
+use crate::physics::position::GridPosition;
24
+
25
+mod physics;
26
+mod ui;
27
+mod scene;
28
+mod behavior;
13 29
 
14 30
 // TODO: create a ScenePosition and a WindowPosition to be more explicit
15 31
 type Point2 = Vec2;
@@ -33,725 +49,6 @@ const DEFAULT_SELECTED_SQUARE_SIDE: f32 = 14.0;
33 49
 const DEFAULT_SELECTED_SQUARE_SIDE_HALF: f32 = DEFAULT_SELECTED_SQUARE_SIDE / 2.0;
34 50
 const SCENE_ITEMS_CHANGE_ERR_MSG: &str = "scene_items content change !";
35 51
 
36
-#[derive(Eq, PartialEq, Hash)]
37
-pub struct GridPosition {
38
-    x: i32,
39
-    y: i32,
40
-}
41
-
42
-impl GridPosition {
43
-    pub fn new(x: i32, y: i32) -> Self {
44
-        Self { x, y }
45
-    }
46
-}
47
-
48
-fn vec_from_angle(angle: f32) -> Vector2 {
49
-    let vx = angle.sin();
50
-    let vy = angle.cos();
51
-    Vector2::new(vx, vy)
52
-}
53
-
54
-fn grid_position_from_position(position: &Point2) -> GridPosition {
55
-    GridPosition::new(
56
-        (position.x / GRID_TILE_WIDTH) as i32,
57
-        (position.y / GRID_TILE_HEIGHT) as i32,
58
-    )
59
-}
60
-
61
-enum UiItem {
62
-    SceneItemMenu,
63
-}
64
-
65
-enum SceneItemMenuItem {
66
-    Move,
67
-}
68
-
69
-struct UiSpriteInfo {
70
-    relative_start_x: f32,
71
-    relative_start_y: f32,
72
-    relative_width: f32,
73
-    relative_height: f32,
74
-    width: f32,
75
-    height: f32,
76
-}
77
-
78
-impl UiSpriteInfo {
79
-    pub fn from_type(type_: UiItem) -> Self {
80
-        match type_ {
81
-            UiItem::SceneItemMenu => Self {
82
-                relative_start_x: 0.0,
83
-                relative_start_y: 0.0,
84
-                relative_width: 71.0 / UI_SPRITE_SHEET_WIDTH,
85
-                relative_height: 68.0 / UI_SPRITE_SHEET_HEIGHT,
86
-                width: 71.0,
87
-                height: 68.0,
88
-            },
89
-        }
90
-    }
91
-
92
-    pub fn as_draw_param(&self) -> graphics::DrawParam {
93
-        graphics::DrawParam::new().src(graphics::Rect::new(
94
-            self.relative_start_x,
95
-            self.relative_start_y,
96
-            self.relative_width,
97
-            self.relative_height,
98
-        ))
99
-    }
100
-
101
-    pub fn which_item_clicked(
102
-        &self,
103
-        menu_position: Point2,
104
-        click_position: Point2,
105
-        scene_item: &SceneItem,
106
-    ) -> Option<SceneItemMenuItem> {
107
-        Some(SceneItemMenuItem::Move)
108
-    }
109
-}
110
-
111
-struct SpriteInfo {
112
-    relative_start_y: f32,
113
-    relative_tile_width: f32,
114
-    relative_tile_height: f32,
115
-    tile_count: u16,
116
-    tile_width: f32,
117
-    tile_height: f32,
118
-    _half_tile_width: f32,
119
-    _half_tile_height: f32,
120
-}
121
-
122
-impl SpriteInfo {
123
-    // TODO: ask on rust community if this is performant, or how to make it static
124
-    pub fn from_type(type_: &SpriteType) -> Self {
125
-        let (start_y, tile_width, tile_height, tile_count) = match type_ {
126
-            SpriteType::WalkingSoldier => (12.0, 12.0, 12.0, 8),
127
-            SpriteType::CrawlingSoldier => (26.0, 26.0, 26.0, 8),
128
-            SpriteType::StandingSoldier => (0.0, 12.0, 12.0, 1),
129
-        };
130
-
131
-        Self {
132
-            relative_start_y: start_y / SCENE_ITEMS_SPRITE_SHEET_HEIGHT,
133
-            relative_tile_width: tile_width / SCENE_ITEMS_SPRITE_SHEET_WIDTH,
134
-            relative_tile_height: tile_height / SCENE_ITEMS_SPRITE_SHEET_HEIGHT,
135
-            tile_count,
136
-            tile_width,
137
-            tile_height,
138
-            _half_tile_width: tile_width / 2.0,
139
-            _half_tile_height: tile_height / 2.0,
140
-        }
141
-    }
142
-}
143
-
144
-enum SpriteType {
145
-    WalkingSoldier,
146
-    CrawlingSoldier,
147
-    StandingSoldier,
148
-}
149
-
150
-enum ItemBehavior {
151
-    Standing(u32), // since
152
-    Crawling,
153
-    Walking(Vector2),
154
-}
155
-
156
-struct ItemState {
157
-    current_behavior: ItemBehavior,
158
-}
159
-
160
-enum SceneItemType {
161
-    Soldier,
162
-}
163
-
164
-impl ItemState {
165
-    pub fn new(current_behavior: ItemBehavior) -> Self {
166
-        Self { current_behavior }
167
-    }
168
-}
169
-
170
-struct SceneItem {
171
-    type_: SceneItemType,
172
-    position: Point2,
173
-    grid_position: GridPosition,
174
-    state: ItemState,
175
-    meta_events: Vec<MetaEvent>,
176
-    current_frame: u16,
177
-}
178
-
179
-impl SceneItem {
180
-    pub fn new(type_: SceneItemType, position: Point2, state: ItemState) -> Self {
181
-        Self {
182
-            type_,
183
-            position: position.clone(),
184
-            grid_position: grid_position_from_position(&position.clone()),
185
-            state,
186
-            meta_events: vec![],
187
-            current_frame: 0,
188
-        }
189
-    }
190
-
191
-    pub fn sprite_info(&self) -> SpriteInfo {
192
-        SpriteInfo::from_type(&self.sprite_type())
193
-    }
194
-
195
-    pub fn tick_sprite(&mut self) {
196
-        self.current_frame += 1;
197
-        // TODO: good way to have sprite info ? performant ?
198
-        if self.current_frame >= self.sprite_info().tile_count {
199
-            self.current_frame = 0;
200
-        }
201
-    }
202
-
203
-    pub fn as_draw_param(&self, current_frame: f32) -> graphics::DrawParam {
204
-        let sprite_info = self.sprite_info();
205
-        graphics::DrawParam::new()
206
-            .src(graphics::Rect::new(
207
-                current_frame as f32 * sprite_info.relative_tile_width,
208
-                sprite_info.relative_start_y,
209
-                sprite_info.relative_tile_width,
210
-                sprite_info.relative_tile_height,
211
-            ))
212
-            .rotation(90.0f32.to_radians())
213
-            .offset(Point2::new(0.5, 0.5))
214
-    }
215
-
216
-    pub fn sprite_type(&self) -> SpriteType {
217
-        // Here some logical about state, nature (soldier, tank, ...) and current behavior to
218
-        // determine sprite type
219
-        match self.state.current_behavior {
220
-            ItemBehavior::Crawling => SpriteType::CrawlingSoldier,
221
-            ItemBehavior::Walking(_) => SpriteType::WalkingSoldier,
222
-            ItemBehavior::Standing(_) => SpriteType::StandingSoldier,
223
-        }
224
-    }
225
-}
226
-
227
-#[derive(Debug)]
228
-enum PhysicEvent {
229
-    Explosion,
230
-}
231
-
232
-#[derive(Debug)]
233
-enum MetaEvent {
234
-    FearAboutExplosion,
235
-}
236
-
237
-#[derive(Debug)]
238
-enum UserEvent {
239
-    Click(Point2),                 // Window coordinates
240
-    RightClick(Point2),            // Window coordinates
241
-    AreaSelection(Point2, Point2), // Window coordinates
242
-}
243
-
244
-enum SceneItemPrepareOrder {
245
-    Move(usize), // scene_item usize
246
-}
247
-
248
-struct MainState {
249
-    // time
250
-    frame_i: u32,
251
-
252
-    // display
253
-    display_offset: Point2,
254
-    sprite_sheet_batch: graphics::spritebatch::SpriteBatch,
255
-    map_batch: graphics::spritebatch::SpriteBatch,
256
-    ui_batch: graphics::spritebatch::SpriteBatch,
257
-
258
-    // scene items
259
-    scene_items: Vec<SceneItem>,
260
-    scene_items_by_grid_position: HashMap<GridPosition, Vec<usize>>,
261
-
262
-    // events
263
-    physics_events: Vec<PhysicEvent>,
264
-
265
-    // user interactions
266
-    left_click_down: Option<Point2>,
267
-    right_click_down: Option<Point2>,
268
-    current_cursor_position: Point2,
269
-    user_events: Vec<UserEvent>,
270
-    selected_scene_items: Vec<usize>,         // scene_item usize
271
-    scene_item_menu: Option<(usize, Point2)>, // scene_item usize, display_at
272
-    scene_item_prepare_order: Option<SceneItemPrepareOrder>,
273
-}
274
-
275
-impl MainState {
276
-    fn new(ctx: &mut Context) -> GameResult<MainState> {
277
-        let sprite_sheet = graphics::Image::new(ctx, "/sprite_sheet.png").unwrap();
278
-        let sprite_sheet_batch = graphics::spritebatch::SpriteBatch::new(sprite_sheet);
279
-        let map = graphics::Image::new(ctx, "/map1bg.png").unwrap();
280
-        let map_batch = graphics::spritebatch::SpriteBatch::new(map);
281
-        let ui = graphics::Image::new(ctx, "/ui.png").unwrap();
282
-        let ui_batch = graphics::spritebatch::SpriteBatch::new(ui);
283
-
284
-        let mut scene_items = vec![];
285
-        for x in 0..1 {
286
-            for y in 0..4 {
287
-                let current_behavior = if y % 2 == 0 {
288
-                    ItemBehavior::Walking(vec_from_angle(90.0))
289
-                } else {
290
-                    ItemBehavior::Crawling
291
-                };
292
-
293
-                scene_items.push(SceneItem::new(
294
-                    SceneItemType::Soldier,
295
-                    Point2::new((x as f32 * 24.0) + 100.0, (y as f32 * 24.0) + 100.0),
296
-                    ItemState::new(current_behavior),
297
-                ));
298
-            }
299
-        }
300
-
301
-        let mut main_state = MainState {
302
-            frame_i: 0,
303
-            display_offset: Point2::new(0.0, 0.0),
304
-            sprite_sheet_batch,
305
-            map_batch,
306
-            ui_batch,
307
-            scene_items,
308
-            scene_items_by_grid_position: HashMap::new(),
309
-            physics_events: vec![],
310
-            left_click_down: None,
311
-            right_click_down: None,
312
-            current_cursor_position: Point2::new(0.0, 0.0),
313
-            user_events: vec![],
314
-            selected_scene_items: vec![],
315
-            scene_item_menu: None,
316
-            scene_item_prepare_order: None,
317
-        };
318
-
319
-        for (i, scene_item) in main_state.scene_items.iter().enumerate() {
320
-            let grid_position = grid_position_from_position(&scene_item.position);
321
-            main_state
322
-                .scene_items_by_grid_position
323
-                .entry(grid_position)
324
-                .or_default()
325
-                .push(i);
326
-        }
327
-
328
-        Ok(main_state)
329
-    }
330
-
331
-    fn inputs(&mut self, ctx: &Context) {
332
-        let display_offset_by =
333
-            if input::keyboard::is_mod_active(ctx, input::keyboard::KeyMods::SHIFT) {
334
-                DISPLAY_OFFSET_BY_SPEED
335
-            } else {
336
-                DISPLAY_OFFSET_BY
337
-            };
338
-
339
-        if input::keyboard::is_key_pressed(ctx, KeyCode::Left) {
340
-            self.display_offset.x += display_offset_by;
341
-        }
342
-        if input::keyboard::is_key_pressed(ctx, KeyCode::Right) {
343
-            self.display_offset.x -= display_offset_by;
344
-        }
345
-        if input::keyboard::is_key_pressed(ctx, KeyCode::Up) {
346
-            self.display_offset.y += display_offset_by;
347
-        }
348
-        if input::keyboard::is_key_pressed(ctx, KeyCode::Down) {
349
-            self.display_offset.y -= display_offset_by;
350
-        }
351
-
352
-        while let Some(user_event) = self.user_events.pop() {
353
-            match user_event {
354
-                UserEvent::Click(click_position) => {
355
-                    let scene_position = Point2::new(
356
-                        click_position.x - self.display_offset.x,
357
-                        click_position.y - self.display_offset.y,
358
-                    );
359
-                    self.selected_scene_items.drain(..);
360
-                    if let Some(scene_item_usize) =
361
-                        self.get_first_scene_item_for_position(&scene_position)
362
-                    {
363
-                        self.selected_scene_items.push(scene_item_usize);
364
-                    }
365
-
366
-                    if let Some(scene_item_prepare_order) = &self.scene_item_prepare_order {
367
-                        // TODO: Add order to scene_item
368
-                        self.scene_item_prepare_order = None;
369
-                    }
370
-
371
-                    // FIXME BS NOW: interpreter sur quel element du menu on a click ...
372
-                    if let Some((scene_item_usize, menu_position)) = self.scene_item_menu {
373
-                        let menu_sprite_info = UiSpriteInfo::from_type(UiItem::SceneItemMenu);
374
-                        let scene_item = self
375
-                            .scene_items
376
-                            .get(scene_item_usize)
377
-                            .expect(SCENE_ITEMS_CHANGE_ERR_MSG);
378
-                        if click_position.x >= menu_position.x
379
-                            && click_position.x <= menu_position.x + menu_sprite_info.width
380
-                            && click_position.y >= menu_position.y
381
-                            && click_position.y <= menu_position.y + menu_sprite_info.height
382
-                        {
383
-                            if let Some(menu_item) = menu_sprite_info.which_item_clicked(
384
-                                menu_position,
385
-                                click_position,
386
-                                scene_item,
387
-                            ) {
388
-                                match menu_item {
389
-                                    SceneItemMenuItem::Move => {
390
-                                        self.scene_item_prepare_order =
391
-                                            Some(SceneItemPrepareOrder::Move(scene_item_usize));
392
-                                        self.scene_item_menu = None;
393
-                                    }
394
-                                }
395
-                            }
396
-                        } else {
397
-                            self.scene_item_menu = None;
398
-                        }
399
-                    };
400
-                }
401
-                UserEvent::AreaSelection(from, to) => {
402
-                    let scene_from = Point2::new(
403
-                        from.x - self.display_offset.x,
404
-                        from.y - self.display_offset.y,
405
-                    );
406
-                    let scene_to =
407
-                        Point2::new(to.x - self.display_offset.x, to.y - self.display_offset.y);
408
-                    self.selected_scene_items.drain(..);
409
-                    self.selected_scene_items
410
-                        .extend(self.get_scene_items_for_area(&scene_from, &scene_to));
411
-                }
412
-                UserEvent::RightClick(position) => {
413
-                    if let Some(scene_item_usize) =
414
-                        self.get_first_scene_item_for_position(&position)
415
-                    {
416
-                        if self.selected_scene_items.contains(&scene_item_usize) {
417
-                            let scene_item = self
418
-                                .scene_items
419
-                                .get(scene_item_usize)
420
-                                .expect(SCENE_ITEMS_CHANGE_ERR_MSG);
421
-                            self.scene_item_menu =
422
-                                Some((scene_item_usize, scene_item.position.clone()))
423
-                        }
424
-                    }
425
-                }
426
-            }
427
-        }
428
-    }
429
-
430
-    // TODO: manage errors
431
-    fn physics(&mut self) {
432
-        // Scene items movements
433
-        for scene_item in self.scene_items.iter_mut() {
434
-            match scene_item.state.current_behavior {
435
-                ItemBehavior::Walking(vector) => {
436
-                    // TODO ici il faut calculer le déplacement réél (en fonction des ticks, etc ...)
437
-                    scene_item.position.x += 1.0;
438
-                    scene_item.grid_position = grid_position_from_position(&scene_item.position);
439
-                }
440
-                _ => {}
441
-            }
442
-        }
443
-
444
-        // (FAKE) Drop a bomb to motivate stop move
445
-        if self.frame_i % 600 == 0 && self.frame_i != 0 {
446
-            self.physics_events.push(PhysicEvent::Explosion);
447
-        }
448
-    }
449
-
450
-    fn metas(&mut self) {
451
-        for physic_event in &self.physics_events {
452
-            match physic_event {
453
-                PhysicEvent::Explosion => {
454
-                    for scene_item in self.scene_items.iter_mut() {
455
-                        scene_item.meta_events.push(MetaEvent::FearAboutExplosion);
456
-                    }
457
-                }
458
-            }
459
-        }
460
-    }
461
-
462
-    fn animate(&mut self) {
463
-        // TODO: ici il faut reflechir a comment organiser les comportements
464
-
465
-        for scene_item in self.scene_items.iter_mut() {
466
-            for meta_event in &scene_item.meta_events {
467
-                match meta_event {
468
-                    MetaEvent::FearAboutExplosion => {
469
-                        scene_item.state = ItemState::new(ItemBehavior::Standing(self.frame_i));
470
-                    }
471
-                }
472
-            }
473
-
474
-            match scene_item.state.current_behavior {
475
-                ItemBehavior::Crawling => {
476
-                    scene_item.state = ItemState::new(ItemBehavior::Walking(vec_from_angle(90.0)));
477
-                }
478
-                ItemBehavior::Walking(_) => {
479
-                    scene_item.state = ItemState::new(ItemBehavior::Crawling);
480
-                }
481
-                ItemBehavior::Standing(since) => {
482
-                    if self.frame_i - since >= 120 {
483
-                        scene_item.state =
484
-                            ItemState::new(ItemBehavior::Walking(vec_from_angle(90.0)));
485
-                    }
486
-                }
487
-            }
488
-
489
-            scene_item.meta_events.drain(..);
490
-        }
491
-    }
492
-
493
-    fn tick_sprites(&mut self) {
494
-        for scene_item in self.scene_items.iter_mut() {
495
-            scene_item.tick_sprite();
496
-        }
497
-    }
498
-
499
-    fn position_with_display_offset(&self, position: &Point2) -> Point2 {
500
-        Point2::new(
501
-            position.x + self.display_offset.x,
502
-            position.y + self.display_offset.y,
503
-        )
504
-    }
505
-
506
-    fn get_first_scene_item_for_position(&self, position: &Point2) -> Option<usize> {
507
-        // TODO: if found multiple: select nearest
508
-        for (i, scene_item) in self.scene_items.iter().enumerate() {
509
-            let sprite_info = scene_item.sprite_info();
510
-            if scene_item.position.x >= position.x - sprite_info.tile_width
511
-                && scene_item.position.x <= position.x + sprite_info.tile_width
512
-                && scene_item.position.y >= position.y - sprite_info.tile_height
513
-                && scene_item.position.y <= position.y + sprite_info.tile_height
514
-            {
515
-                return Some(i);
516
-            }
517
-        }
518
-
519
-        None
520
-    }
521
-
522
-    fn get_scene_items_for_area(&self, from: &Point2, to: &Point2) -> Vec<usize> {
523
-        let mut selection = vec![];
524
-
525
-        for (i, scene_item) in self.scene_items.iter().enumerate() {
526
-            if scene_item.position.x >= from.x
527
-                && scene_item.position.x <= to.x
528
-                && scene_item.position.y >= from.y
529
-                && scene_item.position.y <= to.y
530
-            {
531
-                selection.push(i);
532
-            }
533
-        }
534
-
535
-        selection
536
-    }
537
-}
538
-
539
-impl event::EventHandler for MainState {
540
-    fn update(&mut self, ctx: &mut Context) -> GameResult {
541
-        while check_update_time(ctx, TARGET_FPS) {
542
-            self.inputs(ctx);
543
-
544
-            // TODO: meta: calculer par ex qui voit qui (soldat voit un ennemi: ajouter l'event a vu
545
-            // ennemi, dans animate il se mettra a tirer)
546
-            let tick_sprite = self.frame_i % SPRITE_EACH == 0;
547
-            let tick_animate = self.frame_i % ANIMATE_EACH == 0;
548
-            let tick_physics = self.frame_i % PHYSICS_EACH == 0;
549
-            let tick_meta = self.frame_i % META_EACH == 0;
550
-
551
-            // Apply moves, explosions, etc
552
-            if tick_physics {
553
-                self.physics();
554
-            }
555
-
556
-            // Generate meta events according to physics events and current physic state
557
-            if tick_meta {
558
-                self.metas();
559
-            }
560
-
561
-            // Animate scene items according to meta events
562
-            if tick_animate {
563
-                self.animate();
564
-            };
565
-
566
-            // Change scene items tiles
567
-            if tick_sprite {
568
-                self.tick_sprites();
569
-            }
570
-
571
-            // Increment frame counter
572
-            self.frame_i += 1;
573
-            if self.frame_i >= MAX_FRAME_I {
574
-                self.frame_i = 0;
575
-            }
576
-
577
-            // Empty physics event
578
-            self.physics_events.drain(..);
579
-        }
580
-
581
-        Ok(())
582
-    }
583
-
584
-    fn draw(&mut self, ctx: &mut Context) -> GameResult {
585
-        graphics::clear(ctx, graphics::BLACK);
586
-
587
-        let mut scene_mesh_builder = MeshBuilder::new();
588
-
589
-        for scene_item in self.scene_items.iter() {
590
-            self.sprite_sheet_batch.add(
591
-                scene_item
592
-                    .as_draw_param(scene_item.current_frame as f32)
593
-                    .dest(scene_item.position.clone()),
594
-            );
595
-            scene_mesh_builder.circle(
596
-                DrawMode::fill(),
597
-                scene_item.position.clone(),
598
-                2.0,
599
-                2.0,
600
-                graphics::WHITE,
601
-            )?;
602
-        }
603
-
604
-        for i in &self.selected_scene_items {
605
-            let selected_scene_item = self.scene_items.get(*i).expect(SCENE_ITEMS_CHANGE_ERR_MSG);
606
-            scene_mesh_builder.rectangle(
607
-                DrawMode::Stroke(StrokeOptions::default()),
608
-                graphics::Rect::new(
609
-                    selected_scene_item.position.x - DEFAULT_SELECTED_SQUARE_SIDE_HALF,
610
-                    selected_scene_item.position.y - DEFAULT_SELECTED_SQUARE_SIDE_HALF,
611
-                    DEFAULT_SELECTED_SQUARE_SIDE,
612
-                    DEFAULT_SELECTED_SQUARE_SIDE,
613
-                ),
614
-                graphics::GREEN,
615
-            )?;
616
-        }
617
-
618
-        if let Some(left_click_down) = self.left_click_down {
619
-            if left_click_down != self.current_cursor_position {
620
-                scene_mesh_builder.rectangle(
621
-                    DrawMode::fill(),
622
-                    graphics::Rect::new(
623
-                        left_click_down.x - self.display_offset.x,
624
-                        left_click_down.y - self.display_offset.y,
625
-                        self.current_cursor_position.x - left_click_down.x,
626
-                        self.current_cursor_position.y - left_click_down.y,
627
-                    ),
628
-                    graphics::GREEN,
629
-                )?;
630
-            }
631
-
632
-            scene_mesh_builder.circle(
633
-                DrawMode::fill(),
634
-                left_click_down,
635
-                2.0,
636
-                2.0,
637
-                graphics::YELLOW,
638
-            )?;
639
-        }
640
-
641
-        if let Some((_, position)) = self.scene_item_menu {
642
-            self.ui_batch.add(
643
-                UiSpriteInfo::from_type(UiItem::SceneItemMenu)
644
-                    .as_draw_param()
645
-                    .dest(position),
646
-            );
647
-        }
648
-
649
-        if let Some(scene_item_prepare_order) = &self.scene_item_prepare_order {
650
-            match scene_item_prepare_order {
651
-                SceneItemPrepareOrder::Move(scene_item_usize) => {
652
-                    let scene_item = self
653
-                        .scene_items
654
-                        .get(*scene_item_usize)
655
-                        .expect(SCENE_ITEMS_CHANGE_ERR_MSG);
656
-                    scene_mesh_builder.line(
657
-                        &vec![scene_item.position.clone(), self.current_cursor_position],
658
-                        2.0,
659
-                        graphics::WHITE,
660
-                    )?;
661
-                }
662
-            }
663
-        }
664
-
665
-        self.map_batch.add(
666
-            graphics::DrawParam::new()
667
-                .src(graphics::Rect::new(0.0, 0.0, 1.0, 1.0))
668
-                .dest(Point2::new(0.0, 0.0)),
669
-        );
670
-
671
-        let scene_mesh = scene_mesh_builder.build(ctx)?;
672
-        graphics::draw(
673
-            ctx,
674
-            &self.map_batch,
675
-            graphics::DrawParam::new()
676
-                .dest(self.position_with_display_offset(&Point2::new(0.0, 0.0))),
677
-        )?;
678
-        graphics::draw(
679
-            ctx,
680
-            &self.sprite_sheet_batch,
681
-            graphics::DrawParam::new()
682
-                .dest(self.position_with_display_offset(&Point2::new(0.0, 0.0))),
683
-        )?;
684
-        graphics::draw(
685
-            ctx,
686
-            &scene_mesh,
687
-            graphics::DrawParam::new()
688
-                .dest(self.position_with_display_offset(&Point2::new(0.0, 0.0))),
689
-        )?;
690
-        graphics::draw(
691
-            ctx,
692
-            &self.ui_batch,
693
-            graphics::DrawParam::new()
694
-                .dest(self.position_with_display_offset(&Point2::new(0.0, 0.0))),
695
-        )?;
696
-
697
-        self.sprite_sheet_batch.clear();
698
-        self.map_batch.clear();
699
-        self.ui_batch.clear();
700
-        graphics::present(ctx)?;
701
-
702
-        println!("FPS: {}", ggez::timer::fps(ctx));
703
-        Ok(())
704
-    }
705
-
706
-    fn mouse_button_down_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
707
-        match button {
708
-            MouseButton::Left => {
709
-                self.left_click_down = Some(Point2::new(x, y));
710
-            }
711
-            MouseButton::Right => {
712
-                self.right_click_down = Some(Point2::new(x, y));
713
-            }
714
-            MouseButton::Middle => {}
715
-            MouseButton::Other(_) => {}
716
-        }
717
-    }
718
-
719
-    fn mouse_button_up_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
720
-        match button {
721
-            MouseButton::Left => {
722
-                if let Some(left_click_down) = self.left_click_down {
723
-                    if left_click_down == Point2::new(x, y) {
724
-                        self.user_events.push(UserEvent::Click(left_click_down));
725
-                    } else {
726
-                        let from = Point2::new(
727
-                            cmp::min(left_click_down.x as i32, x as i32) as f32,
728
-                            cmp::min(left_click_down.y as i32, y as i32) as f32,
729
-                        );
730
-                        let to = Point2::new(
731
-                            cmp::max(left_click_down.x as i32, x as i32) as f32,
732
-                            cmp::max(left_click_down.y as i32, y as i32) as f32,
733
-                        );
734
-                        self.user_events.push(UserEvent::AreaSelection(from, to));
735
-                    }
736
-                }
737
-                self.left_click_down = None;
738
-            }
739
-            MouseButton::Right => {
740
-                if let Some(right_click_down) = self.right_click_down {
741
-                    self.user_events
742
-                        .push(UserEvent::RightClick(right_click_down));
743
-                }
744
-            }
745
-            MouseButton::Middle => {}
746
-            MouseButton::Other(_) => {}
747
-        }
748
-    }
749
-
750
-    fn mouse_motion_event(&mut self, _ctx: &mut Context, x: f32, y: f32, _dx: f32, _dy: f32) {
751
-        self.current_cursor_position = Point2::new(x, y);
752
-    }
753
-}
754
-
755 52
 pub fn main() -> GameResult {
756 53
     let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
757 54
         let mut path = path::PathBuf::from(manifest_dir);

+ 12 - 0
src/physics/mod.rs View File

@@ -0,0 +1,12 @@
1
+pub mod position;
2
+pub mod util;
3
+
4
+#[derive(Debug)]
5
+pub enum PhysicEvent {
6
+    Explosion,
7
+}
8
+
9
+#[derive(Debug)]
10
+pub enum MetaEvent {
11
+    FeelExplosion,
12
+}

+ 11 - 0
src/physics/position.rs View File

@@ -0,0 +1,11 @@
1
+#[derive(Eq, PartialEq, Hash)]
2
+pub struct GridPosition {
3
+    x: i32,
4
+    y: i32,
5
+}
6
+
7
+impl GridPosition {
8
+    pub fn new(x: i32, y: i32) -> Self {
9
+        Self { x, y }
10
+    }
11
+}

+ 15 - 0
src/physics/util.rs View File

@@ -0,0 +1,15 @@
1
+use crate::physics::position::GridPosition;
2
+use crate::{Point2, Vector2, GRID_TILE_HEIGHT, GRID_TILE_WIDTH};
3
+
4
+pub fn vec_from_angle(angle: f32) -> Vector2 {
5
+    let vx = angle.sin();
6
+    let vy = angle.cos();
7
+    Vector2::new(vx, vy)
8
+}
9
+
10
+pub fn grid_position_from_position(position: &Point2) -> GridPosition {
11
+    GridPosition::new(
12
+        (position.x / GRID_TILE_WIDTH) as i32,
13
+        (position.y / GRID_TILE_HEIGHT) as i32,
14
+    )
15
+}

+ 111 - 0
src/scene/item.rs View File

@@ -0,0 +1,111 @@
1
+use ggez::graphics;
2
+
3
+use crate::{Point2, SCENE_ITEMS_SPRITE_SHEET_HEIGHT, SCENE_ITEMS_SPRITE_SHEET_WIDTH};
4
+use crate::behavior::ItemBehavior;
5
+use crate::physics::{MetaEvent, util};
6
+use crate::physics::position::GridPosition;
7
+use crate::scene::SpriteType;
8
+
9
+pub struct SceneItemSpriteInfo {
10
+    pub relative_start_y: f32,
11
+    pub relative_tile_width: f32,
12
+    pub relative_tile_height: f32,
13
+    pub tile_count: u16,
14
+    pub tile_width: f32,
15
+    pub tile_height: f32,
16
+    pub _half_tile_width: f32,
17
+    pub _half_tile_height: f32,
18
+}
19
+
20
+impl SceneItemSpriteInfo {
21
+    // TODO: ask on rust community if this is performant, or how to make it static
22
+    pub fn from_type(type_: &SpriteType) -> Self {
23
+        let (start_y, tile_width, tile_height, tile_count) = match type_ {
24
+            SpriteType::WalkingSoldier => (12.0, 12.0, 12.0, 8),
25
+            SpriteType::CrawlingSoldier => (26.0, 26.0, 26.0, 8),
26
+            SpriteType::StandingSoldier => (0.0, 12.0, 12.0, 1),
27
+        };
28
+
29
+        Self {
30
+            relative_start_y: start_y / SCENE_ITEMS_SPRITE_SHEET_HEIGHT,
31
+            relative_tile_width: tile_width / SCENE_ITEMS_SPRITE_SHEET_WIDTH,
32
+            relative_tile_height: tile_height / SCENE_ITEMS_SPRITE_SHEET_HEIGHT,
33
+            tile_count,
34
+            tile_width,
35
+            tile_height,
36
+            _half_tile_width: tile_width / 2.0,
37
+            _half_tile_height: tile_height / 2.0,
38
+        }
39
+    }
40
+}
41
+
42
+pub struct ItemState {
43
+    pub current_behavior: ItemBehavior,
44
+}
45
+
46
+impl ItemState {
47
+    pub fn new(current_behavior: ItemBehavior) -> Self {
48
+        Self { current_behavior }
49
+    }
50
+}
51
+
52
+pub enum SceneItemType {
53
+    Soldier,
54
+}
55
+
56
+pub struct SceneItem {
57
+    pub type_: SceneItemType,
58
+    pub position: Point2,
59
+    pub grid_position: GridPosition,
60
+    pub state: ItemState,
61
+    pub meta_events: Vec<MetaEvent>,
62
+    pub current_frame: u16,
63
+}
64
+
65
+impl SceneItem {
66
+    pub fn new(type_: SceneItemType, position: Point2, state: ItemState) -> Self {
67
+        Self {
68
+            type_,
69
+            position: position.clone(),
70
+            grid_position: util::grid_position_from_position(&position.clone()),
71
+            state,
72
+            meta_events: vec![],
73
+            current_frame: 0,
74
+        }
75
+    }
76
+
77
+    pub fn sprite_info(&self) -> SceneItemSpriteInfo {
78
+        SceneItemSpriteInfo::from_type(&self.sprite_type())
79
+    }
80
+
81
+    pub fn tick_sprite(&mut self) {
82
+        self.current_frame += 1;
83
+        // TODO: good way to have sprite info ? performant ?
84
+        if self.current_frame >= self.sprite_info().tile_count {
85
+            self.current_frame = 0;
86
+        }
87
+    }
88
+
89
+    pub fn as_draw_param(&self, current_frame: f32) -> graphics::DrawParam {
90
+        let sprite_info = self.sprite_info();
91
+        graphics::DrawParam::new()
92
+            .src(graphics::Rect::new(
93
+                current_frame as f32 * sprite_info.relative_tile_width,
94
+                sprite_info.relative_start_y,
95
+                sprite_info.relative_tile_width,
96
+                sprite_info.relative_tile_height,
97
+            ))
98
+            .rotation(90.0f32.to_radians())
99
+            .offset(Point2::new(0.5, 0.5))
100
+    }
101
+
102
+    pub fn sprite_type(&self) -> SpriteType {
103
+        // Here some logical about state, nature (soldier, tank, ...) and current behavior to
104
+        // determine sprite type
105
+        match self.state.current_behavior {
106
+            ItemBehavior::Crawling => SpriteType::CrawlingSoldier,
107
+            ItemBehavior::Walking(_) => SpriteType::WalkingSoldier,
108
+            ItemBehavior::Standing(_) => SpriteType::StandingSoldier,
109
+        }
110
+    }
111
+}

+ 525 - 0
src/scene/main.rs View File

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

+ 8 - 0
src/scene/mod.rs View File

@@ -0,0 +1,8 @@
1
+pub mod item;
2
+pub mod main;
3
+
4
+pub enum SpriteType {
5
+    WalkingSoldier,
6
+    CrawlingSoldier,
7
+    StandingSoldier,
8
+}

+ 64 - 0
src/ui/mod.rs View File

@@ -0,0 +1,64 @@
1
+use ggez::graphics;
2
+
3
+use crate::{Point2, UI_SPRITE_SHEET_HEIGHT, UI_SPRITE_SHEET_WIDTH};
4
+use crate::scene::item::SceneItem;
5
+use crate::ui::scene_item_menu::SceneItemMenuItem;
6
+
7
+pub mod scene_item_menu;
8
+
9
+pub enum UiItem {
10
+    SceneItemMenu,
11
+}
12
+
13
+pub struct UiSpriteInfo {
14
+    pub relative_start_x: f32,
15
+    pub relative_start_y: f32,
16
+    pub relative_width: f32,
17
+    pub relative_height: f32,
18
+    pub width: f32,
19
+    pub height: f32,
20
+}
21
+
22
+impl UiSpriteInfo {
23
+    pub fn from_type(type_: UiItem) -> Self {
24
+        match type_ {
25
+            UiItem::SceneItemMenu => Self {
26
+                relative_start_x: 0.0,
27
+                relative_start_y: 0.0,
28
+                relative_width: 71.0 / UI_SPRITE_SHEET_WIDTH,
29
+                relative_height: 68.0 / UI_SPRITE_SHEET_HEIGHT,
30
+                width: 71.0,
31
+                height: 68.0,
32
+            },
33
+        }
34
+    }
35
+
36
+    pub fn as_draw_param(&self) -> graphics::DrawParam {
37
+        graphics::DrawParam::new().src(graphics::Rect::new(
38
+            self.relative_start_x,
39
+            self.relative_start_y,
40
+            self.relative_width,
41
+            self.relative_height,
42
+        ))
43
+    }
44
+
45
+    pub fn which_item_clicked(
46
+        &self,
47
+        menu_position: Point2,
48
+        click_position: Point2,
49
+        scene_item: &SceneItem,
50
+    ) -> Option<SceneItemMenuItem> {
51
+        Some(SceneItemMenuItem::Move)
52
+    }
53
+}
54
+
55
+#[derive(Debug)]
56
+pub enum UserEvent {
57
+    Click(Point2),                 // Window coordinates
58
+    RightClick(Point2),            // Window coordinates
59
+    AreaSelection(Point2, Point2), // Window coordinates
60
+}
61
+
62
+pub enum SceneItemPrepareOrder {
63
+    Move(usize), // scene_item usize
64
+}

+ 3 - 0
src/ui/scene_item_menu.rs View File

@@ -0,0 +1,3 @@
1
+pub enum SceneItemMenuItem {
2
+    Move,
3
+}