diff --git a/server/src/rtsim/mod.rs b/server/src/rtsim/mod.rs
index 4dcec06cc2..b6526c647b 100644
--- a/server/src/rtsim/mod.rs
+++ b/server/src/rtsim/mod.rs
@@ -217,7 +217,7 @@ pub fn init(
                         });
                     }
 
-                    for _ in 0..site2.plazas().len() * 3 {
+                    for _ in 0..(site2.plazas().len() as f32 * 1.5) as usize {
                         rtsim.entities.insert(Entity {
                             is_loaded: false,
                             pos: site2
diff --git a/world/src/land.rs b/world/src/land.rs
index 8384251647..5a3d32611e 100644
--- a/world/src/land.rs
+++ b/world/src/land.rs
@@ -21,15 +21,19 @@ impl<'a> Land<'a> {
 
     pub fn get_gradient_approx(&self, wpos: Vec2<i32>) -> f32 {
         self.sim
-            .and_then(|sim| {
-                sim.get_gradient_approx(
-                    wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e.div_euclid(sz as i32)),
-                )
-            })
+            .and_then(|sim| sim.get_gradient_approx(self.wpos_chunk_pos(wpos)))
             .unwrap_or(0.0)
     }
 
-    pub fn get_chunk_at(&self, wpos: Vec2<i32>) -> Option<&sim::SimChunk> {
+    pub fn wpos_chunk_pos(&self, wpos: Vec2<i32>) -> Vec2<i32> {
+        wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e.div_euclid(sz as i32))
+    }
+
+    pub fn get_chunk(&self, chunk_pos: Vec2<i32>) -> Option<&sim::SimChunk> {
+        self.sim.and_then(|sim| sim.get(chunk_pos))
+    }
+
+    pub fn get_chunk_wpos(&self, wpos: Vec2<i32>) -> Option<&sim::SimChunk> {
         self.sim.and_then(|sim| sim.get_wpos(wpos))
     }
 }
diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs
index d9ee89ce3a..d7e8bbfa4e 100644
--- a/world/src/site2/mod.rs
+++ b/world/src/site2/mod.rs
@@ -301,7 +301,10 @@ impl Site {
         Spiral2d::new()
             .take((SEARCH_RADIUS * 2 + 1).pow(2) as usize)
             .for_each(|tile| {
-                if let Some(kind) = wpos_is_hazard(land, self.tile_wpos(tile)) {
+                if let Some(kind) = Spiral2d::new()
+                    .take(9)
+                    .find_map(|rpos| wpos_is_hazard(land, self.tile_center_wpos(tile) + rpos))
+                {
                     for &rpos in &SQUARE_4 {
                         // `get_mut` doesn't increase generation bounds
                         self.tiles
@@ -935,7 +938,7 @@ pub fn test_site() -> Site { Site::generate_city(&Land::empty(), &mut thread_rng
 
 fn wpos_is_hazard(land: &Land, wpos: Vec2<i32>) -> Option<HazardKind> {
     if land
-        .get_chunk_at(wpos)
+        .get_chunk_wpos(wpos)
         .map_or(true, |c| c.river.near_water())
     {
         Some(HazardKind::Water)
diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs
index 1097c52ef9..06d7d3429a 100644
--- a/world/src/site2/plot/dungeon.rs
+++ b/world/src/site2/plot/dungeon.rs
@@ -1418,7 +1418,7 @@ impl SiteStructure for Dungeon {
         }
 
         let biome = land
-            .get_chunk_at(self.origin)
+            .get_chunk_wpos(self.origin)
             .map_or(BiomeKind::Void, |c| c.get_biome());
         let entrances = match biome {
             BiomeKind::Jungle => *JUNGLE,