1#![allow(clippy::needless_return)]
4#![allow(clippy::implicit_return)]
5#![allow(clippy::uninlined_format_args)]
6#![allow(clippy::items_after_statements)]
7#![allow(clippy::unnecessary_cast)]
8#![allow(clippy::doc_markdown)]
9#![allow(clippy::cast_sign_loss)]
10#![allow(clippy::explicit_iter_loop)]
11#![allow(clippy::format_in_format_args)]
12#![allow(clippy::cast_precision_loss)]
13use tiles_tools::{
29 coordinates::{square::{Coordinate as SquareCoord, FourConnected}, Distance, Neighbors},
30 pathfind::astar,
31 ecs::{World, Position, Health},
32 debug::{GridRenderer, GridStyle, DebugColor},
33 game_systems::{TurnBasedGame, ResourceManager, GameStateMachine, GameState, GameStateEvent},
34 events::{EventBus, EventResult},
35};
36
37fn main() {
38 println!("š® Tiles Tools Beginner Tutorial");
39 println!("=================================");
40 println!("Welcome to your first tile-based game!");
41 println!("We'll build a simple dungeon explorer step by step.\n");
42
43 tutorial_step_1_coordinates();
45
46 tutorial_step_2_map_creation();
48
49 tutorial_step_3_player_basics();
51
52 tutorial_step_4_movement();
54
55 tutorial_step_5_entities();
57
58 tutorial_step_6_combat();
60
61 tutorial_step_7_debugging();
63
64 tutorial_step_8_complete_game();
66
67 println!("\nš Tutorial Complete!");
68 println!("You've learned the fundamentals of tile-based game development with tiles_tools!");
69 println!("\nNext steps:");
70 println!("⢠Check out the other examples for advanced features");
71 println!("⢠Experiment with different coordinate systems (hexagonal, triangular)");
72 println!("⢠Try the animation system for smooth movement");
73 println!("⢠Explore the behavior tree system for advanced AI");
74 println!("⢠Add serialization for save/load functionality");
75}
76
77fn tutorial_step_1_coordinates() {
79 println!("š Step 1: Understanding Coordinates");
80 println!("------------------------------------");
81 println!("Tiles_tools supports multiple coordinate systems. Let's start with square tiles.");
82
83 let start = SquareCoord::<FourConnected>::new(0, 0);
85 let destination = SquareCoord::<FourConnected>::new(3, 2);
86
87 println!("Starting position: ({}, {})", start.x, start.y);
88 println!("Destination: ({}, {})", destination.x, destination.y);
89
90 let distance = start.distance(&destination);
92 println!("Manhattan distance: {}", distance);
93
94 let neighbors = start.neighbors();
96 println!("Neighbors of start position:");
97 for (i, neighbor) in neighbors.iter().enumerate() {
98 println!(" {}: ({}, {})", i + 1, neighbor.x, neighbor.y);
99 }
100
101 println!("ā
Coordinates are the foundation of tile-based games!\n");
102}
103
104fn tutorial_step_2_map_creation() {
106 println!("šŗļø Step 2: Creating a Simple Map");
107 println!("--------------------------------");
108
109 let dungeon_map = [
111 [false, false, false, false, false, false, false, false],
112 [false, true, true, true, false, true, true, false],
113 [false, true, false, true, false, true, false, false],
114 [false, true, true, true, true, true, true, false],
115 [false, false, false, true, false, false, true, false],
116 [false, true, true, true, false, true, true, false],
117 [false, false, false, false, false, false, false, false],
118 ];
119
120 println!("Created a {}x{} dungeon map:", dungeon_map[0].len(), dungeon_map.len());
121 println!("Legend: # = wall, . = floor");
122
123 for row in &dungeon_map {
125 print!(" ");
126 for &cell in row {
127 print!("{}", if cell { "." } else { "#" });
128 }
129 println!();
130 }
131
132 let is_walkable = |coord: &SquareCoord<FourConnected>| -> bool {
134 let x = coord.x as usize;
135 let y = coord.y as usize;
136 if y >= dungeon_map.len() || x >= dungeon_map[0].len() {
137 return false;
138 }
139 dungeon_map[y][x]
140 };
141
142 let test_positions = [
144 SquareCoord::<FourConnected>::new(1, 1), SquareCoord::<FourConnected>::new(0, 0), SquareCoord::<FourConnected>::new(3, 3), ];
148
149 println!("\nTesting walkability:");
150 for pos in &test_positions {
151 println!(" ({}, {}): {}", pos.x, pos.y,
152 if is_walkable(pos) { "walkable" } else { "blocked" });
153 }
154
155 println!("ā
Map creation is essential for defining your game world!\n");
156}
157
158fn tutorial_step_3_player_basics() {
160 println!("š§ Step 3: Adding a Player Character");
161 println!("------------------------------------");
162
163 let mut world = World::new();
165
166 let player_position = Position::new(SquareCoord::<FourConnected>::new(1, 1));
168 let player_health = Health::new(100);
169
170 let player = world.spawn((player_position, player_health));
171 println!("Created player entity with ID: {:?}", player);
172
173 for (entity, (pos, health)) in world.query::<(&Position<SquareCoord<FourConnected>>, &Health)>().iter() {
175 if entity == player {
176 println!("Player stats:");
177 println!(" Position: ({}, {})", pos.coord.x, pos.coord.y);
178 println!(" Health: {}/{}", health.current, health.maximum);
179 }
180 }
181
182 println!("Player moved to (2, 1) - position updates would be handled by game systems");
185
186 println!("ā
Entities are the building blocks of your game objects!\n");
187}
188
189fn tutorial_step_4_movement() {
191 println!("š¶ Step 4: Movement and Pathfinding");
192 println!("-----------------------------------");
193
194 let obstacles: Vec<SquareCoord<FourConnected>> = vec![
196 SquareCoord::new(2, 1),
197 SquareCoord::new(2, 2),
198 SquareCoord::new(2, 3),
199 SquareCoord::new(4, 2),
200 SquareCoord::new(4, 3),
201 ];
202
203 let start = SquareCoord::<FourConnected>::new(1, 1);
205 let goal = SquareCoord::<FourConnected>::new(6, 3);
206
207 println!("Finding path from ({}, {}) to ({}, {})", start.x, start.y, goal.x, goal.y);
208 println!("Obstacles at: {:?}", obstacles);
209
210 let path_result = astar(
212 &start,
213 &goal,
214 |coord| !obstacles.contains(coord), |_coord| 1, );
217
218 match path_result {
219 Some((path, cost)) => {
220 println!("ā
Path found! {} steps with cost {}:", path.len(), cost);
221 for (i, step) in path.iter().enumerate() {
222 println!(" Step {}: ({}, {})", i, step.x, step.y);
223 }
224
225 println!("\nValidating movement:");
227 for i in 1..path.len().min(4) { let from = &path[i-1];
229 let to = &path[i];
230 let is_valid = from.distance(to) == 1; println!(" {} -> {}: {}",
232 format!("({}, {})", from.x, from.y),
233 format!("({}, {})", to.x, to.y),
234 if is_valid { "valid" } else { "invalid" });
235 }
236 },
237 None => {
238 println!("ā No path found to destination!");
239 }
240 }
241
242 println!("ā
Pathfinding enables intelligent movement in your game!\n");
243}
244
245fn tutorial_step_5_entities() {
247 println!("š¾ Step 5: Adding Game Entities");
248 println!("-------------------------------");
249
250 let mut world = World::new();
251
252 let player = world.spawn((
256 Position::new(SquareCoord::<FourConnected>::new(1, 1)),
257 Health::new(100),
258 ));
259
260 let goblin = world.spawn((
262 Position::new(SquareCoord::<FourConnected>::new(5, 2)),
263 Health::new(30),
264 ));
265
266 let orc = world.spawn((
267 Position::new(SquareCoord::<FourConnected>::new(6, 4)),
268 Health::new(60),
269 ));
270
271 let treasure = world.spawn((
273 Position::new(SquareCoord::<FourConnected>::new(3, 5)),
274 ));
275
276 println!("Created entities:");
277 println!(" Player: {:?}", player);
278 println!(" Goblin: {:?}", goblin);
279 println!(" Orc: {:?}", orc);
280 println!(" Treasure: {:?}", treasure);
281
282 let mut entity_count = 0;
284 let mut entities_with_health = 0;
285
286 for (entity, (pos, health)) in world.query::<(&Position<SquareCoord<FourConnected>>, Option<&Health>)>().iter() {
288 entity_count += 1;
289 if health.is_some() {
290 entities_with_health += 1;
291 }
292
293 let entity_type = if entity == player {
294 "Player"
295 } else if entity == goblin {
296 "Goblin"
297 } else if entity == orc {
298 "Orc"
299 } else if entity == treasure {
300 "Treasure"
301 } else {
302 "Unknown"
303 };
304
305 if let Some(hp) = health {
306 println!(" {} at ({}, {}): {}/{} HP",
307 entity_type, pos.coord.x, pos.coord.y, hp.current, hp.maximum);
308 } else {
309 println!(" {} at ({}, {}): No health",
310 entity_type, pos.coord.x, pos.coord.y);
311 }
312 }
313
314 println!("\nEntity summary:");
315 println!(" Total entities: {}", entity_count);
316 println!(" Living entities: {}", entities_with_health);
317
318 println!("ā
Different entity types add variety to your game world!\n");
319}
320
321fn tutorial_step_6_combat() {
323 println!("āļø Step 6: Simple Combat System");
324 println!("-------------------------------");
325
326 let mut world = World::new();
328 let mut resource_manager = ResourceManager::new();
329
330 let player = world.spawn((
332 Position::new(SquareCoord::<FourConnected>::new(2, 2)),
333 Health::new(100),
334 ));
335
336 let goblin = world.spawn((
337 Position::new(SquareCoord::<FourConnected>::new(3, 2)),
338 Health::new(30),
339 ));
340
341 resource_manager.add_entity(player.id() as u32, 100.0, 50.0);
343 resource_manager.add_entity(goblin.id() as u32, 30.0, 0.0);
344
345 println!("Combat scenario: Player vs Goblin");
346 println!("Player at (2,2) - Goblin at (3,2)");
347
348 let mut player_pos = None;
350 let mut goblin_pos = None;
351
352 for (entity, pos) in world.query::<&Position<SquareCoord<FourConnected>>>().iter() {
353 if entity == player {
354 player_pos = Some(pos.coord);
355 } else if entity == goblin {
356 goblin_pos = Some(pos.coord);
357 } else {
358 }
360 }
361
362 if let (Some(player_coord), Some(goblin_coord)) = (player_pos, goblin_pos) {
363 let distance = player_coord.distance(&goblin_coord);
364 println!("Distance between combatants: {}", distance);
365
366 if distance == 1 {
367 println!("Combatants are adjacent - combat can begin!");
368
369 for round in 1..=3 {
371 println!("\n--- Round {} ---", round);
372
373 let player_damage = 15;
375 resource_manager.modify_health(goblin.id() as u32, -(player_damage as f32));
376
377 if let Some(goblin_resources) = resource_manager.get_resources(goblin.id() as u32) {
378 println!("Player attacks for {} damage!", player_damage);
379 println!("Goblin health: {}/{}",
380 goblin_resources.health.current,
381 goblin_resources.health.maximum);
382
383 if goblin_resources.health.current <= 0.0 {
384 println!("š Goblin defeated!");
385 break;
386 }
387 }
388
389 if let Some(goblin_resources) = resource_manager.get_resources(goblin.id() as u32) {
391 if goblin_resources.health.current > 0.0 {
392 let goblin_damage = 8;
393 resource_manager.modify_health(player.id() as u32, -(goblin_damage as f32));
394
395 if let Some(player_resources) = resource_manager.get_resources(player.id() as u32) {
396 println!("Goblin attacks for {} damage!", goblin_damage);
397 println!("Player health: {}/{}",
398 player_resources.health.current,
399 player_resources.health.maximum);
400 }
401 }
402 }
403 }
404 } else {
405 println!("Combatants are too far apart for melee combat");
406 }
407 }
408
409 println!("ā
Combat systems bring challenge and interaction to your game!\n");
410}
411
412fn tutorial_step_7_debugging() {
414 println!("š Step 7: Visual Debugging");
415 println!("---------------------------");
416 println!("Debugging tools help you visualize and understand your game state.");
417
418 let mut debug_renderer = GridRenderer::new()
420 .with_size(10, 8)
421 .with_style(GridStyle::Square4);
422
423 debug_renderer.add_colored_marker((2, 2), "P", "Player", DebugColor::Green, 20);
425
426 debug_renderer.add_colored_marker((5, 3), "G", "Goblin", DebugColor::Red, 15);
428 debug_renderer.add_colored_marker((7, 5), "O", "Orc", DebugColor::Red, 15);
429
430 debug_renderer.add_colored_marker((8, 1), "T", "Treasure", DebugColor::Yellow, 10);
432
433 let walls = vec![(3, 1), (3, 2), (3, 3), (6, 4), (6, 5), (6, 6)];
435 for (x, y) in walls {
436 debug_renderer.add_colored_marker((x, y), "#", "Wall", DebugColor::Gray, 5);
437 }
438
439 let path = vec![(2, 2), (1, 2), (1, 3), (1, 4), (2, 4), (4, 4), (5, 4), (5, 3)];
441 debug_renderer.add_path(path, "Player Path", DebugColor::Blue);
442
443 println!("Game world visualization:");
444 println!("{}", debug_renderer.render_ascii());
445
446 debug_renderer.clear(); debug_renderer.add_colored_marker((2, 2), "P", "Player (HP: 85/100)", DebugColor::Green, 20);
451 debug_renderer.add_colored_marker((5, 3), "G", "Goblin (HP: 15/30)", DebugColor::Red, 15);
452 debug_renderer.add_annotation((2, 1), "Start", DebugColor::Blue);
453 debug_renderer.add_annotation((8, 1), "Goal", DebugColor::Yellow);
454
455 println!("\nDetailed game state with annotations:");
456 println!("{}", debug_renderer.render_ascii());
457
458 println!("ā
Visual debugging is crucial for understanding complex game states!\n");
459}
460
461fn tutorial_step_8_complete_game() {
463 println!("šÆ Step 8: Complete Mini-Game");
464 println!("-----------------------------");
465 println!("Let's put everything together into a working game!");
466
467 let mut world = World::new();
469 let mut turn_game = TurnBasedGame::new();
470 let mut resource_manager = ResourceManager::new();
471 let mut event_bus = EventBus::new();
472 let mut state_machine = GameStateMachine::new(GameState::Playing);
473
474 #[derive(Debug, Clone)]
476 struct PlayerMoved {
477 from: (i32, i32),
478 to: (i32, i32),
479 }
480
481 #[derive(Debug, Clone)]
482 struct EnemyDefeated {
483 enemy_type: String,
484 position: (i32, i32),
485 }
486
487 #[derive(Debug, Clone)]
488 struct TreasureFound {
489 position: (i32, i32),
490 value: u32,
491 }
492
493 event_bus.subscribe(|event: &PlayerMoved| {
497 println!("š Player moved from ({}, {}) to ({}, {})",
498 event.from.0, event.from.1, event.to.0, event.to.1);
499 EventResult::Continue
500 });
501
502 event_bus.subscribe(|event: &EnemyDefeated| {
503 println!("š {} defeated at ({}, {})!",
504 event.enemy_type, event.position.0, event.position.1);
505 EventResult::Continue
506 });
507
508 event_bus.subscribe(|event: &TreasureFound| {
509 println!("š° Found treasure worth {} gold at ({}, {})!",
510 event.value, event.position.0, event.position.1);
511 EventResult::Continue
512 });
513
514 let player = world.spawn((
516 Position::new(SquareCoord::<FourConnected>::new(1, 1)),
517 Health::new(100),
518 ));
519
520 let goblin = world.spawn((
521 Position::new(SquareCoord::<FourConnected>::new(4, 2)),
522 Health::new(25),
523 ));
524
525 let _treasure = world.spawn((
526 Position::new(SquareCoord::<FourConnected>::new(6, 3)),
527 ));
528
529 turn_game.add_participant(player.id() as u32, 100); turn_game.add_participant(goblin.id() as u32, 80); resource_manager.add_entity(player.id() as u32, 100.0, 30.0);
534 resource_manager.add_entity(goblin.id() as u32, 25.0, 0.0);
535
536 println!("š® Mini Dungeon Explorer Started!");
537 println!("Player Goal: Defeat the goblin and find the treasure");
538
539 let mut player_gold = 0;
541 let mut enemies_defeated = 0;
542
543 println!("\n=== Game Simulation ===");
545
546 let player_start = SquareCoord::<FourConnected>::new(1, 1);
548 let goblin_pos = SquareCoord::<FourConnected>::new(4, 2);
549
550 event_bus.publish(PlayerMoved {
551 from: (player_start.x, player_start.y),
552 to: (goblin_pos.x - 1, goblin_pos.y)
553 });
554
555 println!("āļø Player attacks goblin!");
557 resource_manager.modify_health(goblin.id() as u32, -25.0); event_bus.publish(EnemyDefeated {
560 enemy_type: "Goblin".to_string(),
561 position: (goblin_pos.x, goblin_pos.y),
562 });
563 enemies_defeated += 1;
564
565 let treasure_pos = SquareCoord::<FourConnected>::new(6, 3);
567 event_bus.publish(TreasureFound {
568 position: (treasure_pos.x, treasure_pos.y),
569 value: 100,
570 });
571 player_gold += 100;
572
573 state_machine.process_event(GameStateEvent::VictoryAchieved);
575 println!("š Victory! Game completed!");
576
577 event_bus.process_events();
578
579 println!("\nš Game Summary:");
581 println!(" Final State: {:?}", state_machine.current_state());
582 println!(" Player Gold: {}", player_gold);
583 println!(" Enemies Defeated: {}", enemies_defeated);
584
585 if state_machine.current_state() == GameState::Victory {
586 println!("š Congratulations! You've mastered the basics of tiles_tools!");
587 } else {
588 println!("ā° Game ended - but you've learned the fundamentals!");
589 }
590
591 println!("ā
You've built a complete tile-based game using tiles_tools!\n");
592}