Began work on citadel

This commit is contained in:
Joshua Barretto 2022-03-02 15:37:28 +00:00 committed by IsseW
parent dbc7ac9710
commit 0b5d043d8e
7 changed files with 229 additions and 7 deletions

View File

@ -77,9 +77,9 @@ impl<T> Grid<T> {
&self, &self,
pos: Vec2<i32>, pos: Vec2<i32>,
size: Vec2<i32>, size: Vec2<i32>,
) -> impl Iterator<Item = Option<(Vec2<i32>, &T)>> + '_ { ) -> impl Iterator<Item = (Vec2<i32>, &T)> + '_ {
(0..size.x).flat_map(move |x| { (0..size.x).flat_map(move |x| {
(0..size.y).map(move |y| { (0..size.y).flat_map(move |y| {
Some(( Some((
pos + Vec2::new(x, y), pos + Vec2::new(x, y),
&self.cells[self.idx(pos + Vec2::new(x, y))?], &self.cells[self.idx(pos + Vec2::new(x, y))?],

View File

@ -129,6 +129,7 @@ impl Civs {
} }
}, },
32..=37 => (SiteKind::Gnarling, (&gnarling_enemies, 40)), 32..=37 => (SiteKind::Gnarling, (&gnarling_enemies, 40)),
// 32..=37 => (SiteKind::Citadel, 5, (&castle_enemies, 20)),
38..=43 => (SiteKind::ChapelSite, (&chapel_site_enemies, 40)), 38..=43 => (SiteKind::ChapelSite, (&chapel_site_enemies, 40)),
_ => (SiteKind::Dungeon, (&dungeon_enemies, 40)), _ => (SiteKind::Dungeon, (&dungeon_enemies, 40)),
}; };
@ -178,7 +179,7 @@ impl Civs {
let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32); let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32);
let (radius, flatten_radius) = match &site.kind { let (radius, flatten_radius) = match &site.kind {
SiteKind::Settlement => (32i32, 10.0), SiteKind::Settlement => (32i32, 10.0f32),
SiteKind::Dungeon => (8i32, 3.0), SiteKind::Dungeon => (8i32, 3.0),
SiteKind::Castle => (16i32, 5.0), SiteKind::Castle => (16i32, 5.0),
SiteKind::Refactor => (32i32, 10.0), SiteKind::Refactor => (32i32, 10.0),
@ -188,6 +189,7 @@ impl Civs {
SiteKind::Tree => (12i32, 8.0), SiteKind::Tree => (12i32, 8.0),
SiteKind::GiantTree => (12i32, 8.0), SiteKind::GiantTree => (12i32, 8.0),
SiteKind::Gnarling => (16i32, 10.0), SiteKind::Gnarling => (16i32, 10.0),
SiteKind::Citadel => (16i32, 0.0),
}; };
let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind { let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind {
@ -207,7 +209,8 @@ impl Civs {
}; // Raise the town centre up a little }; // Raise the town centre up a little
let pos = site.center + offs; let pos = site.center + offs;
let factor = ((1.0 let factor = ((1.0
- (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) - (site.center - pos).map(|e| e as f32).magnitude()
/ flatten_radius.max(0.01))
* 1.25) * 1.25)
.min(1.0); .min(1.0);
let rng = &mut ctx.rng; let rng = &mut ctx.rng;
@ -288,6 +291,11 @@ impl Civs {
SiteKind::ChapelSite => WorldSite::chapel_site( SiteKind::ChapelSite => WorldSite::chapel_site(
site2::Site::generate_chapel_site(&Land::from_sim(ctx.sim), &mut rng, wpos), site2::Site::generate_chapel_site(&Land::from_sim(ctx.sim), &mut rng, wpos),
), ),
SiteKind::Citadel => WorldSite::gnarling(site2::Site::generate_citadel(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
}); });
sim_site.site_tmp = Some(site); sim_site.site_tmp = Some(site);
let site_ref = &index.sites[site]; let site_ref = &index.sites[site];
@ -1245,6 +1253,7 @@ pub enum SiteKind {
Tree, Tree,
GiantTree, GiantTree,
Gnarling, Gnarling,
Citadel,
} }
impl SiteKind { impl SiteKind {
@ -1378,6 +1387,7 @@ impl SiteKind {
&& chunk.tree_density > 0.4 && chunk.tree_density > 0.4
&& (-0.3..0.4).contains(&chunk.temp) && (-0.3..0.4).contains(&chunk.temp)
}, },
SiteKind::Citadel => true,
SiteKind::CliffTown => { SiteKind::CliffTown => {
(-0.6..0.4).contains(&chunk.temp) (-0.6..0.4).contains(&chunk.temp)
&& chunk.near_cliffs() && chunk.near_cliffs()

View File

@ -7,7 +7,12 @@
)] )]
#![allow(clippy::branches_sharing_code)] // TODO: evaluate #![allow(clippy::branches_sharing_code)] // TODO: evaluate
#![deny(clippy::clone_on_ref_ptr)] #![deny(clippy::clone_on_ref_ptr)]
#![feature(option_zip, arbitrary_enum_discriminant)] #![feature(
option_zip,
arbitrary_enum_discriminant,
int_log,
map_first_last,
)]
mod all; mod all;
mod block; mod block;
@ -156,6 +161,7 @@ impl World {
// TODO: Maybe change? // TODO: Maybe change?
civ::SiteKind::Gnarling => world_msg::SiteKind::Gnarling, civ::SiteKind::Gnarling => world_msg::SiteKind::Gnarling,
civ::SiteKind::ChapelSite => world_msg::SiteKind::ChapelSite, civ::SiteKind::ChapelSite => world_msg::SiteKind::ChapelSite,
civ::SiteKind::Citadel => world_msg::SiteKind::Castle,
}, },
wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
} }

View File

@ -318,7 +318,7 @@ impl Fill {
let a: f32 = aabb.max.x as f32 - center.x - 0.5; let a: f32 = aabb.max.x as f32 - center.x - 0.5;
let b: f32 = aabb.max.y as f32 - center.y - 0.5; let b: f32 = aabb.max.y as f32 - center.y - 0.5;
let c: f32 = aabb.max.z as f32 - center.z - 0.5; let c: f32 = aabb.max.z as f32 - center.z - 0.5;
let rpos = pos.as_::<f32>() - center; let rpos = pos.as_::<f32>() + 0.5 - center;
aabb_contains(*aabb, pos) aabb_contains(*aabb, pos)
&& (rpos.x / a).abs().powf(degree) && (rpos.x / a).abs().powf(degree)
+ (rpos.y / b).abs().powf(degree) + (rpos.y / b).abs().powf(degree)

View File

@ -386,6 +386,34 @@ impl Site {
site site
} }
pub fn generate_citadel(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
let mut rng = reseed(rng);
let mut site = Site {
origin,
..Site::default()
};
site.demarcate_obstacles(land);
let citadel = plot::Citadel::generate(origin, land, &mut rng);
site.name = citadel.name().to_string();
let size = citadel.radius() / tile::TILE_SIZE as i32;
let aabr = Aabr {
min: Vec2::broadcast(-size),
max: Vec2::broadcast(size),
};
let plot = site.create_plot(Plot {
kind: PlotKind::Citadel(citadel),
root_tile: aabr.center(),
tiles: aabr_tiles(aabr).collect(),
seed: rng.gen(),
});
site.blit_aabr(aabr, Tile {
kind: TileKind::Building,
plot: Some(plot),
hard_alt: None,
});
site
}
pub fn generate_gnarling(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self { pub fn generate_gnarling(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
let mut rng = reseed(rng); let mut rng = reseed(rng);
let mut site = Site { let mut site = Site {
@ -1174,6 +1202,7 @@ impl Site {
PlotKind::DesertCityTemple(desert_city_temple) => { PlotKind::DesertCityTemple(desert_city_temple) => {
desert_city_temple.render_collect(self, canvas) desert_city_temple.render_collect(self, canvas)
}, },
PlotKind::Citadel(citadel) => citadel.render_collect(self, canvas),
_ => continue, _ => continue,
}; };

View File

@ -2,6 +2,7 @@ mod castle;
mod cliff_tower; mod cliff_tower;
mod desert_city_multiplot; mod desert_city_multiplot;
mod desert_city_temple; mod desert_city_temple;
mod citadel;
pub mod dungeon; pub mod dungeon;
mod giant_tree; mod giant_tree;
mod gnarling; mod gnarling;
@ -12,7 +13,7 @@ mod workshop;
pub use self::{ pub use self::{
castle::Castle, cliff_tower::CliffTower, desert_city_multiplot::DesertCityMultiPlot, castle::Castle, cliff_tower::CliffTower, desert_city_multiplot::DesertCityMultiPlot,
desert_city_temple::DesertCityTemple, dungeon::Dungeon, giant_tree::GiantTree, desert_city_temple::DesertCityTemple, dungeon::Dungeon, giant_tree::GiantTree,
gnarling::GnarlingFortification, house::House, sea_chapel::SeaChapel, workshop::Workshop, gnarling::GnarlingFortification, house::House, sea_chapel::SeaChapel, workshop::Workshop, citadel::Citadel,
}; };
use super::*; use super::*;
@ -61,4 +62,5 @@ pub enum PlotKind {
Gnarling(GnarlingFortification), Gnarling(GnarlingFortification),
GiantTree(GiantTree), GiantTree(GiantTree),
CliffTower(CliffTower), CliffTower(CliffTower),
Citadel(Citadel),
} }

View File

@ -0,0 +1,175 @@
use super::*;
use crate::{
assets::AssetHandle,
site2::util::Dir,
util::{attempt, sampler::Sampler, RandomField, NEIGHBORS},
Land,
};
use common::{
generation::{ChunkSupplement, EntityInfo},
terrain::{Structure as PrefabStructure, StructuresGroup},
};
use kiddo::{distance::squared_euclidean, KdTree};
use lazy_static::lazy_static;
use rand::prelude::*;
use std::ops::{Add, Div, Mul};
use vek::*;
struct Cell {
alt: i32,
colonade: Option<i32>,
}
const CELL_SIZE: i32 = 16;
pub struct Citadel {
name: String,
seed: u32,
origin: Vec3<i32>,
radius: i32,
grid: Grid<Option<Cell>>,
}
impl Citadel {
pub fn generate(wpos: Vec2<i32>, land: &Land, rng: &mut impl Rng) -> Self {
let alt = land.get_alt_approx(wpos) as i32;
let name = NameGen::location(rng).generate_town();
let seed = rng.gen();
let origin = wpos.with_z(alt);
let radius = 150;
let cell_radius = radius / CELL_SIZE;
let mut grid = Grid::populate_from(Vec2::broadcast((cell_radius + 1) * 2), |pos| {
let rpos = pos - cell_radius;
if rpos.magnitude_squared() < cell_radius.pow(2) {
let height = Lerp::lerp(
120.0,
24.0,
rpos.map(i32::abs).reduce_max() as f32 / cell_radius as f32,
);
let level_height = 32.0;
Some(Cell {
alt: land
.get_alt_approx(wpos + rpos * CELL_SIZE + CELL_SIZE / 2)
.add(height)
.div(level_height)
.floor()
.mul(level_height) as i32,
colonade: None,
})
} else {
None
}
});
for y in 0..grid.size().y {
for x in 0..grid.size().x {
let pos = Vec2::new(x, y);
if let Some(min_alt) = NEIGHBORS
.into_iter()
.filter_map(|rpos| Some(grid.get(pos + rpos)?.as_ref()?.alt))
.min()
{
let Some(Some(cell)) = grid.get_mut(pos)
else { continue };
if min_alt < cell.alt {
cell.colonade = Some(min_alt);
}
}
}
}
Self {
name,
seed,
origin,
radius,
grid,
}
}
pub fn name(&self) -> &str { &self.name }
pub fn radius(&self) -> i32 { self.radius }
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules {
trees: (wpos - self.origin).map(i32::abs).reduce_max() > self.radius,
waypoints: false,
..SpawnRules::default()
}
}
fn wpos_cell(&self, wpos: Vec2<i32>) -> Vec2<i32> {
(wpos - self.origin) / CELL_SIZE + self.grid.size() / 2
}
fn cell_wpos(&self, pos: Vec2<i32>) -> Vec2<i32> {
(pos - self.grid.size() / 2) * CELL_SIZE + self.origin
}
}
impl Structure for Citadel {
fn render(&self, _site: &Site, land: &Land, painter: &Painter) {
for (pos, cell) in self.grid.iter_area(
self.wpos_cell(painter.render_aabr().min) - 1,
Vec2::<i32>::from(painter.render_aabr().size()) / CELL_SIZE + 2,
) {
if let Some(cell) = cell {
let wpos = self.cell_wpos(pos);
// Clear space above
painter
.aabb(Aabb {
min: wpos.with_z(cell.alt),
max: (wpos + CELL_SIZE).with_z(cell.alt + 16),
})
.clear();
let mut prim = painter.aabb(Aabb {
min: wpos.with_z(land.get_alt_approx(wpos + CELL_SIZE / 2) as i32 - 32),
max: (wpos + CELL_SIZE).with_z(cell.alt),
});
// Colonades under cells
if let Some(colonade_alt) = cell.colonade {
let hole = painter
.aabb(Aabb {
min: wpos.with_z(colonade_alt),
max: (wpos + CELL_SIZE).with_z(cell.alt),
})
.intersect(painter.prim(Primitive::Superquadric {
aabb: Aabb {
min: (wpos - 1).with_z(colonade_alt - 32),
max: (wpos + 1 + CELL_SIZE).with_z(cell.alt - 1),
},
degree: 2.5,
}));
hole.clear();
prim = prim.without(hole);
}
// Walls around cells
for dir in CARDINALS {
if self
.grid
.get(pos + dir)
.and_then(Option::as_ref)
.map_or(true, |near| near.alt < cell.alt)
{
let offset = wpos + CELL_SIZE / 2 + dir * CELL_SIZE / 2;
let rad = dir.map(|e| if e == 0 { CELL_SIZE / 2 + 1 } else { 1 });
let height = if pos.sum() % 2 == 0 { 5 } else { 2 };
prim = prim.union(painter.aabb(Aabb {
min: (offset - rad).with_z(cell.alt - 6),
max: (offset + rad).with_z(cell.alt + height),
}));
}
}
prim.fill(Fill::Brick(BlockKind::Rock, Rgb::new(100, 100, 100), 20));
}
}
}
}