mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/worldgen' into 'master'
Improvements to site2 See merge request veloren/veloren!3159
This commit is contained in:
commit
d8c4284a32
@ -89,7 +89,7 @@ void main() {
|
|||||||
: compute_attenuation_point(f_pos, -view_dir, vec3(0), fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
|
: compute_attenuation_point(f_pos, -view_dir, vec3(0), fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, f_pos, MU_WATER, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
|
max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
|
||||||
|
|
||||||
max_light += lights_at(f_pos, f_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
|
max_light += lights_at(f_pos, f_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
|
||||||
|
|
||||||
|
@ -90,6 +90,24 @@ impl BlockKind {
|
|||||||
/// fields.
|
/// fields.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn has_color(&self) -> bool { self.is_filled() }
|
pub const fn has_color(&self) -> bool { self.is_filled() }
|
||||||
|
|
||||||
|
/// Determine whether the block is 'terrain-like'. This definition is
|
||||||
|
/// arbitrary, but includes things like rocks, soils, sands, grass, and
|
||||||
|
/// other blocks that might be expected to the landscape. Plant matter and
|
||||||
|
/// snow are *not* included.
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_terrain(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
BlockKind::Rock
|
||||||
|
| BlockKind::WeakRock
|
||||||
|
| BlockKind::GlowingRock
|
||||||
|
| BlockKind::GlowingWeakRock
|
||||||
|
| BlockKind::Grass
|
||||||
|
| BlockKind::Earth
|
||||||
|
| BlockKind::Sand
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -198,7 +198,7 @@ pub fn init(
|
|||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
SiteKind::Refactor(site2) => {
|
SiteKind::Refactor(site2) => {
|
||||||
for _ in 0..(site.economy.pop as usize).min(site2.plots().len() * 3) {
|
for _ in 0..site.economy.pop.min(site2.plots().len() as f32 * 1.5) as usize {
|
||||||
rtsim.entities.insert(Entity {
|
rtsim.entities.insert(Entity {
|
||||||
is_loaded: false,
|
is_loaded: false,
|
||||||
pos: site2
|
pos: site2
|
||||||
@ -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 {
|
rtsim.entities.insert(Entity {
|
||||||
is_loaded: false,
|
is_loaded: false,
|
||||||
pos: site2
|
pos: site2
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
all::ForestKind,
|
all::ForestKind,
|
||||||
sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim},
|
sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim},
|
||||||
|
site::SpawnRules,
|
||||||
util::{RandomField, Sampler},
|
util::{RandomField, Sampler},
|
||||||
IndexRef, CONFIG,
|
IndexRef, CONFIG,
|
||||||
};
|
};
|
||||||
@ -109,6 +110,11 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
let neighbor_chunk = sim.get(neighbor_pos)?;
|
let neighbor_chunk = sim.get(neighbor_pos)?;
|
||||||
Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river))
|
Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river))
|
||||||
});
|
});
|
||||||
|
let spawn_rules = sim_chunk
|
||||||
|
.sites
|
||||||
|
.iter()
|
||||||
|
.map(|site| index.sites[*site].spawn_rules(wpos))
|
||||||
|
.fold(SpawnRules::default(), |a, b| a.combine(b));
|
||||||
|
|
||||||
let gradient = sim.get_gradient_approx(chunk_pos);
|
let gradient = sim.get_gradient_approx(chunk_pos);
|
||||||
|
|
||||||
@ -829,12 +835,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
// NOTE: To disable warp, uncomment this line.
|
// NOTE: To disable warp, uncomment this line.
|
||||||
// let warp_factor = 0.0;
|
// let warp_factor = 0.0;
|
||||||
|
|
||||||
let warp_factor = warp_factor
|
let warp_factor = warp_factor * spawn_rules.max_warp;
|
||||||
* sim_chunk
|
|
||||||
.sites
|
|
||||||
.iter()
|
|
||||||
.map(|site| index.sites[*site].spawn_rules(wpos).max_warp)
|
|
||||||
.fold(1.0f32, |a, b| a.min(b));
|
|
||||||
|
|
||||||
let riverless_alt_delta = Lerp::lerp(0.0, riverless_alt_delta, warp_factor);
|
let riverless_alt_delta = Lerp::lerp(0.0, riverless_alt_delta, warp_factor);
|
||||||
let alt = alt + riverless_alt_delta;
|
let alt = alt + riverless_alt_delta;
|
||||||
@ -1120,7 +1121,11 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
// dirt
|
// dirt
|
||||||
let ground = Lerp::lerp(ground, sub_surface_color, marble_mid * tree_density);
|
let ground = Lerp::lerp(ground, sub_surface_color, marble_mid * tree_density);
|
||||||
|
|
||||||
let path = sim.get_nearest_path(wpos);
|
let path = if spawn_rules.paths {
|
||||||
|
sim.get_nearest_path(wpos)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let cave = sim.get_nearest_cave(wpos);
|
let cave = sim.get_nearest_cave(wpos);
|
||||||
|
|
||||||
let ice_depth = if snow_factor < -0.25
|
let ice_depth = if snow_factor < -0.25
|
||||||
@ -1168,11 +1173,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
// No growing directly on bedrock.
|
// No growing directly on bedrock.
|
||||||
// And, no growing on sites that don't want them TODO: More precise than this when we
|
// And, no growing on sites that don't want them TODO: More precise than this when we
|
||||||
// apply trees as a post-processing layer
|
// apply trees as a post-processing layer
|
||||||
tree_density: if sim_chunk
|
tree_density: if spawn_rules.trees {
|
||||||
.sites
|
|
||||||
.iter()
|
|
||||||
.all(|site| index.sites[*site].spawn_rules(wpos).trees)
|
|
||||||
{
|
|
||||||
Lerp::lerp(0.0, tree_density, alt.sub(2.0).sub(basement).mul(0.5))
|
Lerp::lerp(0.0, tree_density, alt.sub(2.0).sub(basement).mul(0.5))
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
|
@ -21,15 +21,19 @@ impl<'a> Land<'a> {
|
|||||||
|
|
||||||
pub fn get_gradient_approx(&self, wpos: Vec2<i32>) -> f32 {
|
pub fn get_gradient_approx(&self, wpos: Vec2<i32>) -> f32 {
|
||||||
self.sim
|
self.sim
|
||||||
.and_then(|sim| {
|
.and_then(|sim| sim.get_gradient_approx(self.wpos_chunk_pos(wpos)))
|
||||||
sim.get_gradient_approx(
|
|
||||||
wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e.div_euclid(sz as i32)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or(0.0)
|
.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))
|
self.sim.and_then(|sim| sim.get_wpos(wpos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,19 @@ pub struct Colors {
|
|||||||
pub struct SpawnRules {
|
pub struct SpawnRules {
|
||||||
pub trees: bool,
|
pub trees: bool,
|
||||||
pub max_warp: f32,
|
pub max_warp: f32,
|
||||||
|
pub paths: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpawnRules {
|
||||||
|
#[must_use]
|
||||||
|
pub fn combine(self, other: Self) -> Self {
|
||||||
|
// Should be commutative
|
||||||
|
Self {
|
||||||
|
trees: self.trees && other.trees,
|
||||||
|
max_warp: self.max_warp.min(other.max_warp),
|
||||||
|
paths: self.paths && other.paths,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SpawnRules {
|
impl Default for SpawnRules {
|
||||||
@ -33,6 +46,7 @@ impl Default for SpawnRules {
|
|||||||
Self {
|
Self {
|
||||||
trees: true,
|
trees: true,
|
||||||
max_warp: 1.0,
|
max_warp: 1.0,
|
||||||
|
paths: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,13 +60,26 @@ impl Site {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
|
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
|
||||||
let not_near_things = SQUARE_9.iter().all(|&rpos| {
|
let tile_pos = self.wpos_tile_pos(wpos);
|
||||||
self.wpos_tile(wpos + rpos * tile::TILE_SIZE as i32)
|
let max_warp = SQUARE_9
|
||||||
.is_empty()
|
.iter()
|
||||||
});
|
.filter_map(|rpos| {
|
||||||
|
let tile_pos = tile_pos + rpos;
|
||||||
|
if self.tiles.get(tile_pos).is_natural() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let clamped =
|
||||||
|
wpos.clamped(self.tile_wpos(tile_pos), self.tile_wpos(tile_pos + 1) - 1);
|
||||||
|
Some(clamped.distance_squared(wpos) as f32)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.min_by_key(|d2| *d2 as i32)
|
||||||
|
.map(|d2| d2.sqrt() as f32 / TILE_SIZE as f32)
|
||||||
|
.unwrap_or(1.0);
|
||||||
SpawnRules {
|
SpawnRules {
|
||||||
trees: not_near_things,
|
trees: max_warp == 1.0,
|
||||||
max_warp: if not_near_things { 1.0 } else { 0.0 },
|
max_warp,
|
||||||
|
paths: max_warp > std::f32::EPSILON,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +222,7 @@ impl Site {
|
|||||||
|
|
||||||
pub fn make_plaza(&mut self, land: &Land, rng: &mut impl Rng) -> Id<Plot> {
|
pub fn make_plaza(&mut self, land: &Land, rng: &mut impl Rng) -> Id<Plot> {
|
||||||
let plaza_radius = rng.gen_range(1..4);
|
let plaza_radius = rng.gen_range(1..4);
|
||||||
let plaza_dist = 10.0 + plaza_radius as f32 * 5.0;
|
let plaza_dist = 6.5 + plaza_radius as f32 * 4.0;
|
||||||
let pos = attempt(32, || {
|
let pos = attempt(32, || {
|
||||||
self.plazas
|
self.plazas
|
||||||
.choose(rng)
|
.choose(rng)
|
||||||
@ -288,7 +301,10 @@ impl Site {
|
|||||||
Spiral2d::new()
|
Spiral2d::new()
|
||||||
.take((SEARCH_RADIUS * 2 + 1).pow(2) as usize)
|
.take((SEARCH_RADIUS * 2 + 1).pow(2) as usize)
|
||||||
.for_each(|tile| {
|
.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 {
|
for &rpos in &SQUARE_4 {
|
||||||
// `get_mut` doesn't increase generation bounds
|
// `get_mut` doesn't increase generation bounds
|
||||||
self.tiles
|
self.tiles
|
||||||
@ -369,7 +385,7 @@ impl Site {
|
|||||||
match *build_chance.choose_seeded(rng.gen()) {
|
match *build_chance.choose_seeded(rng.gen()) {
|
||||||
// House
|
// House
|
||||||
1 => {
|
1 => {
|
||||||
let size = (2.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
|
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, || {
|
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
|
||||||
site.find_roadside_aabr(
|
site.find_roadside_aabr(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
@ -696,19 +712,24 @@ impl Site {
|
|||||||
|
|
||||||
if dist.map_or(false, |d| d <= 1.5) {
|
if dist.map_or(false, |d| d <= 1.5) {
|
||||||
let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
|
let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
|
||||||
(-8..6).for_each(|z| {
|
let mut underground = true;
|
||||||
|
for z in -8..6 {
|
||||||
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, alt + z), |b| {
|
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, alt + z), |b| {
|
||||||
if z >= 0 {
|
if b.kind() == BlockKind::Snow {
|
||||||
if b.is_filled() {
|
underground = false;
|
||||||
Block::empty()
|
b.into_vacant()
|
||||||
|
} else if b.is_filled() {
|
||||||
|
if b.is_terrain() {
|
||||||
|
Block::new(BlockKind::Earth, Rgb::new(0x6A, 0x47, 0x24))
|
||||||
} else {
|
} else {
|
||||||
b.with_sprite(SpriteKind::Empty)
|
b
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Earth, Rgb::new(0x6A, 0x47, 0x24))
|
underground = false;
|
||||||
|
b.into_vacant()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -761,23 +782,32 @@ impl Site {
|
|||||||
|
|
||||||
if min_dist.is_some() {
|
if min_dist.is_some() {
|
||||||
let alt = /*avg_hard_alt.map(|(sum, weight)| sum / weight).unwrap_or_else(||*/ canvas.col(wpos2d).map_or(0.0, |col| col.alt)/*)*/ as i32;
|
let alt = /*avg_hard_alt.map(|(sum, weight)| sum / weight).unwrap_or_else(||*/ canvas.col(wpos2d).map_or(0.0, |col| col.alt)/*)*/ as i32;
|
||||||
(-6..4).for_each(|z| canvas.map(
|
let mut underground = true;
|
||||||
Vec3::new(wpos2d.x, wpos2d.y, alt + z),
|
for z in -6..4 {
|
||||||
|b| if z > 0 {
|
canvas.map(
|
||||||
let sprite = if z == 1 && self.tile_wpos(tpos) == wpos2d && (tpos + tpos.yx() / 2) % 2 == Vec2::zero() {
|
Vec3::new(wpos2d.x, wpos2d.y, alt + z),
|
||||||
SpriteKind::StreetLamp
|
|b| {
|
||||||
} else {
|
let sprite = if underground && self.tile_wpos(tpos) == wpos2d && (tpos + tpos.yx() / 2) % 2 == Vec2::zero() {
|
||||||
SpriteKind::Empty
|
SpriteKind::StreetLamp
|
||||||
};
|
} else {
|
||||||
if b.is_filled() {
|
SpriteKind::Empty
|
||||||
Block::air(sprite)
|
};
|
||||||
} else {
|
if b.kind() == BlockKind::Snow {
|
||||||
b.with_sprite(sprite)
|
underground = false;
|
||||||
}
|
b.into_vacant().with_sprite(sprite)
|
||||||
} else {
|
} else if b.is_filled() {
|
||||||
Block::new(BlockKind::Earth, Rgb::new(0x6A, 0x47, 0x24))
|
if b.is_terrain() {
|
||||||
},
|
Block::new(BlockKind::Earth, Rgb::new(0x6A, 0x47, 0x24))
|
||||||
));
|
} else {
|
||||||
|
b
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
underground = false;
|
||||||
|
b.into_vacant().with_sprite(sprite)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile = self.wpos_tile(wpos2d);
|
let tile = self.wpos_tile(wpos2d);
|
||||||
@ -916,7 +946,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> {
|
fn wpos_is_hazard(land: &Land, wpos: Vec2<i32>) -> Option<HazardKind> {
|
||||||
if land
|
if land
|
||||||
.get_chunk_at(wpos)
|
.get_chunk_wpos(wpos)
|
||||||
.map_or(true, |c| c.river.near_water())
|
.map_or(true, |c| c.river.near_water())
|
||||||
{
|
{
|
||||||
Some(HazardKind::Water)
|
Some(HazardKind::Water)
|
||||||
|
@ -1423,7 +1423,7 @@ impl SiteStructure for Dungeon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let biome = land
|
let biome = land
|
||||||
.get_chunk_at(self.origin)
|
.get_chunk_wpos(self.origin)
|
||||||
.map_or(BiomeKind::Void, |c| c.get_biome());
|
.map_or(BiomeKind::Void, |c| c.get_biome());
|
||||||
let entrances = match biome {
|
let entrances = match biome {
|
||||||
BiomeKind::Jungle => *JUNGLE,
|
BiomeKind::Jungle => *JUNGLE,
|
||||||
|
@ -856,6 +856,40 @@ impl Structure for House {
|
|||||||
painter.prim(Primitive::intersect(walls, windows)),
|
painter.prim(Primitive::intersect(walls, windows)),
|
||||||
Fill::Block(Block::air(SpriteKind::Window1).with_ori(2).unwrap()),
|
Fill::Block(Block::air(SpriteKind::Window1).with_ori(2).unwrap()),
|
||||||
);
|
);
|
||||||
|
// Wall lamps
|
||||||
|
if i == 1 {
|
||||||
|
let mut torches_min = painter.prim(Primitive::Empty);
|
||||||
|
let mut torches_max = painter.prim(Primitive::Empty);
|
||||||
|
for y in self.tile_aabr.min.y..self.tile_aabr.max.y {
|
||||||
|
let pos = site
|
||||||
|
.tile_wpos(Vec2::new(self.tile_aabr.min.x, y))
|
||||||
|
.with_z(alt + previous_height + 3)
|
||||||
|
+ Vec3::new(-1, 0, 0);
|
||||||
|
let torch = painter.prim(Primitive::Aabb(Aabb {
|
||||||
|
min: pos,
|
||||||
|
max: pos + 1,
|
||||||
|
}));
|
||||||
|
torches_min = painter.prim(Primitive::union(torches_min, torch));
|
||||||
|
|
||||||
|
let pos = site
|
||||||
|
.tile_wpos(Vec2::new(self.tile_aabr.max.x, y + 1))
|
||||||
|
.with_z(alt + previous_height + 3)
|
||||||
|
+ Vec3::new(1, 0, 0);
|
||||||
|
let torch = painter.prim(Primitive::Aabb(Aabb {
|
||||||
|
min: pos,
|
||||||
|
max: pos + 1,
|
||||||
|
}));
|
||||||
|
torches_max = painter.prim(Primitive::union(torches_max, torch));
|
||||||
|
}
|
||||||
|
painter.fill(
|
||||||
|
torches_min,
|
||||||
|
Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(6).unwrap()),
|
||||||
|
);
|
||||||
|
painter.fill(
|
||||||
|
torches_max,
|
||||||
|
Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(2).unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Windows y axis
|
// Windows y axis
|
||||||
{
|
{
|
||||||
@ -886,6 +920,40 @@ impl Structure for House {
|
|||||||
painter.prim(Primitive::intersect(walls, windows)),
|
painter.prim(Primitive::intersect(walls, windows)),
|
||||||
Fill::Block(Block::air(SpriteKind::Window1).with_ori(0).unwrap()),
|
Fill::Block(Block::air(SpriteKind::Window1).with_ori(0).unwrap()),
|
||||||
);
|
);
|
||||||
|
// Wall lamps
|
||||||
|
if i == 1 {
|
||||||
|
let mut torches_min = painter.prim(Primitive::Empty);
|
||||||
|
let mut torches_max = painter.prim(Primitive::Empty);
|
||||||
|
for x in self.tile_aabr.min.x..self.tile_aabr.max.x {
|
||||||
|
let pos = site
|
||||||
|
.tile_wpos(Vec2::new(x + 1, self.tile_aabr.min.y))
|
||||||
|
.with_z(alt + previous_height + 3)
|
||||||
|
+ Vec3::new(0, -1, 0);
|
||||||
|
let torch = painter.prim(Primitive::Aabb(Aabb {
|
||||||
|
min: pos,
|
||||||
|
max: pos + 1,
|
||||||
|
}));
|
||||||
|
torches_min = painter.prim(Primitive::union(torches_min, torch));
|
||||||
|
|
||||||
|
let pos = site
|
||||||
|
.tile_wpos(Vec2::new(x, self.tile_aabr.max.y))
|
||||||
|
.with_z(alt + previous_height + 3)
|
||||||
|
+ Vec3::new(0, 1, 0);
|
||||||
|
let torch = painter.prim(Primitive::Aabb(Aabb {
|
||||||
|
min: pos,
|
||||||
|
max: pos + 1,
|
||||||
|
}));
|
||||||
|
torches_max = painter.prim(Primitive::union(torches_max, torch));
|
||||||
|
}
|
||||||
|
painter.fill(
|
||||||
|
torches_min,
|
||||||
|
Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(0).unwrap()),
|
||||||
|
);
|
||||||
|
painter.fill(
|
||||||
|
torches_max,
|
||||||
|
Fill::Block(Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shed roof on negative overhangs
|
// Shed roof on negative overhangs
|
||||||
|
@ -52,8 +52,8 @@ impl Structure for Workshop {
|
|||||||
|
|
||||||
painter
|
painter
|
||||||
.aabb(Aabb {
|
.aabb(Aabb {
|
||||||
min: self.bounds.min.with_z(base),
|
min: (self.bounds.min + 2).with_z(base),
|
||||||
max: self.bounds.max.with_z(roof),
|
max: (self.bounds.max - 2).with_z(roof),
|
||||||
})
|
})
|
||||||
.clear();
|
.clear();
|
||||||
|
|
||||||
|
@ -216,6 +216,8 @@ impl Tile {
|
|||||||
|
|
||||||
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
|
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
|
||||||
|
|
||||||
|
pub fn is_natural(&self) -> bool { matches!(self.kind, TileKind::Empty | TileKind::Hazard(_)) }
|
||||||
|
|
||||||
pub fn is_road(&self) -> bool { matches!(self.kind, TileKind::Plaza | TileKind::Road { .. }) }
|
pub fn is_road(&self) -> bool { matches!(self.kind, TileKind::Plaza | TileKind::Road { .. }) }
|
||||||
|
|
||||||
pub fn is_obstacle(&self) -> bool {
|
pub fn is_obstacle(&self) -> bool {
|
||||||
|
Loading…
Reference in New Issue
Block a user