diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 213908588e..47b03e2045 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -112,7 +112,7 @@ impl Civs { 29..=31 => (SiteKind::Tree, 4), _ => (SiteKind::Dungeon, 0), }; - let loc = find_site_loc(&mut ctx, None, size)?; + let loc = find_site_loc(&mut ctx, None, size, kind)?; Some(this.establish_site(&mut ctx.reseed(), loc, |place| Site { kind, center: loc, @@ -437,7 +437,7 @@ impl Civs { fn birth_civ(&mut self, ctx: &mut GenCtx) -> Option> { let site = attempt(5, || { - let loc = find_site_loc(ctx, None, 1)?; + let loc = find_site_loc(ctx, None, 1, SiteKind::Settlement)?; Some(self.establish_site(ctx, loc, |place| Site { kind: SiteKind::Settlement, site_tmp: None, @@ -911,14 +911,26 @@ fn loc_suitable_for_walking(sim: &WorldSim, loc: Vec2) -> bool { /// Return true if a site could be constructed between a location and a chunk /// next to it is permitted (TODO: by whom?) -fn site_in_dir(sim: &WorldSim, a: Vec2, dir: Vec2) -> bool { - loc_suitable_for_site(sim, a) && loc_suitable_for_site(sim, a + dir) +fn site_in_dir(sim: &WorldSim, a: Vec2, dir: Vec2, site_kind: SiteKind) -> bool { + loc_suitable_for_site(sim, a, site_kind) && loc_suitable_for_site(sim, a + dir, site_kind) } /// Return true if a position is suitable for site construction (TODO: /// criteria?) -fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2) -> bool { - if let Some(chunk) = sim.get(loc) { +fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2, site_kind: SiteKind) -> bool { + fn check_chunk_occupation(sim: &WorldSim, loc: Vec2, radius: i32) -> bool { + for x in (-radius)..radius { + for y in (-radius)..radius { + let check_loc = + loc + Vec2::new(x, y).map2(TerrainChunkSize::RECT_SIZE, |e, sz| e * sz as i32); + if sim.get(check_loc).map_or(true, |c| !c.sites.is_empty()) { + return false; + } + } + } + true + } + (if let Some(chunk) = sim.get(loc) { !chunk.river.is_ocean() && !chunk.river.is_lake() && !chunk.river.is_river() @@ -928,7 +940,7 @@ fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2) -> bool { .unwrap_or(false) } else { false - } + }) && check_chunk_occupation(sim, loc, site_kind.exclusion_radius()) } /// Attempt to search for a location that's suitable for site construction @@ -936,6 +948,7 @@ fn find_site_loc( ctx: &mut GenCtx, near: Option<(Vec2, f32)>, size: i32, + site_kind: SiteKind, ) -> Option> { const MAX_ATTEMPTS: usize = 100; let mut loc = None; @@ -957,7 +970,7 @@ fn find_site_loc( }); for offset in Spiral2d::new().take((size * 2 + 1).pow(2) as usize) { - if loc_suitable_for_site(ctx.sim, test_loc + offset) { + if loc_suitable_for_site(ctx.sim, test_loc + offset, site_kind) { return Some(test_loc); } } @@ -1015,7 +1028,7 @@ impl fmt::Display for Site { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum SiteKind { Settlement, Dungeon, @@ -1024,6 +1037,16 @@ pub enum SiteKind { Tree, } +impl SiteKind { + pub fn exclusion_radius(&self) -> i32 { + // FIXME: Provide specific values for each individual SiteKind + match self { + SiteKind::Dungeon => 4, + _ => 2, // This is just an arbitrary value + } + } +} + impl Site { pub fn is_dungeon(&self) -> bool { matches!(self.kind, SiteKind::Dungeon) }