Merge branch 'synis/castles-cont' into 'master'

Castle generation

See merge request veloren/veloren!1881
This commit is contained in:
Joshua Barretto 2021-03-12 01:49:26 +00:00
commit e43b57f119
3 changed files with 330 additions and 157 deletions

View File

@ -5,7 +5,7 @@ mod tile;
use self::{
gen::{Fill, Primitive, Structure},
plot::{Plot, PlotKind},
tile::{HazardKind, Ori, Tile, TileGrid, TileKind, TILE_SIZE},
tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE},
};
use crate::{
site::SpawnRules,
@ -326,15 +326,15 @@ impl Site {
},
// Guard tower
2 => {
if let Some((aabr, entrance_tile)) = attempt(10, || {
if let Some((_aabr, _)) = attempt(10, || {
site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
}) {
/*
let plot = site.create_plot(Plot {
kind: PlotKind::Castle(plot::Castle::generate(
land,
&mut rng,
&site,
entrance_tile,
aabr,
)),
root_tile: aabr.center(),
@ -346,6 +346,7 @@ impl Site {
kind: TileKind::Castle,
plot: Some(plot),
});
*/
}
},
// Field
@ -381,40 +382,87 @@ impl Site {
},
// Castle
4 if castles < 1 => {
if let Some((aabr, entrance_tile)) = attempt(10, || {
if let Some((aabr, _entrance_tile)) = attempt(10, || {
site.find_roadside_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16))
}) {
let offset = rng.gen_range(5..(aabr.size().w.min(aabr.size().h) - 4));
let gate_aabr = Aabr {
min: Vec2::new(aabr.min.x + offset - 1, aabr.min.y),
max: Vec2::new(aabr.min.x + offset + 2, aabr.min.y + 1),
};
let plot = site.create_plot(Plot {
kind: PlotKind::Castle(plot::Castle::generate(
land,
&mut rng,
&site,
entrance_tile,
aabr,
land, &mut rng, &site, aabr, gate_aabr,
)),
root_tile: aabr.center(),
tiles: aabr_tiles(aabr).collect(),
seed: rng.gen(),
});
// Walls
site.blit_aabr(aabr, Tile {
let wall_north = Tile {
kind: TileKind::Wall(Ori::North),
plot: Some(plot),
});
};
let tower = Tile {
kind: TileKind::Tower,
let wall_east = Tile {
kind: TileKind::Wall(Ori::East),
plot: Some(plot),
};
for x in 0..aabr.size().w {
site.tiles
.set(aabr.min + Vec2::new(x, 0), wall_east.clone());
site.tiles.set(
aabr.min + Vec2::new(x, aabr.size().h - 1),
wall_east.clone(),
);
}
for y in 0..aabr.size().h {
site.tiles
.set(aabr.min + Vec2::new(0, y), wall_north.clone());
site.tiles.set(
aabr.min + Vec2::new(aabr.size().w - 1, y),
wall_north.clone(),
);
}
let gate = Tile {
kind: TileKind::Gate,
plot: Some(plot),
};
let tower_parapet = Tile {
kind: TileKind::Tower(RoofKind::Parapet),
plot: Some(plot),
};
let tower_pyramid = Tile {
kind: TileKind::Tower(RoofKind::Pyramid),
plot: Some(plot),
};
site.tiles.set(
Vec2::new(aabr.min.x + offset - 2, aabr.min.y),
tower_parapet.clone(),
);
site.tiles
.set(Vec2::new(aabr.min.x, aabr.min.y), tower.clone());
.set(Vec2::new(aabr.min.x + offset - 1, aabr.min.y), gate.clone());
site.tiles
.set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower.clone());
.set(Vec2::new(aabr.min.x + offset, aabr.min.y), gate.clone());
site.tiles
.set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower.clone());
.set(Vec2::new(aabr.min.x + offset + 1, aabr.min.y), gate.clone());
site.tiles.set(
Vec2::new(aabr.min.x + offset + 2, aabr.min.y),
tower_parapet.clone(),
);
site.tiles
.set(Vec2::new(aabr.max.x - 1, aabr.max.y - 1), tower.clone());
.set(Vec2::new(aabr.min.x, aabr.min.y), tower_parapet.clone());
site.tiles
.set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower_parapet.clone());
site.tiles
.set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower_parapet.clone());
site.tiles.set(
Vec2::new(aabr.max.x - 1, aabr.max.y - 1),
tower_parapet.clone(),
);
// Courtyard
site.blit_aabr(
@ -441,19 +489,19 @@ impl Site {
);
site.tiles.set(
Vec2::new(aabr.center().x + 2, aabr.center().y + 2),
tower.clone(),
tower_pyramid.clone(),
);
site.tiles.set(
Vec2::new(aabr.center().x + 2, aabr.center().y - 3),
tower.clone(),
tower_pyramid.clone(),
);
site.tiles.set(
Vec2::new(aabr.center().x - 3, aabr.center().y + 2),
tower.clone(),
tower_pyramid.clone(),
);
site.tiles.set(
Vec2::new(aabr.center().x - 3, aabr.center().y - 3),
tower.clone(),
tower_pyramid.clone(),
);
site.blit_aabr(
@ -462,7 +510,7 @@ impl Site {
max: aabr.center() + 2,
},
Tile {
kind: TileKind::Keep(tile::KeepKind::Middle),
kind: TileKind::Keep(KeepKind::Middle),
plot: Some(plot),
},
);

View File

@ -1,13 +1,15 @@
use super::*;
use crate::Land;
use crate::{util::SQUARE_4, Land};
use common::terrain::{Block, BlockKind};
use num::Integer;
use rand::prelude::*;
use vek::*;
pub struct Castle {
_entrance_tile: Vec2<i32>,
tile_aabr: Aabr<i32>,
_bounds: Aabr<i32>,
gate_aabr: Aabr<i32>,
gate_alt: i32,
alt: i32,
}
@ -16,22 +18,31 @@ impl Castle {
land: &Land,
_rng: &mut impl Rng,
site: &Site,
entrance_tile: Vec2<i32>,
tile_aabr: Aabr<i32>,
gate_aabr: Aabr<i32>,
) -> Self {
let alt = SQUARE_4
.iter()
.map(|corner| tile_aabr.min + (tile_aabr.max - tile_aabr.min - 1) * corner)
.map(|pos| land.get_alt_approx(site.tile_center_wpos(pos)) as i32)
.sum::<i32>()
/ 4;
Self {
_entrance_tile: entrance_tile,
tile_aabr,
_bounds: Aabr {
min: site.tile_wpos(tile_aabr.min),
max: site.tile_wpos(tile_aabr.max),
},
alt: land.get_alt_approx(site.tile_center_wpos(entrance_tile)) as i32,
gate_aabr,
gate_alt: land.get_alt_approx(site.tile_center_wpos(gate_aabr.center())) as i32,
alt,
}
}
}
impl Structure for Castle {
#[allow(clippy::identity_op)]
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Id<Primitive>, Fill)>(
&self,
site: &Site,
@ -39,177 +50,232 @@ impl Structure for Castle {
mut fill: G,
) {
let wall_height = 24;
let _thickness = 12;
let parapet_height = 2;
let parapet_width = 1;
let _downwards = 40;
let tower_height = 12;
let parapet_gap = 2;
let parapet_offset = 2;
let ts = TILE_SIZE as i32;
let tower_height = 16;
let keep_levels = 3;
let keep_level_height = 8;
let _keep_height = wall_height + keep_levels * keep_level_height + 1;
let wall_rgb = Rgb::new(38, 46, 43);
// Flatten inside of the castle
fill(
prim(Primitive::Aabb(Aabb {
min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt),
max: site
.tile_wpos(self.tile_aabr.max)
.with_z(self.alt + tower_height),
})),
Fill::Block(Block::empty()),
);
fill(
prim(Primitive::Aabb(Aabb {
min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt),
max: site.tile_wpos(self.tile_aabr.max).with_z(self.gate_alt + 1),
})),
Fill::Block(Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))),
);
for x in 0..self.tile_aabr.size().w {
for y in 0..self.tile_aabr.size().h {
let tile_pos = self.tile_aabr.min + Vec2::new(x, y);
let _wpos_center = site.tile_center_wpos(tile_pos);
let wpos = site.tile_wpos(tile_pos);
let ori = if x == 0 || x == self.tile_aabr.size().w - 1 {
Vec2::new(1, 0)
} else {
Vec2::new(0, 1)
};
let ori_tower_x = if x == 0 {
Vec2::new(1, 0)
} else {
Vec2::new(0, 0)
};
let ori_tower_y = if y == 0 {
Vec2::new(0, 1)
} else {
Vec2::new(0, 0)
};
let ori_tower = ori_tower_x + ori_tower_y;
match site.tiles.get(tile_pos).kind.clone() {
TileKind::Wall(_ori) => {
TileKind::Wall(ori) => {
let dir = ori.dir();
let wall = prim(Primitive::Aabb(Aabb {
min: wpos.with_z(self.alt),
max: (wpos + 6).with_z(self.alt + wall_height + parapet_height),
min: wpos.with_z(self.alt - 20),
max: (wpos + ts).with_z(self.alt + wall_height),
}));
let cut_path = prim(Primitive::Aabb(Aabb {
min: (wpos + (parapet_width * ori) as Vec2<i32>)
.with_z(self.alt + wall_height),
max: (wpos
+ (6 - parapet_width) * ori as Vec2<i32>
+ 6 * ori.yx() as Vec2<i32>)
// TODO Figure out logic to choose on on which site wall should be placed
// (inner, outer)
let parapet = prim(Primitive::Aabb(Aabb {
min: (wpos - dir.yx()).with_z(self.alt + wall_height),
max: (wpos + ts * dir).with_z(self.alt + wall_height + parapet_height),
}));
let parapet2 = prim(Primitive::Aabb(Aabb {
min: (wpos + ts * dir.yx()).with_z(self.alt + wall_height),
max: (wpos + (ts + 1) * dir.yx() + ts * dir)
.with_z(self.alt + wall_height + parapet_height),
}));
let cut_sides1 = prim(Primitive::Aabb(Aabb {
min: Vec3::new(wpos.x, wpos.y, self.alt + wall_height + 1),
max: Vec3::new(
wpos.x + 6 * ori.x + ori.y,
wpos.y + 6 * ori.y + ori.x,
self.alt + wall_height + parapet_height,
),
let cut_sides = prim(Primitive::Aabb(Aabb {
min: (wpos + parapet_offset * dir - dir.yx())
.with_z(self.alt + wall_height + parapet_height - 1),
max: (wpos
+ (ts + 1) * dir.yx()
+ (parapet_offset + parapet_gap) * dir)
.with_z(self.alt + wall_height + parapet_height),
}));
let pillar_start = prim(Primitive::Aabb(Aabb {
min: Vec3::new(wpos.x, wpos.y - 1, self.alt),
max: Vec3::new(wpos.x + 1, wpos.y + 7, self.alt + wall_height),
}));
let pillar_end = prim(Primitive::Aabb(Aabb {
min: Vec3::new(wpos.x + 5, wpos.y - 1, self.alt),
max: Vec3::new(wpos.x + 6, wpos.y + 7, self.alt + wall_height),
}));
let pillars = prim(Primitive::Or(pillar_start, pillar_end));
fill(
prim(Primitive::Or(wall, pillars)),
Fill::Block(Block::new(BlockKind::Rock, Rgb::new(33, 33, 33))),
);
fill(cut_path, Fill::Block(Block::empty()));
fill(cut_sides1, Fill::Block(Block::empty()));
fill(wall, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
let sides = prim(Primitive::Or(parapet, parapet2));
fill(sides, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
if (x + y).is_odd() {
fill(
prim(Primitive::Aabb(Aabb {
min: (wpos + 2 * dir - dir.yx()).with_z(self.alt - 20),
max: (wpos + 4 * dir + (ts + 1) * dir.yx())
.with_z(self.alt + wall_height),
})),
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
);
} else {
let window_top = prim(Primitive::Aabb(Aabb {
min: (wpos + 2 * dir).with_z(self.alt + wall_height / 4 + 9),
max: (wpos + (ts - 2) * dir + dir.yx())
.with_z(self.alt + wall_height / 4 + 12),
}));
let window_bottom = prim(Primitive::Aabb(Aabb {
min: (wpos + 1 * dir).with_z(self.alt + wall_height / 4),
max: (wpos + (ts - 1) * dir + dir.yx())
.with_z(self.alt + wall_height / 4 + 9),
}));
let window_top2 = prim(Primitive::Aabb(Aabb {
min: (wpos + 2 * dir + (ts - 1) * dir.yx())
.with_z(self.alt + wall_height / 4 + 9),
max: (wpos + (ts - 2) * dir + ts * dir.yx())
.with_z(self.alt + wall_height / 4 + 12),
}));
let window_bottom2 = prim(Primitive::Aabb(Aabb {
min: (wpos + 1 * dir + (ts - 1) * dir.yx())
.with_z(self.alt + wall_height / 4),
max: (wpos + (ts - 1) * dir + ts * dir.yx())
.with_z(self.alt + wall_height / 4 + 9),
}));
fill(window_bottom, Fill::Block(Block::empty()));
fill(window_top, Fill::Block(Block::empty()));
fill(window_bottom2, Fill::Block(Block::empty()));
fill(window_top2, Fill::Block(Block::empty()));
}
fill(cut_sides, Fill::Block(Block::empty()));
},
TileKind::Tower => {
TileKind::Tower(roof) => {
let tower_total_height =
self.alt + wall_height + parapet_height + tower_height;
let tower_lower = prim(Primitive::Aabb(Aabb {
min: wpos.with_z(self.alt),
max: (wpos + 6).with_z(self.alt + wall_height + tower_height),
min: (wpos - 1).with_z(self.alt - 20),
max: (wpos + ts + 1).with_z(tower_total_height),
}));
let tower_lower_inner_x = prim(Primitive::Aabb(Aabb {
min: Vec3::new(
wpos.x + ori_tower.x,
wpos.y + parapet_width,
self.alt + wall_height,
),
max: Vec3::new(
wpos.x + 6 + ori_tower.x - 1,
wpos.y + 6 - parapet_width,
self.alt + wall_height + tower_height / 3,
),
}));
let tower_lower_inner_y = prim(Primitive::Aabb(Aabb {
min: Vec3::new(
wpos.x + parapet_width,
wpos.y + ori_tower.y,
self.alt + wall_height,
),
max: Vec3::new(
wpos.x + 6 - parapet_width,
wpos.y + 6 + ori_tower.y - 1,
self.alt + wall_height + tower_height / 3,
),
}));
let tower_lower_inner =
prim(Primitive::Or(tower_lower_inner_x, tower_lower_inner_y));
fill(
prim(Primitive::Xor(tower_lower, tower_lower_inner)),
Fill::Block(Block::new(BlockKind::Rock, Rgb::new(33, 33, 33))),
);
fill(tower_lower, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
let tower_upper = prim(Primitive::Aabb(Aabb {
min: Vec3::new(
wpos.x - 1,
wpos.y - 1,
self.alt + wall_height + tower_height - 3i32,
),
max: Vec3::new(
wpos.x + 7,
wpos.y + 7,
self.alt + wall_height + tower_height - 1i32,
),
min: (wpos - 2).with_z(tower_total_height - 4i32),
max: (wpos + ts + 2).with_z(tower_total_height - 2i32),
}));
let tower_upper2 = prim(Primitive::Aabb(Aabb {
min: Vec3::new(
wpos.x - 2,
wpos.y - 2,
self.alt + wall_height + tower_height - 1i32,
),
max: Vec3::new(
wpos.x + 8,
wpos.y + 8,
self.alt + wall_height + tower_height,
),
min: (wpos - 3).with_z(tower_total_height - 2i32),
max: (wpos + ts + 3).with_z(tower_total_height),
}));
fill(
prim(Primitive::Or(tower_upper, tower_upper2)),
Fill::Block(Block::new(BlockKind::Rock, Rgb::new(33, 33, 33))),
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
);
let roof_lip = 1;
let roof_height = 8 / 2 + roof_lip + 1;
match roof {
RoofKind::Pyramid => {
let roof_lip = 1;
let roof_height = (ts + 3) / 2 + roof_lip + 1;
// Roof
fill(
prim(Primitive::Pyramid {
aabb: Aabb {
min: (wpos - 2 - roof_lip)
.with_z(self.alt + wall_height + tower_height),
max: (wpos + 8 + roof_lip).with_z(
self.alt + wall_height + tower_height + roof_height,
fill(
prim(Primitive::Pyramid {
aabb: Aabb {
min: (wpos - 3 - roof_lip).with_z(tower_total_height),
max: (wpos + ts + 3 + roof_lip)
.with_z(tower_total_height + roof_height),
},
inset: roof_height,
}),
Fill::Brick(BlockKind::Wood, Rgb::new(40, 5, 11), 10),
);
},
RoofKind::Parapet => {
let tower_top_outer = prim(Primitive::Aabb(Aabb {
min: (wpos - 3).with_z(
self.alt + wall_height + parapet_height + tower_height,
),
},
inset: roof_height,
}),
Fill::Block(Block::new(BlockKind::Wood, Rgb::new(116, 20, 20))),
);
max: (wpos + ts + 3)
.with_z(tower_total_height + parapet_height),
}));
let tower_top_inner = prim(Primitive::Aabb(Aabb {
min: (wpos - 2).with_z(tower_total_height),
max: (wpos + ts + 2)
.with_z(tower_total_height + parapet_height),
}));
fill(
prim(Primitive::Xor(tower_top_outer, tower_top_inner)),
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
);
for x in (wpos.x..wpos.x + ts).step_by(2 * parapet_gap as usize) {
fill(
prim(Primitive::Aabb(Aabb {
min: Vec3::new(x, wpos.y - 3, tower_total_height + 1),
max: Vec3::new(
x + parapet_gap,
wpos.y + ts + 3,
tower_total_height + parapet_height,
),
})),
Fill::Block(Block::empty()),
);
}
for y in (wpos.y..wpos.y + ts).step_by(2 * parapet_gap as usize) {
fill(
prim(Primitive::Aabb(Aabb {
min: Vec3::new(wpos.x - 3, y, tower_total_height + 1),
max: Vec3::new(
wpos.x + ts + 3,
y + parapet_gap,
tower_total_height + parapet_height,
),
})),
Fill::Block(Block::empty()),
);
}
for &cpos in SQUARE_4.iter() {
let pos = wpos - 3 + (ts + 6) * cpos - cpos;
let pos2 = wpos - 2 + (ts + 4) * cpos - cpos;
fill(
prim(Primitive::Aabb(Aabb {
min: pos.with_z(tower_total_height - 2),
max: (pos + 1)
.with_z(tower_total_height + parapet_height),
})),
Fill::Block(Block::empty()),
);
fill(
prim(Primitive::Aabb(Aabb {
min: pos2.with_z(tower_total_height - 4),
max: (pos2 + 1).with_z(tower_total_height - 2),
})),
Fill::Block(Block::empty()),
);
}
},
}
},
TileKind::Keep(kind) => {
match kind {
tile::KeepKind::Middle => {
KeepKind::Middle => {
for i in 0..keep_levels + 1 {
let height = keep_level_height * i;
fill(
prim(Primitive::Aabb(Aabb {
min: wpos.with_z(self.alt + height),
max: (wpos + 6).with_z(self.alt + height + 1),
max: (wpos + ts).with_z(self.alt + height + 1),
})),
Fill::Block(Block::new(
BlockKind::Rock,
BlockKind::Wood,
Rgb::new(89, 44, 14),
)),
);
}
},
tile::KeepKind::Corner => {},
tile::KeepKind::Wall(_ori) => {
KeepKind::Corner => {},
KeepKind::Wall(_ori) => {
for i in 0..keep_levels + 1 {
let _height = keep_level_height * i;
// TODO clamp value in case of big heights
@ -222,5 +288,57 @@ impl Structure for Castle {
}
}
}
// Render gate here
// TODO move this into tile loop
let gate_aabb = Aabb {
min: (site.tile_wpos(self.gate_aabr.min) + Vec2::unit_x()).with_z(self.gate_alt - 1),
max: (site.tile_wpos(self.gate_aabr.max) - Vec2::unit_x())
.with_z(self.alt + wall_height),
};
fill(
prim(Primitive::Aabb(gate_aabb)),
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
);
fill(
prim(Primitive::Aabb(Aabb {
min: (gate_aabb.min + Vec3::unit_x() * 2 + Vec3::unit_z() * 2),
max: (gate_aabb.max - Vec3::unit_x() * 2 - Vec3::unit_z() * 16),
})),
Fill::Block(Block::empty()),
);
let height = self.alt + wall_height - 17;
for i in 1..5 {
fill(
prim(Primitive::Aabb(Aabb {
min: Vec3::new(gate_aabb.min.x + 2 + i, gate_aabb.min.y, height + i as i32),
max: Vec3::new(
gate_aabb.max.x - 2 - i,
gate_aabb.max.y,
height + i as i32 + 1,
),
})),
Fill::Block(Block::empty()),
);
}
let height = self.alt + wall_height - 7;
for x in (gate_aabb.min.x + 1..gate_aabb.max.x - 2).step_by(4) {
fill(
prim(Primitive::Aabb(Aabb {
min: Vec3::new(x, gate_aabb.min.y + 1, height - 13),
max: Vec3::new(x + 2, gate_aabb.min.y + 2, height),
})),
Fill::Brick(BlockKind::Rock, Rgb::new(27, 35, 32), 8),
);
}
for z in (height - 12..height).step_by(4) {
fill(
prim(Primitive::Aabb(Aabb {
min: Vec3::new(gate_aabb.min.x + 2, gate_aabb.min.y + 1, z),
max: Vec3::new(gate_aabb.max.x - 2, gate_aabb.min.y + 2, z + 2),
})),
Fill::Brick(BlockKind::Rock, Rgb::new(27, 35, 32), 8),
);
}
}
}

View File

@ -183,8 +183,9 @@ pub enum TileKind {
Building,
Castle,
Wall(Ori),
Tower,
Tower(RoofKind),
Keep(KeepKind),
Gate,
}
#[derive(Clone, PartialEq)]
@ -229,6 +230,12 @@ pub enum KeepKind {
Wall(Ori),
}
#[derive(Copy, Clone, PartialEq)]
pub enum RoofKind {
Parapet,
Pyramid,
}
#[repr(u8)]
#[derive(Copy, Clone, PartialEq)]
pub enum Ori {