Browse Source

Add chapter 20 - magic mapping.

Herbert Wolverson 4 years ago
parent
commit
8b29693624

BIN
book/src/c20-s2.gif


+ 73 - 0
book/src/chapter_20.md

@@ -109,6 +109,79 @@ There are some framework changes also (see the source); we've done this often en
 
 ## Making it pretty
 
+While the code presented there is effective, it isn't visually attractive. It's nice to include fluff in games, and let the user be pleasantly surprised by the beauty of an ASCII terminal from time to time! We'll start by modifying `inventory_system.rs` again:
+
+```rust
+// If its a magic mapper...
+let is_mapper = magic_mapper.get(useitem.item);
+match is_mapper {
+    None => {}
+    Some(_) => {
+        used_item = true;
+        gamelog.entries.insert(0, "The map is revealed to you!".to_string());
+        *runstate = RunState::MagicMapReveal{ row : 0};
+    }
+}
+```
+
+Notice that instead of modifying the map, we are just changing the game state to mapping mode. We don't actually support doing that yet, so lets go into the state mapper in `main.rs` and modify `PlayerTurn` to handle it:
+
+```rust
+RunState::PlayerTurn => {
+    self.systems.dispatch(&self.ecs);
+    self.ecs.maintain();
+    match *self.ecs.fetch::<RunState>() {
+        RunState::MagicMapReveal{ .. } => newrunstate = RunState::MagicMapReveal{ row: 0 },
+        _ => newrunstate = RunState::MonsterTurn
+    }                
+}
+```
+
+While we're here, lets add the state to `RunState`:
+
+```rust
+#[derive(PartialEq, Copy, Clone)]
+pub enum RunState { AwaitingInput, 
+    PreRun, 
+    PlayerTurn, 
+    MonsterTurn, 
+    ShowInventory, 
+    ShowDropItem, 
+    ShowTargeting { range : i32, item : Entity},
+    MainMenu { menu_selection : gui::MainMenuSelection },
+    SaveGame,
+    NextLevel,
+    ShowRemoveItem,
+    GameOver,
+    MagicMapReveal { row : i32 }
+}
+```
+
+We also add some logic to the tick loop for the new state:
+
+```rust
+RunState::MagicMapReveal{row} => {
+    let mut map = self.ecs.fetch_mut::<Map>();
+    for x in 0..MAPWIDTH {
+        let idx = map.xy_idx(x as i32,row);
+        map.revealed_tiles[idx] = true;
+    }
+    if row as usize == MAPHEIGHT-1 {
+        newrunstate = RunState::MonsterTurn;
+    } else {
+        newrunstate = RunState::MagicMapReveal{ row: row+1 };
+    }
+}
+```
+
+This is pretty straightforward: it reveals the tiles on the current row, and then if we haven't hit the bottom of the map - it adds to row. If we have, it returns to where we were - `MonsterTurn`. If you `cargo run` now, find a magic mapping scroll and use it, the map fades in nicely:
+
+![Screenshot](./c20-s2.gif)
+
+## Wrap Up
+
+This was a relatively quick chapter, but we now have another staple of the roguelike genre: magic mapping.
+
 
 **The source code for this chapter may be found [here](https://github.com/thebracket/rustrogueliketutorial/tree/master/chapter-20-magicmapping)**
 

+ 6 - 7
chapter-20-magicmapping/src/inventory_system.rs

@@ -3,7 +3,7 @@ use specs::prelude::*;
 use super::{WantsToPickupItem, Name, InBackpack, Position, gamelog::GameLog, WantsToUseItem, 
     Consumable, ProvidesHealing, CombatStats, WantsToDropItem, InflictsDamage, Map, SufferDamage,
     AreaOfEffect, Confusion, Equippable, Equipped, WantsToRemoveItem, particle_system::ParticleBuilder,
-    ProvidesFood, HungerClock, HungerState, MagicMapper};
+    ProvidesFood, HungerClock, HungerState, MagicMapper, RunState};
 
 pub struct ItemCollectionSystem {}
 
@@ -57,15 +57,16 @@ impl<'a> System<'a> for ItemUseSystem {
                         ReadStorage<'a, Position>,
                         ReadStorage<'a, ProvidesFood>,
                         WriteStorage<'a, HungerClock>,
-                        ReadStorage<'a, MagicMapper>
+                        ReadStorage<'a, MagicMapper>,
+                        WriteExpect<'a, RunState>
                       );
 
     #[allow(clippy::cognitive_complexity)]
     fn run(&mut self, data : Self::SystemData) {
-        let (player_entity, mut gamelog, mut map, entities, mut wants_use, names, 
+        let (player_entity, mut gamelog, map, entities, mut wants_use, names, 
             consumables, healing, inflict_damage, mut combat_stats, mut suffer_damage, 
             aoe, mut confused, equippable, mut equipped, mut backpack, mut particle_builder, positions,
-            provides_food, mut hunger_clocks, magic_mapper) = data;
+            provides_food, mut hunger_clocks, magic_mapper, mut runstate) = data;
 
         for (entity, useitem) in (&entities, &wants_use).join() {
             let mut used_item = true;
@@ -153,10 +154,8 @@ impl<'a> System<'a> for ItemUseSystem {
                 None => {}
                 Some(_) => {
                     used_item = true;
-                    for r in map.revealed_tiles.iter_mut() {
-                        *r = true;
-                    }
                     gamelog.entries.insert(0, "The map is revealed to you!".to_string());
+                    *runstate = RunState::MagicMapReveal{ row : 0};
                 }
             }
 

+ 18 - 2
chapter-20-magicmapping/src/main.rs

@@ -46,7 +46,8 @@ pub enum RunState { AwaitingInput,
     SaveGame,
     NextLevel,
     ShowRemoveItem,
-    GameOver
+    GameOver,
+    MagicMapReveal { row : i32 }
 }
 
 pub struct State {
@@ -96,7 +97,10 @@ impl GameState for State {
             RunState::PlayerTurn => {
                 self.systems.dispatch(&self.ecs);
                 self.ecs.maintain();
-                newrunstate = RunState::MonsterTurn;
+                match *self.ecs.fetch::<RunState>() {
+                    RunState::MagicMapReveal{ .. } => newrunstate = RunState::MagicMapReveal{ row: 0 },
+                    _ => newrunstate = RunState::MonsterTurn
+                }                
             }
             RunState::MonsterTurn => {
                 self.systems.dispatch(&self.ecs);
@@ -195,6 +199,18 @@ impl GameState for State {
                 self.goto_next_level();                
                 newrunstate = RunState::PreRun;
             }
+            RunState::MagicMapReveal{row} => {
+                let mut map = self.ecs.fetch_mut::<Map>();
+                for x in 0..MAPWIDTH {
+                    let idx = map.xy_idx(x as i32,row);
+                    map.revealed_tiles[idx] = true;
+                }
+                if row as usize == MAPHEIGHT-1 {
+                    newrunstate = RunState::MonsterTurn;
+                } else {
+                    newrunstate = RunState::MagicMapReveal{ row: row+1 };
+                }
+            }
         }
 
         {