spawner.rs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. extern crate rltk;
  2. use rltk::{ RGB, RandomNumberGenerator };
  3. extern crate specs;
  4. use specs::prelude::*;
  5. use super::{CombatStats, Player, Renderable, Name, Position, Viewshed, Monster, BlocksTile, Rect, Item,
  6. Consumable, Ranged, ProvidesHealing, map::MAPWIDTH, InflictsDamage, AreaOfEffect, Confusion, SerializeMe,
  7. random_table::RandomTable, EquipmentSlot, Equippable, MeleePowerBonus, DefenseBonus };
  8. use crate::specs::saveload::{MarkedBuilder, SimpleMarker};
  9. use std::collections::HashMap;
  10. /// Spawns the player and returns his/her entity object.
  11. pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
  12. ecs
  13. .create_entity()
  14. .with(Position { x: player_x, y: player_y })
  15. .with(Renderable {
  16. glyph: rltk::to_cp437('@'),
  17. fg: RGB::named(rltk::YELLOW),
  18. bg: RGB::named(rltk::BLACK),
  19. render_order: 0
  20. })
  21. .with(Player{})
  22. .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
  23. .with(Name{name: "Player".to_string() })
  24. .with(CombatStats{ max_hp: 30, hp: 30, defense: 2, power: 5 })
  25. .marked::<SimpleMarker<SerializeMe>>()
  26. .build()
  27. }
  28. const MAX_MONSTERS : i32 = 4;
  29. fn room_table(map_depth: i32) -> RandomTable {
  30. RandomTable::new()
  31. .add("Goblin", 10)
  32. .add("Orc", 1 + map_depth)
  33. .add("Health Potion", 7)
  34. .add("Fireball Scroll", 2 + map_depth)
  35. .add("Confusion Scroll", 2 + map_depth)
  36. .add("Magic Missile Scroll", 4)
  37. .add("Dagger", 3)
  38. .add("Shield", 3)
  39. }
  40. /// Fills a room with stuff!
  41. #[allow(clippy::map_entry)]
  42. pub fn spawn_room(ecs: &mut World, room : &Rect, map_depth: i32) {
  43. let spawn_table = room_table(map_depth);
  44. let mut spawn_points : HashMap<usize, String> = HashMap::new();
  45. // Scope to keep the borrow checker happy
  46. {
  47. let mut rng = ecs.write_resource::<RandomNumberGenerator>();
  48. let num_spawns = rng.roll_dice(1, MAX_MONSTERS + 3) + (map_depth - 1) - 3;
  49. for _i in 0 .. num_spawns {
  50. let mut added = false;
  51. let mut tries = 0;
  52. while !added && tries < 20 {
  53. let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
  54. let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
  55. let idx = (y * MAPWIDTH) + x;
  56. if !spawn_points.contains_key(&idx) {
  57. spawn_points.insert(idx, spawn_table.roll(&mut rng));
  58. added = true;
  59. } else {
  60. tries += 1;
  61. }
  62. }
  63. }
  64. }
  65. // Actually spawn the monsters
  66. for spawn in spawn_points.iter() {
  67. let x = (*spawn.0 % MAPWIDTH) as i32;
  68. let y = (*spawn.0 / MAPWIDTH) as i32;
  69. match spawn.1.as_ref() {
  70. "Goblin" => goblin(ecs, x, y),
  71. "Orc" => orc(ecs, x, y),
  72. "Health Potion" => health_potion(ecs, x, y),
  73. "Fireball Scroll" => fireball_scroll(ecs, x, y),
  74. "Confusion Scroll" => confusion_scroll(ecs, x, y),
  75. "Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
  76. "Dagger" => dagger(ecs, x, y),
  77. "Shield" => shield(ecs, x, y),
  78. _ => {}
  79. }
  80. }
  81. }
  82. fn orc(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('o'), "Orc"); }
  83. fn goblin(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('g'), "Goblin"); }
  84. fn monster<S : ToString>(ecs: &mut World, x: i32, y: i32, glyph : u8, name : S) {
  85. ecs.create_entity()
  86. .with(Position{ x, y })
  87. .with(Renderable{
  88. glyph,
  89. fg: RGB::named(rltk::RED),
  90. bg: RGB::named(rltk::BLACK),
  91. render_order: 1
  92. })
  93. .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
  94. .with(Monster{})
  95. .with(Name{ name : name.to_string() })
  96. .with(BlocksTile{})
  97. .with(CombatStats{ max_hp: 16, hp: 16, defense: 1, power: 4 })
  98. .marked::<SimpleMarker<SerializeMe>>()
  99. .build();
  100. }
  101. fn health_potion(ecs: &mut World, x: i32, y: i32) {
  102. ecs.create_entity()
  103. .with(Position{ x, y })
  104. .with(Renderable{
  105. glyph: rltk::to_cp437('¡'),
  106. fg: RGB::named(rltk::MAGENTA),
  107. bg: RGB::named(rltk::BLACK),
  108. render_order: 2
  109. })
  110. .with(Name{ name : "Health Potion".to_string() })
  111. .with(Item{})
  112. .with(Consumable{})
  113. .with(ProvidesHealing{ heal_amount: 8 })
  114. .marked::<SimpleMarker<SerializeMe>>()
  115. .build();
  116. }
  117. fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
  118. ecs.create_entity()
  119. .with(Position{ x, y })
  120. .with(Renderable{
  121. glyph: rltk::to_cp437(')'),
  122. fg: RGB::named(rltk::CYAN),
  123. bg: RGB::named(rltk::BLACK),
  124. render_order: 2
  125. })
  126. .with(Name{ name : "Magic Missile Scroll".to_string() })
  127. .with(Item{})
  128. .with(Consumable{})
  129. .with(Ranged{ range: 6 })
  130. .with(InflictsDamage{ damage: 20 })
  131. .marked::<SimpleMarker<SerializeMe>>()
  132. .build();
  133. }
  134. fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
  135. ecs.create_entity()
  136. .with(Position{ x, y })
  137. .with(Renderable{
  138. glyph: rltk::to_cp437(')'),
  139. fg: RGB::named(rltk::ORANGE),
  140. bg: RGB::named(rltk::BLACK),
  141. render_order: 2
  142. })
  143. .with(Name{ name : "Fireball Scroll".to_string() })
  144. .with(Item{})
  145. .with(Consumable{})
  146. .with(Ranged{ range: 6 })
  147. .with(InflictsDamage{ damage: 20 })
  148. .with(AreaOfEffect{ radius: 3 })
  149. .marked::<SimpleMarker<SerializeMe>>()
  150. .build();
  151. }
  152. fn confusion_scroll(ecs: &mut World, x: i32, y: i32) {
  153. ecs.create_entity()
  154. .with(Position{ x, y })
  155. .with(Renderable{
  156. glyph: rltk::to_cp437(')'),
  157. fg: RGB::named(rltk::PINK),
  158. bg: RGB::named(rltk::BLACK),
  159. render_order: 2
  160. })
  161. .with(Name{ name : "Confusion Scroll".to_string() })
  162. .with(Item{})
  163. .with(Consumable{})
  164. .with(Ranged{ range: 6 })
  165. .with(Confusion{ turns: 4 })
  166. .marked::<SimpleMarker<SerializeMe>>()
  167. .build();
  168. }
  169. fn dagger(ecs: &mut World, x: i32, y: i32) {
  170. ecs.create_entity()
  171. .with(Position{ x, y })
  172. .with(Renderable{
  173. glyph: rltk::to_cp437('/'),
  174. fg: RGB::named(rltk::CYAN),
  175. bg: RGB::named(rltk::BLACK),
  176. render_order: 2
  177. })
  178. .with(Name{ name : "Dagger".to_string() })
  179. .with(Item{})
  180. .with(Equippable{ slot: EquipmentSlot::Melee })
  181. .with(MeleePowerBonus{ power: 2 })
  182. .marked::<SimpleMarker<SerializeMe>>()
  183. .build();
  184. }
  185. fn shield(ecs: &mut World, x: i32, y: i32) {
  186. ecs.create_entity()
  187. .with(Position{ x, y })
  188. .with(Renderable{
  189. glyph: rltk::to_cp437('('),
  190. fg: RGB::named(rltk::CYAN),
  191. bg: RGB::named(rltk::BLACK),
  192. render_order: 2
  193. })
  194. .with(Name{ name : "Shield".to_string() })
  195. .with(Item{})
  196. .with(Equippable{ slot: EquipmentSlot::Shield })
  197. .with(DefenseBonus{ defense: 1 })
  198. .marked::<SimpleMarker<SerializeMe>>()
  199. .build();
  200. }