Sfoglia il codice sorgente

Chapter 13 ready for editing.

Herbert Wolverson 5 anni fa
parent
commit
729f32cc55

File diff suppressed because it is too large
+ 191 - 1
book/src/chapter_13.md


+ 5 - 3
chapter-13-difficulty/src/main.rs

@@ -30,6 +30,7 @@ mod spawner;
 mod inventory_system;
 use inventory_system::{ ItemCollectionSystem, ItemUseSystem, ItemDropSystem };
 pub mod saveload_system;
+pub mod random_table;
 
 #[derive(PartialEq, Copy, Clone)]
 pub enum RunState { AwaitingInput, 
@@ -221,16 +222,17 @@ impl State {
 
         // Build a new map and place the player
         let worldmap;
+        let current_depth;
         {
             let mut worldmap_resource = self.ecs.write_resource::<Map>();
-            let current_depth = worldmap_resource.depth;
+            current_depth = worldmap_resource.depth;
             *worldmap_resource = Map::new_map_rooms_and_corridors(current_depth + 1);
             worldmap = worldmap_resource.clone();
         }
 
         // Spawn bad guys
         for room in worldmap.rooms.iter().skip(1) {
-            spawner::spawn_room(&mut self.ecs, room);
+            spawner::spawn_room(&mut self.ecs, room, current_depth+1);
         }
 
         // Place the player and update resources
@@ -312,7 +314,7 @@ fn main() {
 
     gs.ecs.insert(rltk::RandomNumberGenerator::new());
     for room in map.rooms.iter().skip(1) {
-        spawner::spawn_room(&mut gs.ecs, room);
+        spawner::spawn_room(&mut gs.ecs, room, 1);
     }
 
     gs.ecs.insert(map);

+ 47 - 0
chapter-13-difficulty/src/random_table.rs

@@ -0,0 +1,47 @@
+use rltk::RandomNumberGenerator;
+
+pub struct RandomEntry {
+    name : String,
+    weight : i32
+}
+
+impl RandomEntry {
+    pub fn new<S:ToString>(name: S, weight: i32) -> RandomEntry {
+        RandomEntry{ name: name.to_string(), weight }
+    }
+}
+
+#[derive(Default)]
+pub struct RandomTable {
+    entries : Vec<RandomEntry>,
+    total_weight : i32
+}
+
+impl RandomTable {
+    pub fn new() -> RandomTable {
+        RandomTable{ entries: Vec::new(), total_weight: 0 }
+    }
+
+    pub fn add<S:ToString>(mut self, name : S, weight: i32) -> RandomTable {
+        self.total_weight += weight;
+        self.entries.push(RandomEntry::new(name.to_string(), weight));
+        self
+    }
+
+    pub fn roll(&self, rng : &mut RandomNumberGenerator) -> String {
+        if self.total_weight == 0 { return "None".to_string(); }
+        let mut roll = rng.roll_dice(1, self.total_weight)-1;
+        let mut index : usize = 0;
+
+        while roll > 0 {
+            if roll < self.entries[index].weight {
+                return self.entries[index].name.clone();
+            }
+
+            roll -= self.entries[index].weight;
+            index += 1;
+        }
+
+        "None".to_string()
+    }
+}

+ 38 - 61
chapter-13-difficulty/src/spawner.rs

@@ -3,8 +3,10 @@ use rltk::{ RGB, RandomNumberGenerator };
 extern crate specs;
 use specs::prelude::*;
 use super::{CombatStats, Player, Renderable, Name, Position, Viewshed, Monster, BlocksTile, Rect, Item, 
-    Consumable, Ranged, ProvidesHealing, map::MAPWIDTH, InflictsDamage, AreaOfEffect, Confusion, SerializeMe};
+    Consumable, Ranged, ProvidesHealing, map::MAPWIDTH, InflictsDamage, AreaOfEffect, Confusion, SerializeMe,
+    random_table::RandomTable};
 use crate::specs::saveload::{MarkedBuilder, SimpleMarker};
+use std::collections::HashMap;
 
 /// Spawns the player and returns his/her entity object.
 pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
@@ -26,84 +28,59 @@ pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
 }
 
 const MAX_MONSTERS : i32 = 4;
-const MAX_ITEMS : i32 = 2;
+
+fn room_table(map_depth: i32) -> RandomTable {
+    RandomTable::new()
+        .add("Goblin", 10)
+        .add("Orc", 1 + map_depth)
+        .add("Health Potion", 7)
+        .add("Fireball Scroll", 2 + map_depth)
+        .add("Confusion Scroll", 2 + map_depth)
+        .add("Magic Missile Scroll", 4)
+}
 
 /// Fills a room with stuff!
-pub fn spawn_room(ecs: &mut World, room : &Rect) {
-    let mut monster_spawn_points : Vec<usize> = Vec::new();
-    let mut item_spawn_points : Vec<usize> = Vec::new();
+#[allow(clippy::map_entry)]
+pub fn spawn_room(ecs: &mut World, room : &Rect, map_depth: i32) {
+    let spawn_table = room_table(map_depth);
+    let mut spawn_points : HashMap<usize, String> = HashMap::new();
 
     // Scope to keep the borrow checker happy
     {
         let mut rng = ecs.write_resource::<RandomNumberGenerator>();
-        let num_monsters = rng.roll_dice(1, MAX_MONSTERS + 2) - 3;
-        let num_items = rng.roll_dice(1, MAX_ITEMS + 2) - 3;
+        let num_spawns = rng.roll_dice(1, MAX_MONSTERS + 3) + (map_depth - 1) - 3;
 
-        for _i in 0 .. num_monsters {
+        for _i in 0 .. num_spawns {
             let mut added = false;
-            while !added {
+            let mut tries = 0;
+            while !added && tries < 20 {
                 let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
                 let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
                 let idx = (y * MAPWIDTH) + x;
-                if !monster_spawn_points.contains(&idx) {
-                    monster_spawn_points.push(idx);
-                    added = true;
-                }
-            }
-        }
-
-        for _i in 0 .. num_items {
-            let mut added = false;
-            while !added {
-                let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
-                let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
-                let idx = (y * MAPWIDTH) + x;
-                if !item_spawn_points.contains(&idx) {
-                    item_spawn_points.push(idx);
+                if !spawn_points.contains_key(&idx) {
+                    spawn_points.insert(idx, spawn_table.roll(&mut rng));
                     added = true;
+                } else {
+                    tries += 1;
                 }
             }
         }
     }
 
     // Actually spawn the monsters
-    for idx in monster_spawn_points.iter() {
-        let x = *idx % MAPWIDTH;
-        let y = *idx / MAPWIDTH;
-        random_monster(ecs, x as i32, y as i32);
-    }
-
-    // Actually spawn the items
-    for idx in item_spawn_points.iter() {
-        let x = *idx % MAPWIDTH;
-        let y = *idx / MAPWIDTH;
-        random_item(ecs, x as i32, y as i32);
-    }
-}
-
-fn random_monster(ecs: &mut World, x: i32, y: i32) {
-    let roll :i32;
-    {
-        let mut rng = ecs.write_resource::<RandomNumberGenerator>();
-        roll = rng.roll_dice(1, 2);
-    }
-    match roll {
-        1 => { orc(ecs, x, y) }
-        _ => { goblin(ecs, x, y) }
-    }
-}
-
-fn random_item(ecs: &mut World, x: i32, y: i32) {
-    let roll :i32;
-    {
-        let mut rng = ecs.write_resource::<RandomNumberGenerator>();
-        roll = rng.roll_dice(1, 4);
-    }
-    match roll {
-        1 => { health_potion(ecs, x, y) }
-        2 => { fireball_scroll(ecs, x, y) }
-        3 => { confusion_scroll(ecs, x, y) }
-        _ => { magic_missile_scroll(ecs, x, y) }
+    for spawn in spawn_points.iter() {
+        let x = (*spawn.0 % MAPWIDTH) as i32;
+        let y = (*spawn.0 / MAPWIDTH) as i32;
+
+        match spawn.1.as_ref() {
+            "Goblin" => goblin(ecs, x, y),
+            "Orc" => orc(ecs, x, y),
+            "Health Potion" => health_potion(ecs, x, y),
+            "Fireball Scroll" => fireball_scroll(ecs, x, y),
+            "Confusion Scroll" => confusion_scroll(ecs, x, y),
+            "Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
+            _ => {}
+        }
     }
 }