|
@@ -5,25 +5,142 @@ use super::{CombatStats, Player, Renderable,
|
|
|
Monster, BlocksTile, Rect,
|
|
|
MAPWIDTH, Item, ProvidesHealing,
|
|
|
Consumable, InflictsDamage, Ranged,
|
|
|
- AreaOfEffect,SerializeMe};
|
|
|
+ AreaOfEffect,SerializeMe,RandomTable,
|
|
|
+ EquipmentSlot,Equippable, MeleePowerBonus,
|
|
|
+ DefenseBonus};
|
|
|
use specs::saveload::{MarkedBuilder, SimpleMarker};
|
|
|
+use std::collections::HashMap;
|
|
|
|
|
|
const MAX_MONSTERS : i32 = 4;
|
|
|
-const MAX_ITEMS : i32 = 2; // PER MAP
|
|
|
|
|
|
-fn random_item(ecs: &mut World, x: i32, y: i32) {
|
|
|
- let roll :i32;
|
|
|
+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("Magic Missile Scroll", 4 + map_depth)
|
|
|
+ .add("Staff", 3)
|
|
|
+ .add("Holy Cross", 3)
|
|
|
+ .add("Shepards Staff", map_depth - 1)
|
|
|
+ .add("Tower Shield", map_depth - 1)
|
|
|
+}
|
|
|
+
|
|
|
+#[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>();
|
|
|
- roll = rng.roll_dice(1, 3);
|
|
|
+ let num_spawns = rng.roll_dice(1, MAX_MONSTERS + 3) + (map_depth - 1) - 3;
|
|
|
+
|
|
|
+ for _i in 0 .. num_spawns {
|
|
|
+ let mut added = false;
|
|
|
+ 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 !spawn_points.contains_key(&idx) {
|
|
|
+ spawn_points.insert(idx, spawn_table.roll(&mut rng));
|
|
|
+ added = true;
|
|
|
+ } else {
|
|
|
+ tries += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- match roll {
|
|
|
- 1 => { health_potion(ecs, x, y) }
|
|
|
- 2 => { fireball_scroll(ecs, x, y) }
|
|
|
- _ => { magic_missile_scroll(ecs, x, y) }
|
|
|
+
|
|
|
+ // Actually spawn the monsters
|
|
|
+ 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),
|
|
|
+ "Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
|
|
|
+ "Staff" => staff(ecs, x, y),
|
|
|
+ "Holy Cross" => holy_cross(ecs, x, y),
|
|
|
+ "Shepards Staff" => shepards_staff(ecs, x, y),
|
|
|
+ "Tower Sheild" => tower_shield(ecs, x, y),
|
|
|
+ _ => {}
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+fn shepards_staff(ecs: &mut World, x: i32, y: i32) {
|
|
|
+ ecs.create_entity()
|
|
|
+ .with(Position{ x, y })
|
|
|
+ .with(Renderable{
|
|
|
+ glyph: rltk::to_cp437('/'),
|
|
|
+ fg: RGB::named(rltk::YELLOW),
|
|
|
+ bg: RGB::named(rltk::BLACK),
|
|
|
+ render_order: 2
|
|
|
+ })
|
|
|
+ .with(Name{ name : "Shepards Staff".to_string() })
|
|
|
+ .with(Item{})
|
|
|
+ .with(Equippable{ slot: EquipmentSlot::Melee })
|
|
|
+ .with(MeleePowerBonus{ power: 4 })
|
|
|
+ .marked::<SimpleMarker<SerializeMe>>()
|
|
|
+ .build();
|
|
|
+}
|
|
|
+
|
|
|
+fn tower_shield(ecs: &mut World, x: i32, y: i32) {
|
|
|
+ ecs.create_entity()
|
|
|
+ .with(Position{ x, y })
|
|
|
+ .with(Renderable{
|
|
|
+ glyph: rltk::to_cp437('('),
|
|
|
+ fg: RGB::named(rltk::YELLOW),
|
|
|
+ bg: RGB::named(rltk::BLACK),
|
|
|
+ render_order: 2
|
|
|
+ })
|
|
|
+ .with(Name{ name : "Tower Shield".to_string() })
|
|
|
+ .with(Item{})
|
|
|
+ .with(Equippable{ slot: EquipmentSlot::Shield })
|
|
|
+ .with(DefenseBonus{ defense: 3 })
|
|
|
+ .marked::<SimpleMarker<SerializeMe>>()
|
|
|
+ .build();
|
|
|
+}
|
|
|
+
|
|
|
+fn staff(ecs: &mut World, x: i32, y: i32) {
|
|
|
+ ecs.create_entity()
|
|
|
+ .with(Position{ x, y })
|
|
|
+ .with(Renderable{
|
|
|
+ glyph: rltk::to_cp437('|'),
|
|
|
+ fg: RGB::named(rltk::CYAN),
|
|
|
+ bg: RGB::named(rltk::BLACK),
|
|
|
+ render_order: 2
|
|
|
+ })
|
|
|
+ .with(Name{ name : "Staff".to_string() })
|
|
|
+ .with(Item{})
|
|
|
+ .marked::<SimpleMarker<SerializeMe>>()
|
|
|
+ .with(Equippable{ slot: EquipmentSlot::Melee })
|
|
|
+ .with(MeleePowerBonus{power: 2})
|
|
|
+ .build();
|
|
|
+}
|
|
|
+
|
|
|
+fn holy_cross(ecs: &mut World, x: i32, y: i32) {
|
|
|
+ ecs.create_entity()
|
|
|
+ .with(Position{ x, y })
|
|
|
+ .with(Renderable{
|
|
|
+ glyph: rltk::to_cp437('+'),
|
|
|
+ fg: RGB::named(rltk::CYAN),
|
|
|
+ bg: RGB::named(rltk::BLACK),
|
|
|
+ render_order: 2
|
|
|
+ })
|
|
|
+ .with(Name{ name : "Holy Cross".to_string() })
|
|
|
+ .with(Item{})
|
|
|
+ .marked::<SimpleMarker<SerializeMe>>()
|
|
|
+ .with(Equippable{ slot: EquipmentSlot::Shield })
|
|
|
+ .with(DefenseBonus{ defense: 1})
|
|
|
+ .build();
|
|
|
+}
|
|
|
+
|
|
|
fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
|
|
|
ecs.create_entity()
|
|
|
.with(Position{ x, y })
|
|
@@ -78,60 +195,6 @@ fn health_potion(ecs: &mut World, x: i32, y: i32) {
|
|
|
.build();
|
|
|
}
|
|
|
|
|
|
-/// 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();
|
|
|
-
|
|
|
- // 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;
|
|
|
-
|
|
|
-
|
|
|
- for _i in 0 .. num_monsters {
|
|
|
- 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 !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);
|
|
|
- added = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 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);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/// Spawns the player and returns his/her entity object.
|
|
|
pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
|
|
|
ecs
|
|
@@ -151,19 +214,6 @@ pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
|
|
|
.build()
|
|
|
}
|
|
|
|
|
|
-/// Spawns a random monster at a given location
|
|
|
-pub 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 orc(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('o'), "Orc"); }
|
|
|
fn goblin(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('g'), "Goblin"); }
|
|
|
|