spawner.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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, HungerClock,
  8. HungerState, ProvidesFood, MagicMapper, Hidden, EntryTrigger, SingleActivation };
  9. use crate::specs::saveload::{MarkedBuilder, SimpleMarker};
  10. use std::collections::HashMap;
  11. /// Spawns the player and returns his/her entity object.
  12. pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
  13. ecs
  14. .create_entity()
  15. .with(Position { x: player_x, y: player_y })
  16. .with(Renderable {
  17. glyph: rltk::to_cp437('@'),
  18. fg: RGB::named(rltk::YELLOW),
  19. bg: RGB::named(rltk::BLACK),
  20. render_order: 0
  21. })
  22. .with(Player{})
  23. .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
  24. .with(Name{name: "Player".to_string() })
  25. .with(CombatStats{ max_hp: 30, hp: 30, defense: 2, power: 5 })
  26. .with(HungerClock{ state: HungerState::WellFed, duration: 20 })
  27. .marked::<SimpleMarker<SerializeMe>>()
  28. .build()
  29. }
  30. const MAX_MONSTERS : i32 = 4;
  31. fn room_table(map_depth: i32) -> RandomTable {
  32. RandomTable::new()
  33. .add("Goblin", 10)
  34. .add("Orc", 1 + map_depth)
  35. .add("Health Potion", 7)
  36. .add("Fireball Scroll", 2 + map_depth)
  37. .add("Confusion Scroll", 2 + map_depth)
  38. .add("Magic Missile Scroll", 4)
  39. .add("Dagger", 3)
  40. .add("Shield", 3)
  41. .add("Longsword", map_depth - 1)
  42. .add("Tower Shield", map_depth - 1)
  43. .add("Rations", 10)
  44. .add("Magic Mapping Scroll", 2)
  45. .add("Bear Trap", 5)
  46. }
  47. /// Fills a room with stuff!
  48. #[allow(clippy::map_entry)]
  49. pub fn spawn_room(ecs: &mut World, room : &Rect, map_depth: i32) {
  50. let spawn_table = room_table(map_depth);
  51. let mut spawn_points : HashMap<usize, String> = HashMap::new();
  52. // Scope to keep the borrow checker happy
  53. {
  54. let mut rng = ecs.write_resource::<RandomNumberGenerator>();
  55. let num_spawns = rng.roll_dice(1, MAX_MONSTERS + 3) + (map_depth - 1) - 3;
  56. for _i in 0 .. num_spawns {
  57. let mut added = false;
  58. let mut tries = 0;
  59. while !added && tries < 20 {
  60. let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
  61. let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
  62. let idx = (y * MAPWIDTH) + x;
  63. if !spawn_points.contains_key(&idx) {
  64. spawn_points.insert(idx, spawn_table.roll(&mut rng));
  65. added = true;
  66. } else {
  67. tries += 1;
  68. }
  69. }
  70. }
  71. }
  72. // Actually spawn the monsters
  73. for spawn in spawn_points.iter() {
  74. let x = (*spawn.0 % MAPWIDTH) as i32;
  75. let y = (*spawn.0 / MAPWIDTH) as i32;
  76. match spawn.1.as_ref() {
  77. "Goblin" => goblin(ecs, x, y),
  78. "Orc" => orc(ecs, x, y),
  79. "Health Potion" => health_potion(ecs, x, y),
  80. "Fireball Scroll" => fireball_scroll(ecs, x, y),
  81. "Confusion Scroll" => confusion_scroll(ecs, x, y),
  82. "Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
  83. "Dagger" => dagger(ecs, x, y),
  84. "Shield" => shield(ecs, x, y),
  85. "Longsword" => longsword(ecs, x, y),
  86. "Tower Shield" => tower_shield(ecs, x, y),
  87. "Rations" => rations(ecs, x, y),
  88. "Magic Mapping Scroll" => magic_mapping_scroll(ecs, x, y),
  89. "Bear Trap" => bear_trap(ecs, x, y),
  90. _ => {}
  91. }
  92. }
  93. }
  94. fn orc(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('o'), "Orc"); }
  95. fn goblin(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('g'), "Goblin"); }
  96. fn monster<S : ToString>(ecs: &mut World, x: i32, y: i32, glyph : u8, name : S) {
  97. ecs.create_entity()
  98. .with(Position{ x, y })
  99. .with(Renderable{
  100. glyph,
  101. fg: RGB::named(rltk::RED),
  102. bg: RGB::named(rltk::BLACK),
  103. render_order: 1
  104. })
  105. .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
  106. .with(Monster{})
  107. .with(Name{ name : name.to_string() })
  108. .with(BlocksTile{})
  109. .with(CombatStats{ max_hp: 16, hp: 16, defense: 1, power: 4 })
  110. .marked::<SimpleMarker<SerializeMe>>()
  111. .build();
  112. }
  113. fn health_potion(ecs: &mut World, x: i32, y: i32) {
  114. ecs.create_entity()
  115. .with(Position{ x, y })
  116. .with(Renderable{
  117. glyph: rltk::to_cp437('¡'),
  118. fg: RGB::named(rltk::MAGENTA),
  119. bg: RGB::named(rltk::BLACK),
  120. render_order: 2
  121. })
  122. .with(Name{ name : "Health Potion".to_string() })
  123. .with(Item{})
  124. .with(Consumable{})
  125. .with(ProvidesHealing{ heal_amount: 8 })
  126. .marked::<SimpleMarker<SerializeMe>>()
  127. .build();
  128. }
  129. fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
  130. ecs.create_entity()
  131. .with(Position{ x, y })
  132. .with(Renderable{
  133. glyph: rltk::to_cp437(')'),
  134. fg: RGB::named(rltk::CYAN),
  135. bg: RGB::named(rltk::BLACK),
  136. render_order: 2
  137. })
  138. .with(Name{ name : "Magic Missile Scroll".to_string() })
  139. .with(Item{})
  140. .with(Consumable{})
  141. .with(Ranged{ range: 6 })
  142. .with(InflictsDamage{ damage: 20 })
  143. .marked::<SimpleMarker<SerializeMe>>()
  144. .build();
  145. }
  146. fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
  147. ecs.create_entity()
  148. .with(Position{ x, y })
  149. .with(Renderable{
  150. glyph: rltk::to_cp437(')'),
  151. fg: RGB::named(rltk::ORANGE),
  152. bg: RGB::named(rltk::BLACK),
  153. render_order: 2
  154. })
  155. .with(Name{ name : "Fireball Scroll".to_string() })
  156. .with(Item{})
  157. .with(Consumable{})
  158. .with(Ranged{ range: 6 })
  159. .with(InflictsDamage{ damage: 20 })
  160. .with(AreaOfEffect{ radius: 3 })
  161. .marked::<SimpleMarker<SerializeMe>>()
  162. .build();
  163. }
  164. fn confusion_scroll(ecs: &mut World, x: i32, y: i32) {
  165. ecs.create_entity()
  166. .with(Position{ x, y })
  167. .with(Renderable{
  168. glyph: rltk::to_cp437(')'),
  169. fg: RGB::named(rltk::PINK),
  170. bg: RGB::named(rltk::BLACK),
  171. render_order: 2
  172. })
  173. .with(Name{ name : "Confusion Scroll".to_string() })
  174. .with(Item{})
  175. .with(Consumable{})
  176. .with(Ranged{ range: 6 })
  177. .with(Confusion{ turns: 4 })
  178. .marked::<SimpleMarker<SerializeMe>>()
  179. .build();
  180. }
  181. fn dagger(ecs: &mut World, x: i32, y: i32) {
  182. ecs.create_entity()
  183. .with(Position{ x, y })
  184. .with(Renderable{
  185. glyph: rltk::to_cp437('/'),
  186. fg: RGB::named(rltk::CYAN),
  187. bg: RGB::named(rltk::BLACK),
  188. render_order: 2
  189. })
  190. .with(Name{ name : "Dagger".to_string() })
  191. .with(Item{})
  192. .with(Equippable{ slot: EquipmentSlot::Melee })
  193. .with(MeleePowerBonus{ power: 2 })
  194. .marked::<SimpleMarker<SerializeMe>>()
  195. .build();
  196. }
  197. fn shield(ecs: &mut World, x: i32, y: i32) {
  198. ecs.create_entity()
  199. .with(Position{ x, y })
  200. .with(Renderable{
  201. glyph: rltk::to_cp437('('),
  202. fg: RGB::named(rltk::CYAN),
  203. bg: RGB::named(rltk::BLACK),
  204. render_order: 2
  205. })
  206. .with(Name{ name : "Shield".to_string() })
  207. .with(Item{})
  208. .with(Equippable{ slot: EquipmentSlot::Shield })
  209. .with(DefenseBonus{ defense: 1 })
  210. .marked::<SimpleMarker<SerializeMe>>()
  211. .build();
  212. }
  213. fn longsword(ecs: &mut World, x: i32, y: i32) {
  214. ecs.create_entity()
  215. .with(Position{ x, y })
  216. .with(Renderable{
  217. glyph: rltk::to_cp437('/'),
  218. fg: RGB::named(rltk::YELLOW),
  219. bg: RGB::named(rltk::BLACK),
  220. render_order: 2
  221. })
  222. .with(Name{ name : "Longsword".to_string() })
  223. .with(Item{})
  224. .with(Equippable{ slot: EquipmentSlot::Melee })
  225. .with(MeleePowerBonus{ power: 4 })
  226. .marked::<SimpleMarker<SerializeMe>>()
  227. .build();
  228. }
  229. fn tower_shield(ecs: &mut World, x: i32, y: i32) {
  230. ecs.create_entity()
  231. .with(Position{ x, y })
  232. .with(Renderable{
  233. glyph: rltk::to_cp437('('),
  234. fg: RGB::named(rltk::YELLOW),
  235. bg: RGB::named(rltk::BLACK),
  236. render_order: 2
  237. })
  238. .with(Name{ name : "Tower Shield".to_string() })
  239. .with(Item{})
  240. .with(Equippable{ slot: EquipmentSlot::Shield })
  241. .with(DefenseBonus{ defense: 3 })
  242. .marked::<SimpleMarker<SerializeMe>>()
  243. .build();
  244. }
  245. fn rations(ecs: &mut World, x: i32, y: i32) {
  246. ecs.create_entity()
  247. .with(Position{ x, y })
  248. .with(Renderable{
  249. glyph: rltk::to_cp437('%'),
  250. fg: RGB::named(rltk::GREEN),
  251. bg: RGB::named(rltk::BLACK),
  252. render_order: 2
  253. })
  254. .with(Name{ name : "Rations".to_string() })
  255. .with(Item{})
  256. .with(ProvidesFood{})
  257. .with(Consumable{})
  258. .marked::<SimpleMarker<SerializeMe>>()
  259. .build();
  260. }
  261. fn magic_mapping_scroll(ecs: &mut World, x: i32, y: i32) {
  262. ecs.create_entity()
  263. .with(Position{ x, y })
  264. .with(Renderable{
  265. glyph: rltk::to_cp437(')'),
  266. fg: RGB::named(rltk::CYAN3),
  267. bg: RGB::named(rltk::BLACK),
  268. render_order: 2
  269. })
  270. .with(Name{ name : "Scroll of Magic Mapping".to_string() })
  271. .with(Item{})
  272. .with(MagicMapper{})
  273. .with(Consumable{})
  274. .marked::<SimpleMarker<SerializeMe>>()
  275. .build();
  276. }
  277. fn bear_trap(ecs: &mut World, x: i32, y: i32) {
  278. ecs.create_entity()
  279. .with(Position{ x, y })
  280. .with(Renderable{
  281. glyph: rltk::to_cp437('^'),
  282. fg: RGB::named(rltk::RED),
  283. bg: RGB::named(rltk::BLACK),
  284. render_order: 2
  285. })
  286. .with(Name{ name : "Bear Trap".to_string() })
  287. .with(Hidden{})
  288. .with(EntryTrigger{})
  289. .with(InflictsDamage{ damage: 6 })
  290. .with(SingleActivation{})
  291. .marked::<SimpleMarker<SerializeMe>>()
  292. .build();
  293. }