spawner.rs 6.6 KB

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