use super::*;
use crate::{
assets::AssetHandle,
site2::gen::PrimitiveTransform,
util::{RandomField, Sampler, DIAGONALS, NEIGHBORS},
Land,
};
use common::{
generation::EntityInfo,
terrain::{BlockKind, SpriteKind, Structure as PrefabStructure, StructuresGroup},
};
use lazy_static::lazy_static;
use rand::prelude::*;
use std::{f32::consts::TAU, sync::Arc};
use vek::*;
pub struct TerracottaHouse {
bounds: Aabr<i32>,
pub(crate) alt: i32,
}
impl TerracottaHouse {
pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
let bounds = Aabr {
min: site.tile_wpos(tile_aabr.min),
max: site.tile_wpos(tile_aabr.max),
};
Self {
bounds,
alt: land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
as i32
+ 2,
}
}
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules {
waypoints: false,
trees: wpos.distance_squared(self.bounds.center()) > (85_i32).pow(2),
..SpawnRules::default()
}
}
}
impl Structure for TerracottaHouse {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_terracotta_house\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_terracotta_house")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.alt + 3;
let center = self.bounds.center();
let mut rng = thread_rng();
let clay_broken = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 42 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
36..=38 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
_ => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
})
}));
let clay_unbroken = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 40 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
_ => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
})
}));
let grass_fill = Fill::Sampling(Arc::new(|wpos| {
Some(match (RandomField::new(0).get(wpos)) % 20 {
1..=2 => Block::air(SpriteKind::JungleRedGrass),
3..=7 => Block::air(SpriteKind::JungleLeafyPlant),
8 => Block::air(SpriteKind::JungleFern),
_ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
})
}));
let roof_color = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 400 {
0..=4 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
5..=9 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
10..=14 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
15..=19 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
20..=21 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
22..=23 => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
_ => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
})
}));
let sand = Fill::Brick(BlockKind::Misc, Rgb::new(235, 178, 99), 12);
let size = 30;
let room_size = 15 * (size / 10);
let roof_size = 16 * (size / 10);
let carve_size = 14 * (size / 10) + 2;
let roof_height = room_size / 3;
let storeys = 5;
let var = size / 5;
let clear_var = var / 2;
let clear_limit = painter.aabb(Aabb {
min: (center - (room_size / 2) - 2).with_z(base),
max: (center + (room_size / 2) + 2).with_z(base + (2 * room_size)),
});
let clear_limit_down = painter.aabb(Aabb {
min: (center - (room_size / 2) - 5).with_z(base - (2 * room_size)),
max: (center + (room_size / 2) + 5).with_z(base),
});
let decay = RandomField::new(0).get(center.with_z(base)) % 2;
let model_radius = (room_size / 2) + 4;
for dir in DIAGONALS {
let pos = center + dir * model_radius;
painter
.cylinder(Aabb {
min: (pos - 10).with_z(base - room_size),
max: (pos + 10).with_z(base - 3),
})
.fill(clay_unbroken.clone());
painter
.cylinder(Aabb {
min: (pos - 10).with_z(base - 4),
max: (pos + 10).with_z(base - 3),
})
.fill(clay_broken.clone());
painter
.cylinder(Aabb {
min: (pos - 9).with_z(base - 4),
max: (pos + 9).with_z(base - 3),
})
.fill(sand.clone());
painter
.cylinder(Aabb {
min: (pos - 7).with_z(base - 3),
max: (pos + 7).with_z(base - 2),
})
.fill(grass_fill.clone());
let model_pos = pos.with_z(base - 5);
match RandomField::new(0).get(model_pos) % 2 {
0 => {
lazy_static! {
pub static ref MODEL: AssetHandle<StructuresGroup> =
PrefabStructure::load_group(
"site_structures.terracotta.terracotta_decor_small"
);
}
let rng = RandomField::new(0).get(model_pos) % 62;
let model = MODEL.read();
let model = model[rng as usize % model.len()].clone();
painter
.prim(Primitive::Prefab(Box::new(model.clone())))
.translate(model_pos)
.fill(Fill::Prefab(Box::new(model), model_pos, rng));
},
_ => {
lazy_static! {
pub static ref MODEL: AssetHandle<StructuresGroup> =
PrefabStructure::load_group("trees.palms");
}
let rng = RandomField::new(0).get(model_pos) % 62;
let model = MODEL.read();
let model = model[rng as usize % model.len()].clone();
painter
.prim(Primitive::Prefab(Box::new(model.clone())))
.translate(model_pos)
.fill(Fill::Prefab(Box::new(model), model_pos, rng));
},
}
}
painter
.superquadric(
Aabb {
min: (center - (room_size / 2) - 10).with_z(base - room_size - 15),
max: (center + (room_size / 2) + 10).with_z(base + 5),
},
2.5,
)
.fill(clay_unbroken.clone());
painter
.superquadric(
Aabb {
min: (center - (room_size / 2)).with_z(base - (room_size / 2)),
max: (center + (room_size / 2)).with_z(base + (room_size / 2)),
},
2.5,
)
.fill(clay_broken.clone());
painter
.superquadric(
Aabb {
min: (center - (room_size / 2)).with_z(base - (room_size / 2)),
max: (center + (room_size / 2)).with_z(base + (room_size / 2)),
},
2.5,
)
.intersect(clear_limit_down)
.fill(clay_unbroken.clone());
for s in 0..storeys {
painter
.superquadric(
Aabb {
min: (center - (roof_size / 2) + (s * var)).with_z(
base + roof_height - (size / 4) + (s * (roof_size / 4)) + (s * var),
),
max: (center + (roof_size / 2) - (s * var)).with_z(
base + roof_height - (size / 4) + roof_size + (s * (roof_size / 4))
- (s * var),
),
},
2.5,
)
.fill(clay_broken.clone());
painter
.superquadric(
Aabb {
min: (center - (roof_size / 2) + (s * var) + 1).with_z(
base + roof_height - (size / 4) + (s * (roof_size / 4)) + (s * var) + 1,
),
max: (center + (roof_size / 2) - (s * var) - 1).with_z(
base + roof_height - (size / 4) + roof_size + (s * (roof_size / 4))
- (s * var)
- 1,
),
},
2.5,
)
.fill(roof_color.clone());
for dir in CARDINALS {
let pos = center + dir * (size - (s * var));
painter
.superquadric(
Aabb {
min: (pos - (carve_size / 2) + (s * clear_var)).with_z(
base + roof_height + (s * (roof_size / 4)) + (s * clear_var),
),
max: (pos + (carve_size / 2) - (s * clear_var)).with_z(
base + roof_height + carve_size + (s * (roof_size / 4))
- (s * clear_var),
),
},
2.5,
)
.intersect(clear_limit)
.clear();
}
}
painter
.superquadric(
Aabb {
min: (center - (room_size / 2) + 5).with_z(base - (room_size / 2) + 5),
max: (center + (room_size / 2) - 5).with_z(base + (room_size / 2) - 5),
},
2.5,
)
.union(
painter
.superquadric(
Aabb {
min: Vec2::new(
center.x - (room_size / 4),
center.y - (3 * room_size / 4),
)
.with_z(base - (room_size / 4)),
max: Vec2::new(
center.x + (room_size / 4),
center.y + (3 * room_size / 4),
)
.with_z(base + (room_size / 4)),
},
2.5,
)
.union(
painter.superquadric(
Aabb {
min: Vec2::new(
center.x - (3 * room_size / 4),
center.y - (room_size / 4),
)
.with_z(base - (room_size / 4)),
max: Vec2::new(
center.x + (3 * room_size / 4),
center.y + (room_size / 4),
)
.with_z(base + (room_size / 4)),
},
2.5,
),
),
)
.intersect(clear_limit)
.clear();
painter
.superquadric(
Aabb {
min: (center - (room_size / 3))
.with_z(base + (3 * (room_size / 10)) - (room_size / 3)),
max: (center + (room_size / 3))
.with_z(base + (3 * (room_size / 10)) + (room_size / 4)),
},
2.5,
)
.clear();
painter
.superquadric(
Aabb {
min: (center - (room_size / 4))
.with_z(base + (3 * (room_size / 10)) - (room_size / 4)),
max: (center + (room_size / 4))
.with_z(base + (3 * (room_size / 10)) + (room_size / 3)),
},
2.5,
)
.clear();
painter
.cylinder(Aabb {
min: (center - (room_size / 2) + 1).with_z(base - 1),
max: (center + (room_size / 2) - 1).with_z(base),
})
.fill(clay_unbroken.clone());
let radius_guards = (room_size / 4) + 4;
let guards = 4.0_f32;
let phi_guards = TAU / guards;
for n in 1..=guards as i32 {
let pos = Vec2::new(
center.x + (radius_guards as f32 * ((n as f32 * phi_guards).cos())) as i32,
center.y + (radius_guards as f32 * ((n as f32 * phi_guards).sin())) as i32,
)
.with_z(base);
terracotta_palace::spawn_random_entity(pos, painter, 1..=1);
}
if decay == 0 {
for dir in CARDINALS {
let pos = center + dir * ((room_size / 4) + 4);
painter.sprite(pos.with_z(base), SpriteKind::TerracottaStatue);
}
painter
.cylinder(Aabb {
min: (center - (room_size / 6)).with_z(base - 12),
max: (center + (room_size / 6)).with_z(base),
})
.clear();
painter
.cylinder(Aabb {
min: (center - (room_size / 6)).with_z(base - 13),
max: (center + (room_size / 6)).with_z(base - 12),
})
.fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
painter
.cylinder(Aabb {
min: (center - (room_size / 6)).with_z(base - 1),
max: (center + (room_size / 6)).with_z(base),
})
.fill(Fill::Block(Block::air(SpriteKind::TerracottaBlock)));
painter
.cylinder(Aabb {
min: (center).with_z(base - 14),
max: (center + 1).with_z(base),
})
.fill(clay_unbroken);
painter.spawn(EntityInfo::at(center.with_z(base).as_()).with_asset_expect(
"common.entity.dungeon.terracotta.terracotta_statue_key_chance",
&mut rng,
None,
));
} else {
let decay_limiter = painter.aabb(Aabb {
min: (center - (room_size / 2)).with_z(base + 4),
max: (center + (room_size / 2)).with_z(base + (5 * roof_height)),
});
for dir in NEIGHBORS {
let carve_pos = center + dir * room_size;
let decay_var = RandomField::new(0).get(carve_pos.with_z(base)) % 15;
painter
.line(
carve_pos.with_z(base),
center.with_z(base + (5 * roof_height)),
10.0 + decay_var as f32,
)
.intersect(decay_limiter)
.clear();
}
}
}
}