spawner.rs 6.1 KB

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