short bridges

This commit is contained in:
IsseW 2022-09-27 14:58:15 +02:00 committed by Isse
parent 3bb66cf2ff
commit 78e227d317
6 changed files with 315 additions and 99 deletions

View File

@ -8,7 +8,7 @@ use crate::{
site::{namegen::NameGen, Castle, Settlement, Site as WorldSite, Tree},
site2,
util::{attempt, seed_expan, DHashMap, NEIGHBORS},
Index, Land,
Index, IndexRef, Land,
};
use common::{
astar::Astar,
@ -252,65 +252,73 @@ impl Civs {
});
let mut rng = ctx.reseed().rng;
let site = index.sites.insert(match &sim_site.kind {
SiteKind::Settlement => {
WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng))
},
SiteKind::Dungeon => WorldSite::dungeon(site2::Site::generate_dungeon(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::Castle => {
WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng))
},
SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::CliffTown => WorldSite::cliff_town(site2::Site::generate_cliff_town(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::SavannahPit => WorldSite::savannah_pit(
site2::Site::generate_savannah_pit(&Land::from_sim(ctx.sim), &mut rng, wpos),
),
SiteKind::DesertCity => WorldSite::desert_city(site2::Site::generate_desert_city(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::Tree => {
WorldSite::tree(Tree::generate(wpos, &Land::from_sim(ctx.sim), &mut rng))
},
SiteKind::GiantTree => WorldSite::giant_tree(site2::Site::generate_giant_tree(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::Gnarling => WorldSite::gnarling(site2::Site::generate_gnarling(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::ChapelSite => WorldSite::chapel_site(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,
)),
SiteKind::Bridge(a, b) => WorldSite::bridge(site2::Site::generate_bridge(
&Land::from_sim(ctx.sim),
&mut rng,
*a,
*b,
)),
let site = index.sites.insert({
let index_ref = IndexRef {
colors: &index.colors(),
features: &index.features(),
index,
};
match &sim_site.kind {
SiteKind::Settlement => {
WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng))
},
SiteKind::Dungeon => WorldSite::dungeon(site2::Site::generate_dungeon(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::Castle => {
WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng))
},
SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::CliffTown => WorldSite::cliff_town(site2::Site::generate_cliff_town(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::SavannahPit => {
WorldSite::savannah_pit(site2::Site::generate_savannah_pit(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
))
},
SiteKind::DesertCity => WorldSite::desert_city(
site2::Site::generate_desert_city(&Land::from_sim(ctx.sim), &mut rng, wpos),
),
SiteKind::Tree => {
WorldSite::tree(Tree::generate(wpos, &Land::from_sim(ctx.sim), &mut rng))
},
SiteKind::GiantTree => WorldSite::giant_tree(site2::Site::generate_giant_tree(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::Gnarling => WorldSite::gnarling(site2::Site::generate_gnarling(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
)),
SiteKind::ChapelSite => WorldSite::chapel_site(
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,
)),
SiteKind::Bridge(a, b) => WorldSite::bridge(site2::Site::generate_bridge(
&Land::from_sim(ctx.sim),
index_ref,
&mut rng,
*a,
*b,
)),
}
});
sim_site.site_tmp = Some(site);
let site_ref = &index.sites[site];

View File

@ -1,4 +1,4 @@
use crate::sim;
use crate::{column::ColumnGen, sim, util::Sampler, ColumnSample, IndexRef};
use common::{terrain::TerrainChunkSize, vol::RectVolSize};
use vek::*;
@ -45,4 +45,13 @@ impl<'a> Land<'a> {
) -> Option<(f32, Vec2<f32>, sim::Path, Vec2<f32>)> {
self.sim.and_then(|sim| sim.get_nearest_path(wpos))
}
pub fn column_sample<'sample>(
&'sample self,
wpos: Vec2<i32>,
index: IndexRef<'sample>,
) -> Option<ColumnSample<'sample>> {
self.sim
.and_then(|sim| ColumnGen::new(sim).get((wpos, index, None)))
}
}

View File

@ -1148,9 +1148,10 @@ impl Painter {
let stairs = self.ramp(
Aabb {
min: (corner - right.to_vec2() * (stair_len + thickness))
.with_z(z - stair_len),
max: (corner - right.to_vec2() * (thickness - 1) - forward.to_vec2() * thickness)
min: (corner - right.to_vec2() * (stair_len + thickness)).with_z(z - stair_len),
max: (corner
- right.to_vec2() * (thickness - 1)
- forward.to_vec2() * thickness)
.with_z(z + 1),
}
.made_valid(),

View File

@ -13,7 +13,7 @@ use crate::{
sim::Path,
site::{namegen::NameGen, SpawnRules},
util::{attempt, DHashSet, Grid, CARDINALS, SQUARE_4, SQUARE_9},
Canvas, Land,
Canvas, IndexRef, Land,
};
use common::{
astar::Astar,
@ -990,6 +990,7 @@ impl Site {
pub fn generate_bridge(
land: &Land,
index: IndexRef,
rng: &mut impl Rng,
start: Vec2<i32>,
end: Vec2<i32>,
@ -1012,6 +1013,18 @@ impl Site {
let orth = (start_tile - end_tile).yx().map(|dir| dir.signum().abs());
let start_aabr = Aabr {
min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
};
let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile);
let start_tile = site.wpos_tile_pos(bridge.start.xy());
let end_tile = site.wpos_tile_pos(bridge.end.xy());
let width = (bridge.width + TILE_SIZE as i32 / 2) / TILE_SIZE as i32;
let aabr = Aabr {
min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
@ -1020,20 +1033,18 @@ impl Site {
site.create_road(
land,
&mut rng,
aabr.min - 1,
aabr.min - 1 + orth * (3 + width * 2),
bridge.dir.select_aabr_with(aabr, aabr.center()) + bridge.dir.to_vec2(),
bridge.dir.select_aabr_with(start_aabr, aabr.center()),
2,
);
site.create_road(
land,
&mut rng,
aabr.max + 1,
aabr.max + 1 - orth * (3 + width * 2),
(-bridge.dir).select_aabr_with(aabr, aabr.center()) - bridge.dir.to_vec2(),
(-bridge.dir).select_aabr_with(start_aabr, aabr.center()),
2,
);
let bridge = plot::Bridge::generate(land, &mut rng, &site, start_tile, end_tile, width);
let plot = site.create_plot(Plot {
kind: PlotKind::Bridge(bridge),
root_tile: start_tile,
@ -1047,6 +1058,40 @@ impl Site {
hard_alt: None,
});
let size = (1.5 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
site.find_roadside_aabr(
&mut rng,
4..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let house = plot::House::generate(
land,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let house_alt = house.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::House(house),
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: Some(house_alt),
});
} else {
site.make_plaza(land, &mut rng);
}
site
}

View File

@ -14,6 +14,7 @@ enum RoofKind {
enum BridgeKind {
Flat,
Tower(RoofKind),
Short,
}
fn aabb(min: Vec3<i32>, max: Vec3<i32>) -> Aabb<i32> {
@ -24,6 +25,92 @@ fn aabb(min: Vec3<i32>, max: Vec3<i32>) -> Aabb<i32> {
}
}
fn render_short(bridge: &Bridge, painter: &Painter) {
let rock = Fill::Brick(BlockKind::Rock, Rgb::gray(70), 25);
let light_rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(140)));
let bridge_width = 3;
let orth_dir = bridge.dir.orthogonal();
let orthogonal = orth_dir.to_vec2();
let forward = bridge.dir.to_vec2();
let len = (bridge.start.xy() - bridge.end.xy())
.map(|e| e.abs())
.reduce_max();
let inset = 4;
let height = bridge.end.z + len / 3 - inset;
let side = orthogonal * bridge_width;
let remove = painter.vault(
aabb(
(bridge.start.xy() - side + forward * inset).with_z(bridge.start.z),
(bridge.end.xy() + side - forward * inset).with_z(height),
),
orth_dir,
);
let ramp_in = len / 3;
let top = height + 2;
let outset = 7;
let up_ramp = |point: Vec3<i32>, dir: Dir, side_len: i32| {
let forward = dir.to_vec2();
let side = dir.orthogonal().to_vec2() * side_len;
painter.ramp(
aabb(
(point.xy() - side - forward * (top - point.z - ramp_in + outset))
.with_z(bridge.start.z),
(point.xy() + side + forward * ramp_in).with_z(top),
),
top - point.z + 1 + outset,
dir,
)
};
let bridge_prim = |side_len: i32| {
let side = orthogonal * side_len;
painter
.aabb(aabb(
(bridge.start.xy() - side + forward * ramp_in).with_z(bridge.start.z),
(bridge.end.xy() + side - forward * ramp_in).with_z(top),
))
.union(up_ramp(bridge.start, bridge.dir, side_len).union(up_ramp(
bridge.end,
-bridge.dir,
side_len,
)))
};
let b = bridge_prim(bridge_width);
let t = 4;
b.union(painter.aabb(aabb(
(bridge.start.xy() - side - forward * (top - bridge.start.z - ramp_in + outset)).with_z(bridge.start.z - t),
(bridge.end.xy() + side + forward * (top - bridge.end.z - ramp_in + outset)).with_z(bridge.start.z),
)))
.translate(Vec3::new(0, 0, t))
.without(b)
.clear();
b.without(remove).fill(rock.clone());
let prim = bridge_prim(bridge_width + 1);
prim.translate(Vec3::unit_z())
.without(prim)
.without(painter.aabb(aabb(
bridge.start - side - forward * outset,
(bridge.end.xy() + side + forward * outset).with_z(top + 1),
)))
.fill(light_rock);
}
fn render_flat(bridge: &Bridge, painter: &Painter) {
let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
@ -98,9 +185,10 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
let tower_end = bridge.end.z + tower_height_extend;
let tower_center = bridge.start.xy() + forward * tower_size;
let tower_aabr = Aabr {
min: bridge.start.xy() - tower_size,
max: bridge.start.xy() + tower_size,
min: tower_center - tower_size,
max: tower_center + tower_size,
};
let len = (bridge.dir.select(bridge.end.xy()) - bridge.dir.select_aabr(tower_aabr)).abs();
@ -119,7 +207,7 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
))
.clear();
let c = bridge.start.xy() - forward * tower_size;
let c = (-bridge.dir).select_aabr_with(tower_aabr, tower_aabr.center());
painter
.aabb(aabb(
(c - orthogonal).with_z(bridge.start.z),
@ -139,7 +227,7 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
.without(painter.ramp(ramp_aabb, ramp_height, -bridge.dir))
.clear();
let c = bridge.start.xy() + forward * tower_size;
let c = bridge.dir.select_aabr_with(tower_aabr, tower_aabr.center());
painter
.aabb(aabb(
(c - orthogonal).with_z(bridge.end.z),
@ -236,7 +324,12 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
.fill(Fill::Sprite(SpriteKind::FireBowlGround));
},
RoofKind::Hipped => {
painter.pyramid(aabb((tower_aabr.min - 1).with_z(tower_end + 1), (tower_aabr.max + 1).with_z(tower_end + 2 + tower_size))).fill(wood.clone());
painter
.pyramid(aabb(
(tower_aabr.min - 1).with_z(tower_end + 1),
(tower_aabr.max + 1).with_z(tower_end + 2 + tower_size),
))
.fill(wood.clone());
},
}
@ -251,11 +344,11 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
let offset = forward * p;
let size = bridge_width * orthogonal + forward * size;
let start = bridge.dir.select_aabr_with(tower_aabr, tower_aabr.center());
let start = bridge.dir.select_aabr_with(tower_aabr, tower_aabr.center()) + forward;
painter
.aabb(aabb(
(start - orthogonal * bridge_width).with_z(bridge.center.z - 10),
bridge.end.with_z(bridge.end.z - 1) + orthogonal * bridge_width,
(bridge.end + orthogonal * bridge_width).with_z(bridge.end.z - 1),
))
.without(
painter
@ -270,6 +363,13 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
)
.fill(rock.clone());
painter
.aabb(aabb(
(start - orthogonal * bridge_width).with_z(bridge.end.z),
(bridge.end + orthogonal * bridge_width).with_z(bridge.end.z + 5),
))
.clear();
let light_spacing = 10;
let n = len / light_spacing;
let p = len / n;
@ -285,51 +385,104 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
}
pub struct Bridge {
start: Vec3<i32>,
end: Vec3<i32>,
center: Vec3<i32>,
dir: Dir,
kind: BridgeKind,
pub(crate) start: Vec3<i32>,
pub(crate) end: Vec3<i32>,
pub(crate) width: i32,
pub(crate) dir: Dir,
center: Vec3<i32>,
kind: BridgeKind,
}
impl Bridge {
pub fn generate(
land: &Land,
index: IndexRef,
rng: &mut impl Rng,
site: &Site,
start: Vec2<i32>,
end: Vec2<i32>,
width: i32,
) -> Self {
let start = site.tile_center_wpos(start);
let end = site.tile_center_wpos(end);
let width = width * TILE_SIZE as i32 + TILE_SIZE as i32 / 2;
let start = site.tile_wpos(start);
let end = site.tile_wpos(end);
let center = (start + end) / 2;
let min_water_dist = 5;
let find_edge = |start: Vec2<i32>, end: Vec2<i32>| {
let mut test_start = start;
let dir = Dir::from_vector(end - start).to_vec2();
let mut last_alt = if let Some(col) = land.column_sample(start, index) {
col.alt as i32
} else {
return test_start.with_z(land.get_alt_approx(start) as i32);
};
let mut step = 0;
loop {
if let Some(sample) = land.column_sample(test_start + step * dir, index) {
let alt = sample.alt as i32;
if last_alt - alt > 1 + (step + 3) / 4
|| sample.riverless_alt - sample.alt > 2.0
{
break test_start.with_z(last_alt);
} else {
let water_dist = sample.water_dist.unwrap_or(16.0) as i32;
let mut start = start.with_z(land.get_alt_approx(start) as i32);
let mut end = end.with_z(land.get_alt_approx(end) as i32);
if start.z > end.z {
std::mem::swap(&mut start, &mut end);
}
test_start += step * dir;
if water_dist <= min_water_dist {
break test_start.with_z(alt);
}
step = water_dist - min_water_dist;
last_alt = alt;
}
} else {
break test_start.with_z(last_alt);
}
/*
let alt = land.get_alt_approx(test_start + dir);
let is_water = land.get_chunk_wpos(test_start + dir * 5).map_or(false, |chunk| chunk.river.river_kind.is_some());
if is_water || last_alt - alt as i32 > 1 || start.z - alt as i32 > 5 {
break test_start.with_z(last_alt);
}
last_alt = alt as i32;
test_start += dir;
*/
}
};
let test_start = find_edge(start, end);
let test_end = find_edge(end, start);
let (start, end) = if test_start.z < test_end.z {
(test_start, test_end)
} else {
(test_end, test_start)
};
let center = (start.xy() + end.xy()) / 2;
let center = center.with_z(land.get_alt_approx(center) as i32);
let len = (start.xy() - end.xy()).map(|e| e.abs()).reduce_max();
Self {
start,
end,
center,
dir: Dir::from_vector(end.xy() - start.xy()),
kind: if end.z - start.z > 10 {
kind: if end.z - start.z > 14 {
BridgeKind::Tower(match rng.gen_range(0..=2) {
0 => RoofKind::Crenelated,
_ => RoofKind::Hipped,
})
} else if len < 40 {
BridgeKind::Short
} else {
BridgeKind::Flat
},
width,
width: 8,
}
}
}
@ -337,8 +490,9 @@ impl Bridge {
impl Structure for Bridge {
fn render(&self, _site: &Site, _land: &Land, painter: &Painter) {
match &self.kind {
BridgeKind::Flat => render_flat(&self, painter),
BridgeKind::Tower(roof) => render_tower(&self, painter, roof),
BridgeKind::Flat => render_flat(self, painter),
BridgeKind::Tower(roof) => render_tower(self, painter, roof),
BridgeKind::Short => render_short(self, painter),
}
}
}

View File

@ -13,7 +13,6 @@ pub enum Dir {
}
impl Dir {
pub const ALL: [Dir; 4] = [Dir::X, Dir::Y, Dir::NegX, Dir::NegY];
pub fn choose(rng: &mut impl Rng) -> Dir {