spawner.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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};
  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. }
  38. /// Fills a room with stuff!
  39. #[allow(clippy::map_entry)]
  40. pub fn spawn_room(ecs: &mut World, room : &Rect, map_depth: i32) {
  41. let spawn_table = room_table(map_depth);
  42. let mut spawn_points : HashMap<usize, String> = HashMap::new();
  43. // Scope to keep the borrow checker happy
  44. {
  45. let mut rng = ecs.write_resource::<RandomNumberGenerator>();
  46. let num_spawns = rng.roll_dice(1, MAX_MONSTERS + 3) + (map_depth - 1) - 3;
  47. for _i in 0 .. num_spawns {
  48. let mut added = false;
  49. let mut tries = 0;
  50. while !added && tries < 20 {
  51. let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
  52. let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
  53. let idx = (y * MAPWIDTH) + x;
  54. if !spawn_points.contains_key(&idx) {
  55. spawn_points.insert(idx, spawn_table.roll(&mut rng));
  56. added = true;
  57. } else {
  58. tries += 1;
  59. }
  60. }
  61. }
  62. }
  63. // Actually spawn the monsters
  64. for spawn in spawn_points.iter() {
  65. let x = (*spawn.0 % MAPWIDTH) as i32;
  66. let y = (*spawn.0 / MAPWIDTH) as i32;
  67. match spawn.1.as_ref() {
  68. "Goblin" => goblin(ecs, x, y),
  69. "Orc" => orc(ecs, x, y),
  70. "Health Potion" => health_potion(ecs, x, y),
  71. "Fireball Scroll" => fireball_scroll(ecs, x, y),
  72. "Confusion Scroll" => confusion_scroll(ecs, x, y),
  73. "Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
  74. _ => {}
  75. }
  76. }
  77. }
  78. fn orc(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('o'), "Orc"); }
  79. fn goblin(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('g'), "Goblin"); }
  80. fn monster<S : ToString>(ecs: &mut World, x: i32, y: i32, glyph : u8, name : S) {
  81. ecs.create_entity()
  82. .with(Position{ x, y })
  83. .with(Renderable{
  84. glyph,
  85. fg: RGB::named(rltk::RED),
  86. bg: RGB::named(rltk::BLACK),
  87. render_order: 1
  88. })
  89. .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
  90. .with(Monster{})
  91. .with(Name{ name : name.to_string() })
  92. .with(BlocksTile{})
  93. .with(CombatStats{ max_hp: 16, hp: 16, defense: 1, power: 4 })
  94. .marked::<SimpleMarker<SerializeMe>>()
  95. .build();
  96. }
  97. fn health_potion(ecs: &mut World, x: i32, y: i32) {
  98. ecs.create_entity()
  99. .with(Position{ x, y })
  100. .with(Renderable{
  101. glyph: rltk::to_cp437('¡'),
  102. fg: RGB::named(rltk::MAGENTA),
  103. bg: RGB::named(rltk::BLACK),
  104. render_order: 2
  105. })
  106. .with(Name{ name : "Health Potion".to_string() })
  107. .with(Item{})
  108. .with(Consumable{})
  109. .with(ProvidesHealing{ heal_amount: 8 })
  110. .marked::<SimpleMarker<SerializeMe>>()
  111. .build();
  112. }
  113. fn magic_missile_scroll(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::CYAN),
  119. bg: RGB::named(rltk::BLACK),
  120. render_order: 2
  121. })
  122. .with(Name{ name : "Magic Missile Scroll".to_string() })
  123. .with(Item{})
  124. .with(Consumable{})
  125. .with(Ranged{ range: 6 })
  126. .with(InflictsDamage{ damage: 20 })
  127. .marked::<SimpleMarker<SerializeMe>>()
  128. .build();
  129. }
  130. fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
  131. ecs.create_entity()
  132. .with(Position{ x, y })
  133. .with(Renderable{
  134. glyph: rltk::to_cp437(')'),
  135. fg: RGB::named(rltk::ORANGE),
  136. bg: RGB::named(rltk::BLACK),
  137. render_order: 2
  138. })
  139. .with(Name{ name : "Fireball Scroll".to_string() })
  140. .with(Item{})
  141. .with(Consumable{})
  142. .with(Ranged{ range: 6 })
  143. .with(InflictsDamage{ damage: 20 })
  144. .with(AreaOfEffect{ radius: 3 })
  145. .marked::<SimpleMarker<SerializeMe>>()
  146. .build();
  147. }
  148. fn confusion_scroll(ecs: &mut World, x: i32, y: i32) {
  149. ecs.create_entity()
  150. .with(Position{ x, y })
  151. .with(Renderable{
  152. glyph: rltk::to_cp437(')'),
  153. fg: RGB::named(rltk::PINK),
  154. bg: RGB::named(rltk::BLACK),
  155. render_order: 2
  156. })
  157. .with(Name{ name : "Confusion Scroll".to_string() })
  158. .with(Item{})
  159. .with(Consumable{})
  160. .with(Ranged{ range: 6 })
  161. .with(Confusion{ turns: 4 })
  162. .marked::<SimpleMarker<SerializeMe>>()
  163. .build();
  164. }