voronoi.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. use super::{MapBuilder, Map,
  2. TileType, Position, spawner, SHOW_MAPGEN_VISUALIZER,
  3. remove_unreachable_areas_returning_most_distant, generate_voronoi_spawn_regions};
  4. use rltk::RandomNumberGenerator;
  5. use specs::prelude::*;
  6. use std::collections::HashMap;
  7. #[derive(PartialEq, Copy, Clone)]
  8. pub enum DistanceAlgorithm { Pythagoras, Manhattan, Chebyshev }
  9. pub struct VoronoiCellBuilder {
  10. map : Map,
  11. starting_position : Position,
  12. depth: i32,
  13. history: Vec<Map>,
  14. noise_areas : HashMap<i32, Vec<usize>>,
  15. n_seeds: usize,
  16. distance_algorithm: DistanceAlgorithm
  17. }
  18. impl MapBuilder for VoronoiCellBuilder {
  19. fn get_map(&self) -> Map {
  20. self.map.clone()
  21. }
  22. fn get_starting_position(&self) -> Position {
  23. self.starting_position.clone()
  24. }
  25. fn get_snapshot_history(&self) -> Vec<Map> {
  26. self.history.clone()
  27. }
  28. fn build_map(&mut self) {
  29. self.build();
  30. }
  31. fn spawn_entities(&mut self, ecs : &mut World) {
  32. for area in self.noise_areas.iter() {
  33. spawner::spawn_region(ecs, area.1, self.depth);
  34. }
  35. }
  36. fn take_snapshot(&mut self) {
  37. if SHOW_MAPGEN_VISUALIZER {
  38. let mut snapshot = self.map.clone();
  39. for v in snapshot.revealed_tiles.iter_mut() {
  40. *v = true;
  41. }
  42. self.history.push(snapshot);
  43. }
  44. }
  45. }
  46. impl VoronoiCellBuilder {
  47. pub fn new(new_depth : i32) -> VoronoiCellBuilder {
  48. VoronoiCellBuilder{
  49. map : Map::new(new_depth),
  50. starting_position : Position{ x: 0, y : 0 },
  51. depth : new_depth,
  52. history: Vec::new(),
  53. noise_areas : HashMap::new(),
  54. n_seeds: 64,
  55. distance_algorithm: DistanceAlgorithm::Pythagoras
  56. }
  57. }
  58. pub fn pythagoras(new_depth : i32) -> VoronoiCellBuilder {
  59. VoronoiCellBuilder{
  60. map : Map::new(new_depth),
  61. starting_position : Position{ x: 0, y : 0 },
  62. depth : new_depth,
  63. history: Vec::new(),
  64. noise_areas : HashMap::new(),
  65. n_seeds: 64,
  66. distance_algorithm: DistanceAlgorithm::Pythagoras
  67. }
  68. }
  69. pub fn manhattan(new_depth : i32) -> VoronoiCellBuilder {
  70. VoronoiCellBuilder{
  71. map : Map::new(new_depth),
  72. starting_position : Position{ x: 0, y : 0 },
  73. depth : new_depth,
  74. history: Vec::new(),
  75. noise_areas : HashMap::new(),
  76. n_seeds: 64,
  77. distance_algorithm: DistanceAlgorithm::Manhattan
  78. }
  79. }
  80. #[allow(clippy::map_entry)]
  81. fn build(&mut self) {
  82. let mut rng = RandomNumberGenerator::new();
  83. // Make a Voronoi diagram. We'll do this the hard way to learn about the technique!
  84. let mut voronoi_seeds : Vec<(usize, rltk::Point)> = Vec::new();
  85. while voronoi_seeds.len() < self.n_seeds {
  86. let vx = rng.roll_dice(1, self.map.width-1);
  87. let vy = rng.roll_dice(1, self.map.height-1);
  88. let vidx = self.map.xy_idx(vx, vy);
  89. let candidate = (vidx, rltk::Point::new(vx, vy));
  90. if !voronoi_seeds.contains(&candidate) {
  91. voronoi_seeds.push(candidate);
  92. }
  93. }
  94. let mut voroni_distance = vec![(0, 0.0f32) ; self.n_seeds];
  95. let mut voronoi_membership : Vec<i32> = vec![0 ; self.map.width as usize * self.map.height as usize];
  96. for (i, vid) in voronoi_membership.iter_mut().enumerate() {
  97. let x = i as i32 % self.map.width;
  98. let y = i as i32 / self.map.width;
  99. for (seed, pos) in voronoi_seeds.iter().enumerate() {
  100. let distance;
  101. match self.distance_algorithm {
  102. DistanceAlgorithm::Pythagoras => {
  103. distance = rltk::DistanceAlg::PythagorasSquared.distance2d(
  104. rltk::Point::new(x, y),
  105. pos.1
  106. );
  107. }
  108. DistanceAlgorithm::Manhattan => {
  109. distance = rltk::DistanceAlg::Manhattan.distance2d(
  110. rltk::Point::new(x, y),
  111. pos.1
  112. );
  113. }
  114. DistanceAlgorithm::Chebyshev => {
  115. distance = rltk::DistanceAlg::Chebyshev.distance2d(
  116. rltk::Point::new(x, y),
  117. pos.1
  118. );
  119. }
  120. }
  121. voroni_distance[seed] = (seed, distance);
  122. }
  123. voroni_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap());
  124. *vid = voroni_distance[0].0 as i32;
  125. }
  126. for y in 1..self.map.height-1 {
  127. for x in 1..self.map.width-1 {
  128. let mut neighbors = 0;
  129. let my_idx = self.map.xy_idx(x, y);
  130. let my_seed = voronoi_membership[my_idx];
  131. if voronoi_membership[self.map.xy_idx(x-1, y)] != my_seed { neighbors += 1; }
  132. if voronoi_membership[self.map.xy_idx(x+1, y)] != my_seed { neighbors += 1; }
  133. if voronoi_membership[self.map.xy_idx(x, y-1)] != my_seed { neighbors += 1; }
  134. if voronoi_membership[self.map.xy_idx(x, y+1)] != my_seed { neighbors += 1; }
  135. if neighbors < 2 {
  136. self.map.tiles[my_idx] = TileType::Floor;
  137. }
  138. }
  139. self.take_snapshot();
  140. }
  141. // Find a starting point; start at the middle and walk left until we find an open tile
  142. self.starting_position = Position{ x: self.map.width / 2, y : self.map.height / 2 };
  143. let mut start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
  144. while self.map.tiles[start_idx] != TileType::Floor {
  145. self.starting_position.x -= 1;
  146. start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
  147. }
  148. self.take_snapshot();
  149. // Find all tiles we can reach from the starting point
  150. let exit_tile = remove_unreachable_areas_returning_most_distant(&mut self.map, start_idx);
  151. self.take_snapshot();
  152. // Place the stairs
  153. self.map.tiles[exit_tile] = TileType::DownStairs;
  154. self.take_snapshot();
  155. // Now we build a noise map for use in spawning entities later
  156. self.noise_areas = generate_voronoi_spawn_regions(&self.map, &mut rng);
  157. }
  158. }