浏览代码

Add chapter 20 - magic mapping.

Herbert Wolverson 5 年之前
父节点
当前提交
8b29693624
共有 4 个文件被更改,包括 97 次插入9 次删除
  1. 二进制
      book/src/c20-s2.gif
  2. 73 0
      book/src/chapter_20.md
  3. 6 7
      chapter-20-magicmapping/src/inventory_system.rs
  4. 18 2
      chapter-20-magicmapping/src/main.rs

二进制
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
 ## 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)**
 **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, 
 use super::{WantsToPickupItem, Name, InBackpack, Position, gamelog::GameLog, WantsToUseItem, 
     Consumable, ProvidesHealing, CombatStats, WantsToDropItem, InflictsDamage, Map, SufferDamage,
     Consumable, ProvidesHealing, CombatStats, WantsToDropItem, InflictsDamage, Map, SufferDamage,
     AreaOfEffect, Confusion, Equippable, Equipped, WantsToRemoveItem, particle_system::ParticleBuilder,
     AreaOfEffect, Confusion, Equippable, Equipped, WantsToRemoveItem, particle_system::ParticleBuilder,
-    ProvidesFood, HungerClock, HungerState, MagicMapper};
+    ProvidesFood, HungerClock, HungerState, MagicMapper, RunState};
 
 
 pub struct ItemCollectionSystem {}
 pub struct ItemCollectionSystem {}
 
 
@@ -57,15 +57,16 @@ impl<'a> System<'a> for ItemUseSystem {
                         ReadStorage<'a, Position>,
                         ReadStorage<'a, Position>,
                         ReadStorage<'a, ProvidesFood>,
                         ReadStorage<'a, ProvidesFood>,
                         WriteStorage<'a, HungerClock>,
                         WriteStorage<'a, HungerClock>,
-                        ReadStorage<'a, MagicMapper>
+                        ReadStorage<'a, MagicMapper>,
+                        WriteExpect<'a, RunState>
                       );
                       );
 
 
     #[allow(clippy::cognitive_complexity)]
     #[allow(clippy::cognitive_complexity)]
     fn run(&mut self, data : Self::SystemData) {
     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, 
             consumables, healing, inflict_damage, mut combat_stats, mut suffer_damage, 
             aoe, mut confused, equippable, mut equipped, mut backpack, mut particle_builder, positions,
             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() {
         for (entity, useitem) in (&entities, &wants_use).join() {
             let mut used_item = true;
             let mut used_item = true;
@@ -153,10 +154,8 @@ impl<'a> System<'a> for ItemUseSystem {
                 None => {}
                 None => {}
                 Some(_) => {
                 Some(_) => {
                     used_item = true;
                     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());
                     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,
     SaveGame,
     NextLevel,
     NextLevel,
     ShowRemoveItem,
     ShowRemoveItem,
-    GameOver
+    GameOver,
+    MagicMapReveal { row : i32 }
 }
 }
 
 
 pub struct State {
 pub struct State {
@@ -96,7 +97,10 @@ impl GameState for State {
             RunState::PlayerTurn => {
             RunState::PlayerTurn => {
                 self.systems.dispatch(&self.ecs);
                 self.systems.dispatch(&self.ecs);
                 self.ecs.maintain();
                 self.ecs.maintain();
-                newrunstate = RunState::MonsterTurn;
+                match *self.ecs.fetch::<RunState>() {
+                    RunState::MagicMapReveal{ .. } => newrunstate = RunState::MagicMapReveal{ row: 0 },
+                    _ => newrunstate = RunState::MonsterTurn
+                }                
             }
             }
             RunState::MonsterTurn => {
             RunState::MonsterTurn => {
                 self.systems.dispatch(&self.ecs);
                 self.systems.dispatch(&self.ecs);
@@ -195,6 +199,18 @@ impl GameState for State {
                 self.goto_next_level();                
                 self.goto_next_level();                
                 newrunstate = RunState::PreRun;
                 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 };
+                }
+            }
         }
         }
 
 
         {
         {