spawner.rs 7.9 KB

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