mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/site2' into 'master'
Added rtsim villagers to site2 towns See merge request veloren/veloren!2857
This commit is contained in:
commit
715ff845a4
@ -9,4 +9,5 @@
|
|||||||
scatter: true,
|
scatter: true,
|
||||||
paths: true,
|
paths: true,
|
||||||
spots: true,
|
spots: true,
|
||||||
|
site2: false,
|
||||||
)
|
)
|
||||||
|
@ -137,7 +137,7 @@ impl assets::Asset for EntityConfig {
|
|||||||
impl EntityConfig {
|
impl EntityConfig {
|
||||||
pub fn from_asset_expect(asset_specifier: &str) -> Self {
|
pub fn from_asset_expect(asset_specifier: &str) -> Self {
|
||||||
Self::load_owned(asset_specifier)
|
Self::load_owned(asset_specifier)
|
||||||
.unwrap_or_else(|e| panic!("Failed to load {}. Error: {}", asset_specifier, e))
|
.unwrap_or_else(|e| panic!("Failed to load {}. Error: {:?}", asset_specifier, e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_body(mut self, body: BodyBuilder) -> Self {
|
pub fn with_body(mut self, body: BodyBuilder) -> Self {
|
||||||
|
@ -59,9 +59,9 @@ impl<T> Store<T> {
|
|||||||
(0..self.items.len()).map(|i| Id(i as u64, PhantomData))
|
(0..self.items.len()).map(|i| Id(i as u64, PhantomData))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn values(&self) -> impl Iterator<Item = &T> { self.items.iter() }
|
pub fn values(&self) -> impl ExactSizeIterator<Item = &T> { self.items.iter() }
|
||||||
|
|
||||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> { self.items.iter_mut() }
|
pub fn values_mut(&mut self) -> impl ExactSizeIterator<Item = &mut T> { self.items.iter_mut() }
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (Id<T>, &T)> { self.ids().zip(self.values()) }
|
pub fn iter(&self) -> impl Iterator<Item = (Id<T>, &T)> { self.ids().zip(self.values()) }
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@ pub struct Entity {
|
|||||||
pub enum RtSimEntityKind {
|
pub enum RtSimEntityKind {
|
||||||
Random,
|
Random,
|
||||||
Cultist,
|
Cultist,
|
||||||
|
Villager,
|
||||||
|
Merchant,
|
||||||
}
|
}
|
||||||
|
|
||||||
const BIRD_LARGE_ROSTER: &[comp::bird_large::Species] = &[
|
const BIRD_LARGE_ROSTER: &[comp::bird_large::Species] = &[
|
||||||
@ -44,6 +46,7 @@ const PERM_BODY: u32 = 1;
|
|||||||
const PERM_LOADOUT: u32 = 2;
|
const PERM_LOADOUT: u32 = 2;
|
||||||
const PERM_LEVEL: u32 = 3;
|
const PERM_LEVEL: u32 = 3;
|
||||||
const PERM_GENUS: u32 = 4;
|
const PERM_GENUS: u32 = 4;
|
||||||
|
const PERM_TRADE: u32 = 5;
|
||||||
|
|
||||||
impl Entity {
|
impl Entity {
|
||||||
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed + perm) }
|
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed + perm) }
|
||||||
@ -78,7 +81,7 @@ impl Entity {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RtSimEntityKind::Cultist => {
|
RtSimEntityKind::Cultist | RtSimEntityKind::Villager | RtSimEntityKind::Merchant => {
|
||||||
let species = *(&comp::humanoid::ALL_SPECIES)
|
let species = *(&comp::humanoid::ALL_SPECIES)
|
||||||
.choose(&mut self.rng(PERM_SPECIES))
|
.choose(&mut self.rng(PERM_SPECIES))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -87,6 +90,29 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_trade_info(
|
||||||
|
&self,
|
||||||
|
world: &World,
|
||||||
|
index: &world::IndexOwned,
|
||||||
|
) -> Option<trade::SiteInformation> {
|
||||||
|
let site = match self.kind {
|
||||||
|
/*
|
||||||
|
// Travelling merchants (don't work for some reason currently)
|
||||||
|
RtSimEntityKind::Random if self.rng(PERM_TRADE).gen_bool(0.5) => {
|
||||||
|
match self.brain.route {
|
||||||
|
Travel::Path { target_id, .. } => Some(target_id),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
RtSimEntityKind::Merchant => self.brain.begin_site(),
|
||||||
|
_ => None,
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let site = world.civs().sites[site].site_tmp?;
|
||||||
|
index.sites[site].trade_information(site.id())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_entity_config(&self) -> &str {
|
pub fn get_entity_config(&self) -> &str {
|
||||||
match self.get_body() {
|
match self.get_body() {
|
||||||
comp::Body::Humanoid(_) => humanoid_config(self.kind),
|
comp::Body::Humanoid(_) => humanoid_config(self.kind),
|
||||||
@ -104,12 +130,17 @@ impl Entity {
|
|||||||
&self,
|
&self,
|
||||||
) -> fn(LoadoutBuilder, Option<&trade::SiteInformation>) -> LoadoutBuilder {
|
) -> fn(LoadoutBuilder, Option<&trade::SiteInformation>) -> LoadoutBuilder {
|
||||||
let body = self.get_body();
|
let body = self.get_body();
|
||||||
let kind = &self.kind;
|
let kind = self.kind;
|
||||||
|
|
||||||
// give potions to traveler humanoids or return loadout as is otherwise
|
// give potions to traveler humanoids or return loadout as is otherwise
|
||||||
if let (comp::Body::Humanoid(_), RtSimEntityKind::Random) = (body, kind) {
|
match (body, kind) {
|
||||||
|l, _| l.bag(ArmorSlot::Bag1, Some(make_potion_bag(100)))
|
(comp::Body::Humanoid(_), RtSimEntityKind::Random) => {
|
||||||
} else {
|
|l, _| l.bag(ArmorSlot::Bag1, Some(make_potion_bag(100)))
|
||||||
|l, _| l
|
},
|
||||||
|
(_, RtSimEntityKind::Merchant) => {
|
||||||
|
|l, trade| l.with_creator(world::site::settlement::merchant_loadout, trade)
|
||||||
|
},
|
||||||
|
_ => |l, _| l,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,6 +688,28 @@ impl Brain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn villager(home_id: Id<Site>) -> Self {
|
||||||
|
Self {
|
||||||
|
begin: Some(home_id),
|
||||||
|
tgt: None,
|
||||||
|
route: Travel::Idle,
|
||||||
|
last_visited: None,
|
||||||
|
memories: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merchant(home_id: Id<Site>) -> Self {
|
||||||
|
Self {
|
||||||
|
begin: Some(home_id),
|
||||||
|
tgt: None,
|
||||||
|
route: Travel::Idle,
|
||||||
|
last_visited: None,
|
||||||
|
memories: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn begin_site(&self) -> Option<Id<Site>> { self.begin }
|
||||||
|
|
||||||
pub fn add_memory(&mut self, memory: Memory) { self.memories.push(memory); }
|
pub fn add_memory(&mut self, memory: Memory) { self.memories.push(memory); }
|
||||||
|
|
||||||
pub fn forget_enemy(&mut self, to_forget: &str) {
|
pub fn forget_enemy(&mut self, to_forget: &str) {
|
||||||
@ -715,6 +768,8 @@ fn humanoid_config(kind: RtSimEntityKind) -> &'static str {
|
|||||||
match kind {
|
match kind {
|
||||||
RtSimEntityKind::Cultist => "common.entity.dungeon.tier-5.cultist",
|
RtSimEntityKind::Cultist => "common.entity.dungeon.tier-5.cultist",
|
||||||
RtSimEntityKind::Random => "common.entity.world.traveler",
|
RtSimEntityKind::Random => "common.entity.world.traveler",
|
||||||
|
RtSimEntityKind::Villager => "common.entity.village.villager",
|
||||||
|
RtSimEntityKind::Merchant => "common.entity.village.merchant",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +198,49 @@ pub fn init(
|
|||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
|
SiteKind::Refactor(site2) => {
|
||||||
|
for _ in 0..(site.economy.pop as usize).min(site2.plots().len() * 3) {
|
||||||
|
rtsim.entities.insert(Entity {
|
||||||
|
is_loaded: false,
|
||||||
|
pos: site2
|
||||||
|
.plots()
|
||||||
|
.choose(&mut thread_rng())
|
||||||
|
.map_or(site.get_origin(), |plot| {
|
||||||
|
site2.tile_center_wpos(plot.root_tile())
|
||||||
|
})
|
||||||
|
.with_z(0)
|
||||||
|
.map(|e| e as f32),
|
||||||
|
seed: thread_rng().gen(),
|
||||||
|
controller: RtSimController::default(),
|
||||||
|
last_time_ticked: 0.0,
|
||||||
|
kind: RtSimEntityKind::Villager,
|
||||||
|
brain: Brain::villager(site_id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..site2.plazas().len() * 3 {
|
||||||
|
rtsim.entities.insert(Entity {
|
||||||
|
is_loaded: false,
|
||||||
|
pos: site2
|
||||||
|
.plazas()
|
||||||
|
.choose(&mut thread_rng())
|
||||||
|
.map_or(site.get_origin(), |p| {
|
||||||
|
site2.tile_center_wpos(site2.plot(p).root_tile())
|
||||||
|
+ Vec2::new(
|
||||||
|
thread_rng().gen_range(-8..9),
|
||||||
|
thread_rng().gen_range(-8..9),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.with_z(0)
|
||||||
|
.map(|e| e as f32),
|
||||||
|
seed: thread_rng().gen(),
|
||||||
|
controller: RtSimController::default(),
|
||||||
|
last_time_ticked: 0.0,
|
||||||
|
kind: RtSimEntityKind::Merchant,
|
||||||
|
brain: Brain::merchant(site_id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,10 +135,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
let entity_config = EntityConfig::from_asset_expect(entity_config_path)
|
let entity_config = EntityConfig::from_asset_expect(entity_config_path)
|
||||||
.with_body(BodyBuilder::Exact(body));
|
.with_body(BodyBuilder::Exact(body));
|
||||||
|
|
||||||
let entity_info = EntityInfo::at(pos.0)
|
let mut entity_info = EntityInfo::at(pos.0)
|
||||||
.with_entity_config(entity_config, Some(entity_config_path))
|
.with_entity_config(entity_config, Some(entity_config_path))
|
||||||
.with_lazy_loadout(ad_hoc_loadout)
|
.with_lazy_loadout(ad_hoc_loadout)
|
||||||
.with_health_scaling(10);
|
.with_health_scaling(10);
|
||||||
|
// Merchants can be traded with
|
||||||
|
if let Some(economy) = entity.get_trade_info(&world, &index) {
|
||||||
|
entity_info = entity_info
|
||||||
|
.with_agent_mark(comp::agent::Mark::Merchant)
|
||||||
|
.with_economy(&economy);
|
||||||
|
}
|
||||||
match NpcData::from_entity_info(entity_info, &mut loadout_rng) {
|
match NpcData::from_entity_info(entity_info, &mut loadout_rng) {
|
||||||
NpcData::Data {
|
NpcData::Data {
|
||||||
pos,
|
pos,
|
||||||
|
@ -53,9 +53,15 @@ fn main() -> Result {
|
|||||||
for z in aabb.min.z..aabb.max.z {
|
for z in aabb.min.z..aabb.max.z {
|
||||||
let pos = Vec3::new(x, y, z);
|
let pos = Vec3::new(x, y, z);
|
||||||
|
|
||||||
if let Some(block) = fill.sample_at(&prim_tree, prim, pos, canvas) {
|
let _ = volume.map(pos, |block| {
|
||||||
let _ = volume.set(pos, block);
|
if let Some(block) =
|
||||||
}
|
fill.sample_at(&prim_tree, prim, pos, canvas, block)
|
||||||
|
{
|
||||||
|
block
|
||||||
|
} else {
|
||||||
|
block
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ fn main() {
|
|||||||
|
|
||||||
let mut focus = Vec2::<f32>::zero();
|
let mut focus = Vec2::<f32>::zero();
|
||||||
let mut zoom = 1.0;
|
let mut zoom = 1.0;
|
||||||
let colors = &**index.colors().read();
|
let colors = &*index.colors();
|
||||||
let features = &**index.features().read();
|
let features = &*index.features();
|
||||||
let index = IndexRef {
|
let index = IndexRef {
|
||||||
colors,
|
colors,
|
||||||
features,
|
features,
|
||||||
|
@ -108,7 +108,7 @@ impl Civs {
|
|||||||
attempt(5, || {
|
attempt(5, || {
|
||||||
let (kind, size) = match ctx.rng.gen_range(0..64) {
|
let (kind, size) = match ctx.rng.gen_range(0..64) {
|
||||||
0..=4 => (SiteKind::Castle, 3),
|
0..=4 => (SiteKind::Castle, 3),
|
||||||
// 5..=28 => (SiteKind::Refactor, 6),
|
5..=28 if index.features().site2 => (SiteKind::Refactor, 6),
|
||||||
29..=31 => (SiteKind::Tree, 4),
|
29..=31 => (SiteKind::Tree, 4),
|
||||||
_ => (SiteKind::Dungeon, 0),
|
_ => (SiteKind::Dungeon, 0),
|
||||||
};
|
};
|
||||||
|
@ -823,6 +823,13 @@ 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
|
||||||
|
* 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;
|
||||||
let basement =
|
let basement =
|
||||||
|
@ -82,6 +82,7 @@ pub struct Features {
|
|||||||
pub scatter: bool,
|
pub scatter: bool,
|
||||||
pub paths: bool,
|
pub paths: bool,
|
||||||
pub spots: bool,
|
pub spots: bool,
|
||||||
|
pub site2: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl assets::Asset for Features {
|
impl assets::Asset for Features {
|
||||||
|
@ -82,9 +82,9 @@ impl Index {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn colors(&self) -> AssetHandle<Arc<Colors>> { self.colors }
|
pub fn colors(&self) -> impl Deref<Target = Arc<Colors>> + '_ { self.colors.read() }
|
||||||
|
|
||||||
pub fn features(&self) -> AssetHandle<Arc<Features>> { self.features }
|
pub fn features(&self) -> impl Deref<Target = Arc<Features>> + '_ { self.features.read() }
|
||||||
|
|
||||||
pub fn get_site_prices(&self, site_id: SiteId) -> Option<SitePrices> {
|
pub fn get_site_prices(&self, site_id: SiteId) -> Option<SitePrices> {
|
||||||
self.sites
|
self.sites
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
)]
|
)]
|
||||||
#![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(bool_to_option, const_panic, label_break_value)]
|
#![feature(bool_to_option, const_panic, label_break_value, option_zip)]
|
||||||
|
|
||||||
mod all;
|
mod all;
|
||||||
mod block;
|
mod block;
|
||||||
|
@ -2,7 +2,7 @@ mod block_mask;
|
|||||||
mod castle;
|
mod castle;
|
||||||
pub mod economy;
|
pub mod economy;
|
||||||
pub mod namegen;
|
pub mod namegen;
|
||||||
mod settlement;
|
pub mod settlement;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
@ -25,10 +25,16 @@ pub struct Colors {
|
|||||||
|
|
||||||
pub struct SpawnRules {
|
pub struct SpawnRules {
|
||||||
pub trees: bool,
|
pub trees: bool,
|
||||||
|
pub max_warp: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SpawnRules {
|
impl Default for SpawnRules {
|
||||||
fn default() -> Self { Self { trees: true } }
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
trees: true,
|
||||||
|
max_warp: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Site {
|
pub struct Site {
|
||||||
@ -120,6 +126,26 @@ impl Site {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trade_information(
|
||||||
|
&self,
|
||||||
|
site_id: common::trade::SiteId,
|
||||||
|
) -> Option<common::trade::SiteInformation> {
|
||||||
|
match &self.kind {
|
||||||
|
SiteKind::Settlement(_) | SiteKind::Refactor(_) => {
|
||||||
|
Some(common::trade::SiteInformation {
|
||||||
|
id: site_id,
|
||||||
|
unconsumed_stock: self
|
||||||
|
.economy
|
||||||
|
.unconsumed_stock
|
||||||
|
.iter()
|
||||||
|
.map(|(g, a)| (g.into(), *a))
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_to<'a>(&'a self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
pub fn apply_to<'a>(&'a self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||||
let info = canvas.info();
|
let info = canvas.info();
|
||||||
let get_col = |wpos| info.col(wpos + info.wpos);
|
let get_col = |wpos| info.col(wpos + info.wpos);
|
||||||
@ -143,15 +169,9 @@ impl Site {
|
|||||||
) {
|
) {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
SiteKind::Settlement(s) => {
|
SiteKind::Settlement(s) => {
|
||||||
let economy = common::trade::SiteInformation {
|
let economy = self
|
||||||
id: site_id,
|
.trade_information(site_id)
|
||||||
unconsumed_stock: self
|
.expect("Settlement has no economy");
|
||||||
.economy
|
|
||||||
.unconsumed_stock
|
|
||||||
.iter()
|
|
||||||
.map(|(g, a)| (g.into(), *a))
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy)
|
s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy)
|
||||||
},
|
},
|
||||||
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement),
|
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement),
|
||||||
|
@ -1022,7 +1022,7 @@ fn humanoid(pos: Vec3<f32>, economy: &SiteInformation, dynamic_rng: &mut impl Rn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merchant_loadout(
|
pub fn merchant_loadout(
|
||||||
loadout_builder: LoadoutBuilder,
|
loadout_builder: LoadoutBuilder,
|
||||||
economy: Option<&trade::SiteInformation>,
|
economy: Option<&trade::SiteInformation>,
|
||||||
) -> LoadoutBuilder {
|
) -> LoadoutBuilder {
|
||||||
@ -1125,7 +1125,7 @@ fn ingredient_backpack(economy: Option<&trade::SiteInformation>, rng: &mut impl
|
|||||||
|
|
||||||
// `to_skip` is ideologicaly boolean flag either to start from 0-th or 1-th slot
|
// `to_skip` is ideologicaly boolean flag either to start from 0-th or 1-th slot
|
||||||
let mut to_skip = 0;
|
let mut to_skip = 0;
|
||||||
if let Some(coins) = coins {
|
if let Some(coins) = coins.filter(|c| *c > 0) {
|
||||||
let mut coin_item = Item::new_from_asset_expect("common.items.utility.coins");
|
let mut coin_item = Item::new_from_asset_expect("common.items.utility.coins");
|
||||||
coin_item
|
coin_item
|
||||||
.set_amount(coins)
|
.set_amount(coins)
|
||||||
|
@ -38,6 +38,7 @@ impl Tree {
|
|||||||
let trunk_radius = 48i32;
|
let trunk_radius = 48i32;
|
||||||
SpawnRules {
|
SpawnRules {
|
||||||
trees: wpos.distance_squared(self.origin) > trunk_radius.pow(2),
|
trees: wpos.distance_squared(self.origin) > trunk_radius.pow(2),
|
||||||
|
..SpawnRules::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::{cell::RefCell, sync::Arc};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -39,8 +39,8 @@ pub enum Primitive {
|
|||||||
Cone(Aabb<i32>),
|
Cone(Aabb<i32>),
|
||||||
Sphere(Aabb<i32>),
|
Sphere(Aabb<i32>),
|
||||||
Plane(Aabr<i32>, Vec3<i32>, Vec2<f32>),
|
Plane(Aabr<i32>, Vec3<i32>, Vec2<f32>),
|
||||||
/// A line segment from start to finish point
|
/// A line segment from start to finish point with a given radius
|
||||||
Segment(LineSegment3<i32>),
|
Segment(LineSegment3<i32>, f32),
|
||||||
/// A sampling function is always a subset of another primitive to avoid
|
/// A sampling function is always a subset of another primitive to avoid
|
||||||
/// needing infinite bounds
|
/// needing infinite bounds
|
||||||
Sampling(Id<Primitive>, Box<dyn Fn(Vec3<i32>) -> bool>),
|
Sampling(Id<Primitive>, Box<dyn Fn(Vec3<i32>) -> bool>),
|
||||||
@ -58,8 +58,43 @@ pub enum Primitive {
|
|||||||
Scale(Id<Primitive>, Vec3<f32>),
|
Scale(Id<Primitive>, Vec3<f32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Primitive {
|
||||||
|
pub fn and(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
|
||||||
|
Self::And(a.into(), b.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
|
||||||
|
Self::Or(a.into(), b.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xor(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
|
||||||
|
Self::Xor(a.into(), b.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diff(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
|
||||||
|
Self::Diff(a.into(), b.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sampling(a: impl Into<Id<Primitive>>, f: Box<dyn Fn(Vec3<i32>) -> bool>) -> Self {
|
||||||
|
Self::Sampling(a.into(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate(a: impl Into<Id<Primitive>>, rot: Mat3<i32>) -> Self {
|
||||||
|
Self::Rotate(a.into(), rot)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(a: impl Into<Id<Primitive>>, trans: Vec3<i32>) -> Self {
|
||||||
|
Self::Translate(a.into(), trans)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale(a: impl Into<Id<Primitive>>, scale: Vec3<f32>) -> Self {
|
||||||
|
Self::Scale(a.into(), scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Fill {
|
pub enum Fill {
|
||||||
|
Sprite(SpriteKind),
|
||||||
Block(Block),
|
Block(Block),
|
||||||
Brick(BlockKind, Rgb<u8>, u8),
|
Brick(BlockKind, Rgb<u8>, u8),
|
||||||
// TODO: the offset field for Prefab is a hack that breaks the compositionality of Translate,
|
// TODO: the offset field for Prefab is a hack that breaks the compositionality of Translate,
|
||||||
@ -180,11 +215,12 @@ impl Fill {
|
|||||||
.as_()
|
.as_()
|
||||||
.dot(*gradient) as i32)
|
.dot(*gradient) as i32)
|
||||||
},
|
},
|
||||||
Primitive::Segment(segment) => {
|
Primitive::Segment(segment, radius) => {
|
||||||
(segment.start.x..segment.end.x).contains(&pos.x)
|
/*(segment.start.x..segment.end.x).contains(&pos.x)
|
||||||
&& (segment.start.y..segment.end.y).contains(&pos.y)
|
&& (segment.start.y..segment.end.y).contains(&pos.y)
|
||||||
&& (segment.start.z..segment.end.z).contains(&pos.z)
|
&& (segment.start.z..segment.end.z).contains(&pos.z)
|
||||||
&& segment.as_().distance_to_point(pos.map(|e| e as f32)) < 0.75
|
&&*/
|
||||||
|
segment.as_().distance_to_point(pos.map(|e| e as f32)) < radius - 0.25
|
||||||
},
|
},
|
||||||
Primitive::Sampling(a, f) => self.contains_at(tree, *a, pos) && f(pos),
|
Primitive::Sampling(a, f) => self.contains_at(tree, *a, pos) && f(pos),
|
||||||
Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)),
|
Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)),
|
||||||
@ -226,10 +262,16 @@ impl Fill {
|
|||||||
prim: Id<Primitive>,
|
prim: Id<Primitive>,
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
canvas_info: &crate::CanvasInfo,
|
canvas_info: &crate::CanvasInfo,
|
||||||
|
old_block: Block,
|
||||||
) -> Option<Block> {
|
) -> Option<Block> {
|
||||||
if self.contains_at(tree, prim, pos) {
|
if self.contains_at(tree, prim, pos) {
|
||||||
match self {
|
match self {
|
||||||
Fill::Block(block) => Some(*block),
|
Fill::Block(block) => Some(*block),
|
||||||
|
Fill::Sprite(sprite) => Some(if old_block.is_filled() {
|
||||||
|
Block::air(*sprite)
|
||||||
|
} else {
|
||||||
|
old_block.with_sprite(*sprite)
|
||||||
|
}),
|
||||||
Fill::Brick(bk, col, range) => Some(Block::new(
|
Fill::Brick(bk, col, range) => Some(Block::new(
|
||||||
*bk,
|
*bk,
|
||||||
*col + (RandomField::new(13)
|
*col + (RandomField::new(13)
|
||||||
@ -290,9 +332,9 @@ impl Fill {
|
|||||||
};
|
};
|
||||||
aabb.made_valid()
|
aabb.made_valid()
|
||||||
},
|
},
|
||||||
Primitive::Segment(segment) => Aabb {
|
Primitive::Segment(segment, radius) => Aabb {
|
||||||
min: segment.start,
|
min: segment.start - radius.floor() as i32,
|
||||||
max: segment.end,
|
max: segment.end + radius.ceil() as i32,
|
||||||
},
|
},
|
||||||
Primitive::Sampling(a, _) => self.get_bounds_inner(tree, *a)?,
|
Primitive::Sampling(a, _) => self.get_bounds_inner(tree, *a)?,
|
||||||
Primitive::Prefab(p) => p.get_bounds(),
|
Primitive::Prefab(p) => p.get_bounds(),
|
||||||
@ -340,14 +382,87 @@ impl Fill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Painter {
|
||||||
|
prims: RefCell<Store<Primitive>>,
|
||||||
|
fills: RefCell<Vec<(Id<Primitive>, Fill)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Painter {
|
||||||
|
pub fn aabb(&self, aabb: Aabb<i32>) -> PrimitiveRef { self.prim(Primitive::Aabb(aabb)) }
|
||||||
|
|
||||||
|
pub fn line(&self, a: Vec3<i32>, b: Vec3<i32>, radius: f32) -> PrimitiveRef {
|
||||||
|
self.prim(Primitive::Segment(
|
||||||
|
LineSegment3 { start: a, end: b },
|
||||||
|
radius,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sprite(&self, pos: Vec3<i32>, sprite: SpriteKind) {
|
||||||
|
self.aabb(Aabb {
|
||||||
|
min: pos,
|
||||||
|
max: pos + 1,
|
||||||
|
})
|
||||||
|
.fill(Fill::Sprite(sprite))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pyramid(&self, aabb: Aabb<i32>) -> PrimitiveRef {
|
||||||
|
let inset = 0;
|
||||||
|
self.prim(Primitive::Ramp {
|
||||||
|
aabb,
|
||||||
|
inset,
|
||||||
|
dir: 0,
|
||||||
|
})
|
||||||
|
.union(self.prim(Primitive::Ramp {
|
||||||
|
aabb,
|
||||||
|
inset,
|
||||||
|
dir: 1,
|
||||||
|
}))
|
||||||
|
.union(self.prim(Primitive::Ramp {
|
||||||
|
aabb,
|
||||||
|
inset,
|
||||||
|
dir: 2,
|
||||||
|
}))
|
||||||
|
.union(self.prim(Primitive::Ramp {
|
||||||
|
aabb,
|
||||||
|
inset,
|
||||||
|
dir: 3,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prim(&self, prim: Primitive) -> PrimitiveRef {
|
||||||
|
PrimitiveRef {
|
||||||
|
id: self.prims.borrow_mut().insert(prim),
|
||||||
|
painter: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill(&self, prim: impl Into<Id<Primitive>>, fill: Fill) {
|
||||||
|
self.fills.borrow_mut().push((prim.into(), fill));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct PrimitiveRef<'a> {
|
||||||
|
id: Id<Primitive>,
|
||||||
|
painter: &'a Painter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<PrimitiveRef<'a>> for Id<Primitive> {
|
||||||
|
fn from(r: PrimitiveRef<'a>) -> Self { r.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PrimitiveRef<'a> {
|
||||||
|
pub fn union(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
|
||||||
|
self.painter.prim(Primitive::and(self, other))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill(self, fill: Fill) { self.painter.fill(self, fill); }
|
||||||
|
|
||||||
|
pub fn clear(self) { self.painter.fill(self, Fill::Block(Block::empty())); }
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Structure {
|
pub trait Structure {
|
||||||
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Id<Primitive>, Fill)>(
|
fn render(&self, site: &Site, land: &Land, painter: &Painter);
|
||||||
&self,
|
|
||||||
site: &Site,
|
|
||||||
land: &Land,
|
|
||||||
prim: F,
|
|
||||||
fill: G,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Generate a primitive tree and fills for this structure
|
// Generate a primitive tree and fills for this structure
|
||||||
fn render_collect(
|
fn render_collect(
|
||||||
@ -355,10 +470,13 @@ pub trait Structure {
|
|||||||
site: &Site,
|
site: &Site,
|
||||||
land: &Land,
|
land: &Land,
|
||||||
) -> (Store<Primitive>, Vec<(Id<Primitive>, Fill)>) {
|
) -> (Store<Primitive>, Vec<(Id<Primitive>, Fill)>) {
|
||||||
let mut tree = Store::default();
|
let painter = Painter {
|
||||||
let mut fills = Vec::new();
|
prims: RefCell::new(Store::default()),
|
||||||
self.render(site, land, |p| tree.insert(p), |p, f| fills.push((p, f)));
|
fills: RefCell::new(Vec::new()),
|
||||||
(tree, fills)
|
};
|
||||||
|
|
||||||
|
self.render(site, land, &painter);
|
||||||
|
(painter.prims.into_inner(), painter.fills.into_inner())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Extend a 2d AABR to a 3d AABB
|
/// Extend a 2d AABR to a 3d AABB
|
||||||
|
@ -4,7 +4,7 @@ mod tile;
|
|||||||
|
|
||||||
use self::tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE};
|
use self::tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE};
|
||||||
pub use self::{
|
pub use self::{
|
||||||
gen::{aabr_with_z, Fill, Primitive, Structure},
|
gen::{aabr_with_z, Fill, Painter, Primitive, Structure},
|
||||||
plot::{Plot, PlotKind},
|
plot::{Plot, PlotKind},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -59,11 +59,13 @@ 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| {
|
||||||
|
self.wpos_tile(wpos + rpos * tile::TILE_SIZE as i32)
|
||||||
|
.is_empty()
|
||||||
|
});
|
||||||
SpawnRules {
|
SpawnRules {
|
||||||
trees: SQUARE_9.iter().all(|&rpos| {
|
trees: not_near_things,
|
||||||
self.wpos_tile(wpos + rpos * tile::TILE_SIZE as i32)
|
max_warp: if not_near_things { 1.0 } else { 0.0 },
|
||||||
.is_empty()
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +79,11 @@ impl Site {
|
|||||||
|
|
||||||
pub fn plot(&self, id: Id<Plot>) -> &Plot { &self.plots[id] }
|
pub fn plot(&self, id: Id<Plot>) -> &Plot { &self.plots[id] }
|
||||||
|
|
||||||
pub fn plots(&self) -> impl Iterator<Item = &Plot> + '_ { self.plots.values() }
|
pub fn plots(&self) -> impl ExactSizeIterator<Item = &Plot> + '_ { self.plots.values() }
|
||||||
|
|
||||||
|
pub fn plazas(&self) -> impl ExactSizeIterator<Item = Id<Plot>> + '_ {
|
||||||
|
self.plazas.iter().copied()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_plot(&mut self, plot: Plot) -> Id<Plot> { self.plots.insert(plot) }
|
pub fn create_plot(&mut self, plot: Plot) -> Id<Plot> { self.plots.insert(plot) }
|
||||||
|
|
||||||
@ -150,6 +156,7 @@ impl Site {
|
|||||||
w,
|
w,
|
||||||
},
|
},
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(land.get_alt_approx(self.tile_center_wpos(tile)) as i32),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,22 +171,17 @@ impl Site {
|
|||||||
search_pos: Vec2<i32>,
|
search_pos: Vec2<i32>,
|
||||||
area_range: Range<u32>,
|
area_range: Range<u32>,
|
||||||
min_dims: Extent2<u32>,
|
min_dims: Extent2<u32>,
|
||||||
) -> Option<(Aabr<i32>, Vec2<i32>)> {
|
) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>)> {
|
||||||
self.tiles.find_near(search_pos, |center, _| {
|
let ((aabr, door_dir), door_pos) = self.tiles.find_near(search_pos, |center, _| {
|
||||||
|
let dir = CARDINALS
|
||||||
|
.iter()
|
||||||
|
.find(|dir| self.tiles.get(center + *dir).is_road())?;
|
||||||
self.tiles
|
self.tiles
|
||||||
.grow_aabr(center, area_range.clone(), min_dims)
|
.grow_aabr(center, area_range.clone(), min_dims)
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|aabr| {
|
.zip(Some(*dir))
|
||||||
(aabr.min.x..aabr.max.x)
|
})?;
|
||||||
.any(|x| self.tiles.get(Vec2::new(x, aabr.min.y - 1)).is_road())
|
Some((aabr, door_pos, door_dir))
|
||||||
|| (aabr.min.x..aabr.max.x)
|
|
||||||
.any(|x| self.tiles.get(Vec2::new(x, aabr.max.y)).is_road())
|
|
||||||
|| (aabr.min.y..aabr.max.y)
|
|
||||||
.any(|y| self.tiles.get(Vec2::new(aabr.min.x - 1, y)).is_road())
|
|
||||||
|| (aabr.min.y..aabr.max.y)
|
|
||||||
.any(|y| self.tiles.get(Vec2::new(aabr.max.x, y)).is_road())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_roadside_aabr(
|
pub fn find_roadside_aabr(
|
||||||
@ -187,13 +189,14 @@ impl Site {
|
|||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
area_range: Range<u32>,
|
area_range: Range<u32>,
|
||||||
min_dims: Extent2<u32>,
|
min_dims: Extent2<u32>,
|
||||||
) -> Option<(Aabr<i32>, Vec2<i32>)> {
|
) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>)> {
|
||||||
let dir = Vec2::<f32>::zero()
|
let dir = Vec2::<f32>::zero()
|
||||||
.map(|_| rng.gen_range(-1.0..1.0))
|
.map(|_| rng.gen_range(-1.0..1.0))
|
||||||
.normalized();
|
.normalized();
|
||||||
let search_pos = if rng.gen() {
|
let search_pos = if rng.gen() {
|
||||||
self.plot(*self.plazas.choose(rng)?).root_tile
|
let plaza = self.plot(*self.plazas.choose(rng)?);
|
||||||
+ (dir * 4.0).map(|e: f32| e.round() as i32)
|
let sz = plaza.find_bounds().size();
|
||||||
|
plaza.root_tile + dir.map(|e: f32| e.round() as i32) * (sz + 1)
|
||||||
} else if let PlotKind::Road(path) = &self.plot(*self.roads.choose(rng)?).kind {
|
} else if let PlotKind::Road(path) = &self.plot(*self.roads.choose(rng)?).kind {
|
||||||
*path.nodes().choose(rng)? + (dir * 1.0).map(|e: f32| e.round() as i32)
|
*path.nodes().choose(rng)? + (dir * 1.0).map(|e: f32| e.round() as i32)
|
||||||
} else {
|
} else {
|
||||||
@ -204,6 +207,8 @@ 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_dist = 10.0 + plaza_radius as f32 * 5.0;
|
||||||
let pos = attempt(32, || {
|
let pos = attempt(32, || {
|
||||||
self.plazas
|
self.plazas
|
||||||
.choose(rng)
|
.choose(rng)
|
||||||
@ -211,22 +216,24 @@ impl Site {
|
|||||||
self.plot(p).root_tile
|
self.plot(p).root_tile
|
||||||
+ (Vec2::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0))
|
+ (Vec2::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0))
|
||||||
.normalized()
|
.normalized()
|
||||||
* 24.0)
|
* plaza_dist)
|
||||||
.map(|e| e as i32)
|
.map(|e| e as i32)
|
||||||
})
|
})
|
||||||
.filter(|tile| !self.tiles.get(*tile).is_obstacle())
|
.filter(|tile| !self.tiles.get(*tile).is_obstacle())
|
||||||
.filter(|&tile| {
|
.filter(|&tile| {
|
||||||
self.plazas
|
self.plazas.iter().all(|&p| {
|
||||||
.iter()
|
self.plot(p).root_tile.distance_squared(tile) as f32
|
||||||
.all(|&p| self.plot(p).root_tile.distance_squared(tile) > 20i32.pow(2))
|
> (plaza_dist * 0.85).powi(2)
|
||||||
&& rng.gen_range(0..48) > tile.map(|e| e.abs()).reduce_max()
|
}) && rng.gen_range(0..48) > tile.map(|e| e.abs()).reduce_max()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(Vec2::zero);
|
.unwrap_or_else(Vec2::zero);
|
||||||
|
|
||||||
|
let plaza_alt = land.get_alt_approx(self.tile_center_wpos(pos)) as i32;
|
||||||
|
|
||||||
let aabr = Aabr {
|
let aabr = Aabr {
|
||||||
min: pos + Vec2::broadcast(-3),
|
min: pos + Vec2::broadcast(-plaza_radius),
|
||||||
max: pos + Vec2::broadcast(4),
|
max: pos + Vec2::broadcast(plaza_radius + 1),
|
||||||
};
|
};
|
||||||
let plaza = self.create_plot(Plot {
|
let plaza = self.create_plot(Plot {
|
||||||
kind: PlotKind::Plaza,
|
kind: PlotKind::Plaza,
|
||||||
@ -238,18 +245,33 @@ impl Site {
|
|||||||
self.blit_aabr(aabr, Tile {
|
self.blit_aabr(aabr, Tile {
|
||||||
kind: TileKind::Plaza,
|
kind: TileKind::Plaza,
|
||||||
plot: Some(plaza),
|
plot: Some(plaza),
|
||||||
|
hard_alt: Some(plaza_alt),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut already_pathed = vec![plaza];
|
let mut already_pathed = vec![];
|
||||||
// One major, one minor road
|
// One major, one minor road
|
||||||
for width in (1..=2).rev() {
|
for _ in (0..rng.gen_range(1.25..2.25) as u16).rev() {
|
||||||
if let Some(&p) = self
|
if let Some(&p) = self
|
||||||
.plazas
|
.plazas
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| !already_pathed.contains(p))
|
.filter(|&&p| {
|
||||||
|
!already_pathed.contains(&p)
|
||||||
|
&& p != plaza
|
||||||
|
&& already_pathed.iter().all(|&ap| {
|
||||||
|
(self.plot(ap).root_tile - pos)
|
||||||
|
.map(|e| e as f32)
|
||||||
|
.normalized()
|
||||||
|
.dot(
|
||||||
|
(self.plot(p).root_tile - pos)
|
||||||
|
.map(|e| e as f32)
|
||||||
|
.normalized(),
|
||||||
|
)
|
||||||
|
< 0.0
|
||||||
|
})
|
||||||
|
})
|
||||||
.min_by_key(|&&p| self.plot(p).root_tile.distance_squared(pos))
|
.min_by_key(|&&p| self.plot(p).root_tile.distance_squared(pos))
|
||||||
{
|
{
|
||||||
self.create_road(land, rng, self.plot(p).root_tile, pos, width);
|
self.create_road(land, rng, self.plot(p).root_tile, pos, 2 /* + i */);
|
||||||
already_pathed.push(p);
|
already_pathed.push(p);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -319,6 +341,7 @@ impl Site {
|
|||||||
site.blit_aabr(aabr, Tile {
|
site.blit_aabr(aabr, Tile {
|
||||||
kind: TileKind::Empty,
|
kind: TileKind::Empty,
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
site
|
site
|
||||||
@ -337,7 +360,7 @@ impl Site {
|
|||||||
|
|
||||||
site.make_plaza(land, &mut rng);
|
site.make_plaza(land, &mut rng);
|
||||||
|
|
||||||
let build_chance = Lottery::from(vec![(64.0, 1), (5.0, 2), (8.0, 3), (0.75, 4)]);
|
let build_chance = Lottery::from(vec![(64.0, 1), (5.0, 2), (8.0, 3), (5.0, 4), (5.0, 5)]);
|
||||||
|
|
||||||
let mut castles = 0;
|
let mut castles = 0;
|
||||||
|
|
||||||
@ -345,22 +368,25 @@ 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(8.0) * 3.0).round() as u32;
|
let size = (2.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
|
||||||
if let Some((aabr, door_tile)) = attempt(32, || {
|
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
|
||||||
site.find_roadside_aabr(
|
site.find_roadside_aabr(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
4..(size + 1).pow(2),
|
4..(size + 1).pow(2),
|
||||||
Extent2::broadcast(size),
|
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 {
|
let plot = site.create_plot(Plot {
|
||||||
kind: PlotKind::House(plot::House::generate(
|
kind: PlotKind::House(house),
|
||||||
land,
|
|
||||||
&mut reseed(&mut rng),
|
|
||||||
&site,
|
|
||||||
door_tile,
|
|
||||||
aabr,
|
|
||||||
)),
|
|
||||||
root_tile: aabr.center(),
|
root_tile: aabr.center(),
|
||||||
tiles: aabr_tiles(aabr).collect(),
|
tiles: aabr_tiles(aabr).collect(),
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
@ -369,6 +395,42 @@ impl Site {
|
|||||||
site.blit_aabr(aabr, Tile {
|
site.blit_aabr(aabr, Tile {
|
||||||
kind: TileKind::Building,
|
kind: TileKind::Building,
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(house_alt),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
site.make_plaza(land, &mut rng);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Workshop
|
||||||
|
5 => {
|
||||||
|
let size = (2.0 + rng.gen::<f32>().powf(5.0) * 1.5).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 workshop = plot::Workshop::generate(
|
||||||
|
land,
|
||||||
|
&mut reseed(&mut rng),
|
||||||
|
&site,
|
||||||
|
door_tile,
|
||||||
|
door_dir,
|
||||||
|
aabr,
|
||||||
|
);
|
||||||
|
let workshop_alt = workshop.alt;
|
||||||
|
let plot = site.create_plot(Plot {
|
||||||
|
kind: PlotKind::Workshop(workshop),
|
||||||
|
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(workshop_alt),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
site.make_plaza(land, &mut rng);
|
site.make_plaza(land, &mut rng);
|
||||||
@ -376,30 +438,30 @@ impl Site {
|
|||||||
},
|
},
|
||||||
// Guard tower
|
// Guard tower
|
||||||
2 => {
|
2 => {
|
||||||
if let Some((_aabr, _)) = attempt(10, || {
|
if let Some((_aabr, _, _door_dir)) = attempt(10, || {
|
||||||
site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
|
site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
|
||||||
}) {
|
}) {
|
||||||
/*
|
// let plot = site.create_plot(Plot {
|
||||||
let plot = site.create_plot(Plot {
|
// kind: PlotKind::Castle(plot::Castle::generate(
|
||||||
kind: PlotKind::Castle(plot::Castle::generate(
|
// land,
|
||||||
land,
|
// &mut rng,
|
||||||
&mut rng,
|
// &site,
|
||||||
&site,
|
// aabr,
|
||||||
aabr,
|
// )),
|
||||||
)),
|
// root_tile: aabr.center(),
|
||||||
root_tile: aabr.center(),
|
// tiles: aabr_tiles(aabr).collect(),
|
||||||
tiles: aabr_tiles(aabr).collect(),
|
// seed: rng.gen(),
|
||||||
seed: rng.gen(),
|
// });
|
||||||
});
|
|
||||||
|
|
||||||
site.blit_aabr(aabr, Tile {
|
// site.blit_aabr(aabr, Tile {
|
||||||
kind: TileKind::Castle,
|
// kind: TileKind::Castle,
|
||||||
plot: Some(plot),
|
// plot: Some(plot),
|
||||||
});
|
// hard_alt: None,
|
||||||
*/
|
// });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Field
|
// Field
|
||||||
|
/*
|
||||||
3 => {
|
3 => {
|
||||||
attempt(10, || {
|
attempt(10, || {
|
||||||
let search_pos = attempt(16, || {
|
let search_pos = attempt(16, || {
|
||||||
@ -426,13 +488,15 @@ impl Site {
|
|||||||
site.tiles.set(tile, Tile {
|
site.tiles.set(tile, Tile {
|
||||||
kind: TileKind::Field,
|
kind: TileKind::Field,
|
||||||
plot: None,
|
plot: None,
|
||||||
|
hard_alt: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
// Castle
|
// Castle
|
||||||
4 if castles < 1 => {
|
4 if castles < 1 => {
|
||||||
if let Some((aabr, _entrance_tile)) = attempt(10, || {
|
if let Some((aabr, _entrance_tile, _door_dir)) = attempt(32, || {
|
||||||
site.find_roadside_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16))
|
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 offset = rng.gen_range(5..(aabr.size().w.min(aabr.size().h) - 4));
|
||||||
@ -440,10 +504,10 @@ impl Site {
|
|||||||
min: Vec2::new(aabr.min.x + offset - 1, aabr.min.y),
|
min: Vec2::new(aabr.min.x + offset - 1, aabr.min.y),
|
||||||
max: Vec2::new(aabr.min.x + offset + 2, aabr.min.y + 1),
|
max: Vec2::new(aabr.min.x + offset + 2, aabr.min.y + 1),
|
||||||
};
|
};
|
||||||
|
let castle = plot::Castle::generate(land, &mut rng, &site, aabr, gate_aabr);
|
||||||
|
let castle_alt = castle.alt;
|
||||||
let plot = site.create_plot(Plot {
|
let plot = site.create_plot(Plot {
|
||||||
kind: PlotKind::Castle(plot::Castle::generate(
|
kind: PlotKind::Castle(castle),
|
||||||
land, &mut rng, &site, aabr, gate_aabr,
|
|
||||||
)),
|
|
||||||
root_tile: aabr.center(),
|
root_tile: aabr.center(),
|
||||||
tiles: aabr_tiles(aabr).collect(),
|
tiles: aabr_tiles(aabr).collect(),
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
@ -452,11 +516,13 @@ impl Site {
|
|||||||
let wall_north = Tile {
|
let wall_north = Tile {
|
||||||
kind: TileKind::Wall(Ori::North),
|
kind: TileKind::Wall(Ori::North),
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
};
|
};
|
||||||
|
|
||||||
let wall_east = Tile {
|
let wall_east = Tile {
|
||||||
kind: TileKind::Wall(Ori::East),
|
kind: TileKind::Wall(Ori::East),
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
};
|
};
|
||||||
for x in 0..aabr.size().w {
|
for x in 0..aabr.size().w {
|
||||||
site.tiles
|
site.tiles
|
||||||
@ -478,14 +544,17 @@ impl Site {
|
|||||||
let gate = Tile {
|
let gate = Tile {
|
||||||
kind: TileKind::Gate,
|
kind: TileKind::Gate,
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
};
|
};
|
||||||
let tower_parapet = Tile {
|
let tower_parapet = Tile {
|
||||||
kind: TileKind::Tower(RoofKind::Parapet),
|
kind: TileKind::Tower(RoofKind::Parapet),
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
};
|
};
|
||||||
let tower_pyramid = Tile {
|
let tower_pyramid = Tile {
|
||||||
kind: TileKind::Tower(RoofKind::Pyramid),
|
kind: TileKind::Tower(RoofKind::Pyramid),
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
};
|
};
|
||||||
|
|
||||||
site.tiles.set(
|
site.tiles.set(
|
||||||
@ -523,6 +592,7 @@ impl Site {
|
|||||||
Tile {
|
Tile {
|
||||||
kind: TileKind::Road { a: 0, b: 0, w: 0 },
|
kind: TileKind::Road { a: 0, b: 0, w: 0 },
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -535,6 +605,7 @@ impl Site {
|
|||||||
Tile {
|
Tile {
|
||||||
kind: TileKind::Wall(Ori::North),
|
kind: TileKind::Wall(Ori::North),
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
site.tiles.set(
|
site.tiles.set(
|
||||||
@ -562,6 +633,7 @@ impl Site {
|
|||||||
Tile {
|
Tile {
|
||||||
kind: TileKind::Keep(KeepKind::Middle),
|
kind: TileKind::Keep(KeepKind::Middle),
|
||||||
plot: Some(plot),
|
plot: Some(plot),
|
||||||
|
hard_alt: Some(castle_alt),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -634,7 +706,7 @@ impl Site {
|
|||||||
b.with_sprite(SpriteKind::Empty)
|
b.with_sprite(SpriteKind::Empty)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Rock, Rgb::new(55, 45, 50))
|
Block::new(BlockKind::Earth, Rgb::new(0x6A, 0x47, 0x24))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -649,16 +721,16 @@ impl Site {
|
|||||||
canvas.foreach_col(|canvas, wpos2d, col| {
|
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||||
|
|
||||||
let tpos = self.wpos_tile_pos(wpos2d);
|
let tpos = self.wpos_tile_pos(wpos2d);
|
||||||
let near_roads = SQUARE_9
|
let near_roads = CARDINALS
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|rpos| {
|
.filter_map(|rpos| {
|
||||||
let tile = self.tiles.get(tpos + rpos);
|
let tile = self.tiles.get(tpos + rpos);
|
||||||
if let TileKind::Road { a, b, w } = &tile.kind {
|
if let TileKind::Road { a, b, w } = &tile.kind {
|
||||||
if let Some(PlotKind::Road(path)) = tile.plot.map(|p| &self.plot(p).kind) {
|
if let Some(PlotKind::Road(path)) = tile.plot.map(|p| &self.plot(p).kind) {
|
||||||
Some((LineSegment2 {
|
Some((LineSegment2 {
|
||||||
start: self.tile_center_wpos(path.nodes()[*a as usize]).map(|e| e as f32),
|
start: self.tile_wpos(path.nodes()[*a as usize]).map(|e| e as f32),
|
||||||
end: self.tile_center_wpos(path.nodes()[*b as usize]).map(|e| e as f32),
|
end: self.tile_wpos(path.nodes()[*b as usize]).map(|e| e as f32),
|
||||||
}, *w))
|
}, *w, tile.hard_alt))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -668,22 +740,43 @@ impl Site {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let wpos2df = wpos2d.map(|e| e as f32);
|
let wpos2df = wpos2d.map(|e| e as f32);
|
||||||
let dist = near_roads
|
let mut min_dist = None;
|
||||||
.map(|(line, w)| (line.distance_to_point(wpos2df) - w as f32 * 2.0).max(0.0))
|
let mut avg_hard_alt = None;
|
||||||
.min_by_key(|d| (*d * 100.0) as i32);
|
for (line, w, hard_alt) in near_roads {
|
||||||
|
let dist = line.distance_to_point(wpos2df);
|
||||||
|
let path_width = w as f32 * 2.0;
|
||||||
|
if dist < path_width {
|
||||||
|
min_dist = Some(min_dist.map(|d: f32| d.min(dist)).unwrap_or(dist));
|
||||||
|
|
||||||
if dist.map_or(false, |d| d <= 0.75) {
|
if let Some(ha) = hard_alt {
|
||||||
let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
|
let w = path_width - dist;
|
||||||
|
let (sum, weight) = avg_hard_alt.unwrap_or((0.0, 0.0));
|
||||||
|
avg_hard_alt = Some((sum + ha as f32 * w, weight + w));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let dist = near_roads
|
||||||
|
// .map(|(line, w)| (line.distance_to_point(wpos2df) - w as f32 * 2.0).max(0.0))
|
||||||
|
// .min_by_key(|d| (*d * 100.0) as i32);
|
||||||
|
|
||||||
|
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;
|
||||||
(-6..4).for_each(|z| canvas.map(
|
(-6..4).for_each(|z| canvas.map(
|
||||||
Vec3::new(wpos2d.x, wpos2d.y, alt + z),
|
Vec3::new(wpos2d.x, wpos2d.y, alt + z),
|
||||||
|b| if z >= 0 {
|
|b| if z > 0 {
|
||||||
if b.is_filled() {
|
let sprite = if z == 1 && self.tile_wpos(tpos) == wpos2d && (tpos + tpos.yx() / 2) % 2 == Vec2::zero() {
|
||||||
Block::empty()
|
SpriteKind::StreetLamp
|
||||||
} else {
|
} else {
|
||||||
b.with_sprite(SpriteKind::Empty)
|
SpriteKind::Empty
|
||||||
|
};
|
||||||
|
if b.is_filled() {
|
||||||
|
Block::air(sprite)
|
||||||
|
} else {
|
||||||
|
b.with_sprite(sprite)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Rock, Rgb::new(55, 45, 50))
|
Block::new(BlockKind::Earth, Rgb::new(0x6A, 0x47, 0x24))
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -762,9 +855,12 @@ impl Site {
|
|||||||
max: wpos2d + TerrainChunkSize::RECT_SIZE.as_::<i32>(),
|
max: wpos2d + TerrainChunkSize::RECT_SIZE.as_::<i32>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let info = canvas.info();
|
||||||
|
|
||||||
for plot in plots_to_render {
|
for plot in plots_to_render {
|
||||||
let (prim_tree, fills) = match &self.plots[plot].kind {
|
let (prim_tree, fills) = match &self.plots[plot].kind {
|
||||||
PlotKind::House(house) => house.render_collect(self, &canvas.land()),
|
PlotKind::House(house) => house.render_collect(self, &canvas.land()),
|
||||||
|
PlotKind::Workshop(workshop) => workshop.render_collect(self, &canvas.land()),
|
||||||
PlotKind::Castle(castle) => castle.render_collect(self, &canvas.land()),
|
PlotKind::Castle(castle) => castle.render_collect(self, &canvas.land()),
|
||||||
PlotKind::Dungeon(dungeon) => dungeon.render_collect(self, &canvas.land()),
|
PlotKind::Dungeon(dungeon) => dungeon.render_collect(self, &canvas.land()),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
@ -777,13 +873,25 @@ impl Site {
|
|||||||
|
|
||||||
for x in aabb.min.x..aabb.max.x {
|
for x in aabb.min.x..aabb.max.x {
|
||||||
for y in aabb.min.y..aabb.max.y {
|
for y in aabb.min.y..aabb.max.y {
|
||||||
|
let col_tile = self.wpos_tile(Vec2::new(x, y));
|
||||||
|
if
|
||||||
|
/* col_tile.is_building() && */
|
||||||
|
col_tile
|
||||||
|
.plot
|
||||||
|
.and_then(|p| self.plots[p].z_range())
|
||||||
|
.zip(self.plots[plot].z_range())
|
||||||
|
.map_or(false, |(a, b)| a.end > b.end)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for z in aabb.min.z..aabb.max.z {
|
for z in aabb.min.z..aabb.max.z {
|
||||||
let pos = Vec3::new(x, y, z);
|
let pos = Vec3::new(x, y, z);
|
||||||
|
|
||||||
if let Some(block) = fill.sample_at(&prim_tree, prim, pos, &canvas.info)
|
canvas.map(pos, |block| {
|
||||||
{
|
fill.sample_at(&prim_tree, prim, pos, &info, block)
|
||||||
canvas.set(pos, block);
|
.unwrap_or(block)
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
mod castle;
|
mod castle;
|
||||||
pub mod dungeon;
|
pub mod dungeon;
|
||||||
mod house;
|
mod house;
|
||||||
|
mod workshop;
|
||||||
|
|
||||||
pub use self::{castle::Castle, dungeon::Dungeon, house::House};
|
pub use self::{castle::Castle, dungeon::Dungeon, house::House, workshop::Workshop};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::util::DHashSet;
|
use crate::util::DHashSet;
|
||||||
@ -25,11 +26,21 @@ impl Plot {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn z_range(&self) -> Option<Range<i32>> {
|
||||||
|
match &self.kind {
|
||||||
|
PlotKind::House(house) => Some(house.z_range()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> &PlotKind { &self.kind }
|
pub fn kind(&self) -> &PlotKind { &self.kind }
|
||||||
|
|
||||||
|
pub fn root_tile(&self) -> Vec2<i32> { self.root_tile }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PlotKind {
|
pub enum PlotKind {
|
||||||
House(House),
|
House(House),
|
||||||
|
Workshop(Workshop),
|
||||||
Plaza,
|
Plaza,
|
||||||
Castle(Castle),
|
Castle(Castle),
|
||||||
Road(Path<Vec2<i32>>),
|
Road(Path<Vec2<i32>>),
|
||||||
|
@ -10,7 +10,7 @@ pub struct Castle {
|
|||||||
_bounds: Aabr<i32>,
|
_bounds: Aabr<i32>,
|
||||||
gate_aabr: Aabr<i32>,
|
gate_aabr: Aabr<i32>,
|
||||||
gate_alt: i32,
|
gate_alt: i32,
|
||||||
alt: i32,
|
pub(crate) alt: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Castle {
|
impl Castle {
|
||||||
@ -43,13 +43,7 @@ impl Castle {
|
|||||||
|
|
||||||
impl Structure for Castle {
|
impl Structure for Castle {
|
||||||
#[allow(clippy::identity_op)]
|
#[allow(clippy::identity_op)]
|
||||||
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Id<Primitive>, Fill)>(
|
fn render(&self, site: &Site, _land: &Land, painter: &Painter) {
|
||||||
&self,
|
|
||||||
site: &Site,
|
|
||||||
_land: &Land,
|
|
||||||
mut prim: F,
|
|
||||||
mut fill: G,
|
|
||||||
) {
|
|
||||||
let wall_height = 24;
|
let wall_height = 24;
|
||||||
let parapet_height = 2;
|
let parapet_height = 2;
|
||||||
let parapet_gap = 2;
|
let parapet_gap = 2;
|
||||||
@ -61,8 +55,8 @@ impl Structure for Castle {
|
|||||||
let _keep_height = wall_height + keep_levels * keep_level_height + 1;
|
let _keep_height = wall_height + keep_levels * keep_level_height + 1;
|
||||||
let wall_rgb = Rgb::new(38, 46, 43);
|
let wall_rgb = Rgb::new(38, 46, 43);
|
||||||
// Flatten inside of the castle
|
// Flatten inside of the castle
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt),
|
min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt),
|
||||||
max: site
|
max: site
|
||||||
.tile_wpos(self.tile_aabr.max)
|
.tile_wpos(self.tile_aabr.max)
|
||||||
@ -70,8 +64,8 @@ impl Structure for Castle {
|
|||||||
})),
|
})),
|
||||||
Fill::Block(Block::empty()),
|
Fill::Block(Block::empty()),
|
||||||
);
|
);
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt),
|
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),
|
max: site.tile_wpos(self.tile_aabr.max).with_z(self.gate_alt + 1),
|
||||||
})),
|
})),
|
||||||
@ -84,22 +78,22 @@ impl Structure for Castle {
|
|||||||
match site.tiles.get(tile_pos).kind.clone() {
|
match site.tiles.get(tile_pos).kind.clone() {
|
||||||
TileKind::Wall(ori) => {
|
TileKind::Wall(ori) => {
|
||||||
let dir = ori.dir();
|
let dir = ori.dir();
|
||||||
let wall = prim(Primitive::Aabb(Aabb {
|
let wall = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: wpos.with_z(self.alt - 20),
|
min: wpos.with_z(self.alt - 20),
|
||||||
max: (wpos + ts).with_z(self.alt + wall_height),
|
max: (wpos + ts).with_z(self.alt + wall_height),
|
||||||
}));
|
}));
|
||||||
// TODO Figure out logic to choose on on which site wall should be placed
|
// TODO Figure out logic to choose on on which site wall should be placed
|
||||||
// (inner, outer)
|
// (inner, outer)
|
||||||
let parapet = prim(Primitive::Aabb(Aabb {
|
let parapet = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos - dir.yx()).with_z(self.alt + wall_height),
|
min: (wpos - dir.yx()).with_z(self.alt + wall_height),
|
||||||
max: (wpos + ts * dir).with_z(self.alt + wall_height + parapet_height),
|
max: (wpos + ts * dir).with_z(self.alt + wall_height + parapet_height),
|
||||||
}));
|
}));
|
||||||
let parapet2 = prim(Primitive::Aabb(Aabb {
|
let parapet2 = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos + ts * dir.yx()).with_z(self.alt + wall_height),
|
min: (wpos + ts * dir.yx()).with_z(self.alt + wall_height),
|
||||||
max: (wpos + (ts + 1) * dir.yx() + ts * dir)
|
max: (wpos + (ts + 1) * dir.yx() + ts * dir)
|
||||||
.with_z(self.alt + wall_height + parapet_height),
|
.with_z(self.alt + wall_height + parapet_height),
|
||||||
}));
|
}));
|
||||||
let cut_sides = prim(Primitive::Aabb(Aabb {
|
let cut_sides = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos + parapet_offset * dir - dir.yx())
|
min: (wpos + parapet_offset * dir - dir.yx())
|
||||||
.with_z(self.alt + wall_height + parapet_height - 1),
|
.with_z(self.alt + wall_height + parapet_height - 1),
|
||||||
max: (wpos
|
max: (wpos
|
||||||
@ -108,12 +102,12 @@ impl Structure for Castle {
|
|||||||
.with_z(self.alt + wall_height + parapet_height),
|
.with_z(self.alt + wall_height + parapet_height),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fill(wall, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
|
painter.fill(wall, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
|
||||||
let sides = prim(Primitive::Or(parapet, parapet2));
|
let sides = painter.prim(Primitive::or(parapet, parapet2));
|
||||||
fill(sides, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
|
painter.fill(sides, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
|
||||||
if (x + y).is_odd() {
|
if (x + y).is_odd() {
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos + 2 * dir - dir.yx()).with_z(self.alt - 20),
|
min: (wpos + 2 * dir - dir.yx()).with_z(self.alt - 20),
|
||||||
max: (wpos + 4 * dir + (ts + 1) * dir.yx())
|
max: (wpos + 4 * dir + (ts + 1) * dir.yx())
|
||||||
.with_z(self.alt + wall_height),
|
.with_z(self.alt + wall_height),
|
||||||
@ -121,55 +115,55 @@ impl Structure for Castle {
|
|||||||
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let window_top = prim(Primitive::Aabb(Aabb {
|
let window_top = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos + 2 * dir).with_z(self.alt + wall_height / 4 + 9),
|
min: (wpos + 2 * dir).with_z(self.alt + wall_height / 4 + 9),
|
||||||
max: (wpos + (ts - 2) * dir + dir.yx())
|
max: (wpos + (ts - 2) * dir + dir.yx())
|
||||||
.with_z(self.alt + wall_height / 4 + 12),
|
.with_z(self.alt + wall_height / 4 + 12),
|
||||||
}));
|
}));
|
||||||
let window_bottom = prim(Primitive::Aabb(Aabb {
|
let window_bottom = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos + 1 * dir).with_z(self.alt + wall_height / 4),
|
min: (wpos + 1 * dir).with_z(self.alt + wall_height / 4),
|
||||||
max: (wpos + (ts - 1) * dir + dir.yx())
|
max: (wpos + (ts - 1) * dir + dir.yx())
|
||||||
.with_z(self.alt + wall_height / 4 + 9),
|
.with_z(self.alt + wall_height / 4 + 9),
|
||||||
}));
|
}));
|
||||||
let window_top2 = prim(Primitive::Aabb(Aabb {
|
let window_top2 = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos + 2 * dir + (ts - 1) * dir.yx())
|
min: (wpos + 2 * dir + (ts - 1) * dir.yx())
|
||||||
.with_z(self.alt + wall_height / 4 + 9),
|
.with_z(self.alt + wall_height / 4 + 9),
|
||||||
max: (wpos + (ts - 2) * dir + ts * dir.yx())
|
max: (wpos + (ts - 2) * dir + ts * dir.yx())
|
||||||
.with_z(self.alt + wall_height / 4 + 12),
|
.with_z(self.alt + wall_height / 4 + 12),
|
||||||
}));
|
}));
|
||||||
let window_bottom2 = prim(Primitive::Aabb(Aabb {
|
let window_bottom2 = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos + 1 * dir + (ts - 1) * dir.yx())
|
min: (wpos + 1 * dir + (ts - 1) * dir.yx())
|
||||||
.with_z(self.alt + wall_height / 4),
|
.with_z(self.alt + wall_height / 4),
|
||||||
max: (wpos + (ts - 1) * dir + ts * dir.yx())
|
max: (wpos + (ts - 1) * dir + ts * dir.yx())
|
||||||
.with_z(self.alt + wall_height / 4 + 9),
|
.with_z(self.alt + wall_height / 4 + 9),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fill(window_bottom, Fill::Block(Block::empty()));
|
painter.fill(window_bottom, Fill::Block(Block::empty()));
|
||||||
fill(window_top, Fill::Block(Block::empty()));
|
painter.fill(window_top, Fill::Block(Block::empty()));
|
||||||
fill(window_bottom2, Fill::Block(Block::empty()));
|
painter.fill(window_bottom2, Fill::Block(Block::empty()));
|
||||||
fill(window_top2, Fill::Block(Block::empty()));
|
painter.fill(window_top2, Fill::Block(Block::empty()));
|
||||||
}
|
}
|
||||||
fill(cut_sides, Fill::Block(Block::empty()));
|
painter.fill(cut_sides, Fill::Block(Block::empty()));
|
||||||
},
|
},
|
||||||
TileKind::Tower(roof) => {
|
TileKind::Tower(roof) => {
|
||||||
let tower_total_height =
|
let tower_total_height =
|
||||||
self.alt + wall_height + parapet_height + tower_height;
|
self.alt + wall_height + parapet_height + tower_height;
|
||||||
let tower_lower = prim(Primitive::Aabb(Aabb {
|
let tower_lower = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos - 1).with_z(self.alt - 20),
|
min: (wpos - 1).with_z(self.alt - 20),
|
||||||
max: (wpos + ts + 1).with_z(tower_total_height),
|
max: (wpos + ts + 1).with_z(tower_total_height),
|
||||||
}));
|
}));
|
||||||
fill(tower_lower, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
|
painter.fill(tower_lower, Fill::Brick(BlockKind::Rock, wall_rgb, 12));
|
||||||
let tower_upper = prim(Primitive::Aabb(Aabb {
|
let tower_upper = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos - 2).with_z(tower_total_height - 4i32),
|
min: (wpos - 2).with_z(tower_total_height - 4i32),
|
||||||
max: (wpos + ts + 2).with_z(tower_total_height - 2i32),
|
max: (wpos + ts + 2).with_z(tower_total_height - 2i32),
|
||||||
}));
|
}));
|
||||||
let tower_upper2 = prim(Primitive::Aabb(Aabb {
|
let tower_upper2 = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos - 3).with_z(tower_total_height - 2i32),
|
min: (wpos - 3).with_z(tower_total_height - 2i32),
|
||||||
max: (wpos + ts + 3).with_z(tower_total_height),
|
max: (wpos + ts + 3).with_z(tower_total_height),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Or(tower_upper, tower_upper2)),
|
painter.prim(Primitive::or(tower_upper, tower_upper2)),
|
||||||
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -178,8 +172,8 @@ impl Structure for Castle {
|
|||||||
let roof_lip = 1;
|
let roof_lip = 1;
|
||||||
let roof_height = (ts + 3) / 2 + roof_lip + 1;
|
let roof_height = (ts + 3) / 2 + roof_lip + 1;
|
||||||
|
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Pyramid {
|
painter.prim(Primitive::Pyramid {
|
||||||
aabb: Aabb {
|
aabb: Aabb {
|
||||||
min: (wpos - 3 - roof_lip).with_z(tower_total_height),
|
min: (wpos - 3 - roof_lip).with_z(tower_total_height),
|
||||||
max: (wpos + ts + 3 + roof_lip)
|
max: (wpos + ts + 3 + roof_lip)
|
||||||
@ -191,27 +185,27 @@ impl Structure for Castle {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
RoofKind::Parapet => {
|
RoofKind::Parapet => {
|
||||||
let tower_top_outer = prim(Primitive::Aabb(Aabb {
|
let tower_top_outer = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos - 3).with_z(
|
min: (wpos - 3).with_z(
|
||||||
self.alt + wall_height + parapet_height + tower_height,
|
self.alt + wall_height + parapet_height + tower_height,
|
||||||
),
|
),
|
||||||
max: (wpos + ts + 3)
|
max: (wpos + ts + 3)
|
||||||
.with_z(tower_total_height + parapet_height),
|
.with_z(tower_total_height + parapet_height),
|
||||||
}));
|
}));
|
||||||
let tower_top_inner = prim(Primitive::Aabb(Aabb {
|
let tower_top_inner = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (wpos - 2).with_z(tower_total_height),
|
min: (wpos - 2).with_z(tower_total_height),
|
||||||
max: (wpos + ts + 2)
|
max: (wpos + ts + 2)
|
||||||
.with_z(tower_total_height + parapet_height),
|
.with_z(tower_total_height + parapet_height),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Xor(tower_top_outer, tower_top_inner)),
|
painter.prim(Primitive::xor(tower_top_outer, tower_top_inner)),
|
||||||
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
||||||
);
|
);
|
||||||
|
|
||||||
for x in (wpos.x..wpos.x + ts).step_by(2 * parapet_gap as usize) {
|
for x in (wpos.x..wpos.x + ts).step_by(2 * parapet_gap as usize) {
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: Vec3::new(x, wpos.y - 3, tower_total_height + 1),
|
min: Vec3::new(x, wpos.y - 3, tower_total_height + 1),
|
||||||
max: Vec3::new(
|
max: Vec3::new(
|
||||||
x + parapet_gap,
|
x + parapet_gap,
|
||||||
@ -223,8 +217,8 @@ impl Structure for Castle {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
for y in (wpos.y..wpos.y + ts).step_by(2 * parapet_gap as usize) {
|
for y in (wpos.y..wpos.y + ts).step_by(2 * parapet_gap as usize) {
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: Vec3::new(wpos.x - 3, y, tower_total_height + 1),
|
min: Vec3::new(wpos.x - 3, y, tower_total_height + 1),
|
||||||
max: Vec3::new(
|
max: Vec3::new(
|
||||||
wpos.x + ts + 3,
|
wpos.x + ts + 3,
|
||||||
@ -239,16 +233,16 @@ impl Structure for Castle {
|
|||||||
for &cpos in SQUARE_4.iter() {
|
for &cpos in SQUARE_4.iter() {
|
||||||
let pos = wpos - 3 + (ts + 6) * cpos - cpos;
|
let pos = wpos - 3 + (ts + 6) * cpos - cpos;
|
||||||
let pos2 = wpos - 2 + (ts + 4) * cpos - cpos;
|
let pos2 = wpos - 2 + (ts + 4) * cpos - cpos;
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: pos.with_z(tower_total_height - 2),
|
min: pos.with_z(tower_total_height - 2),
|
||||||
max: (pos + 1)
|
max: (pos + 1)
|
||||||
.with_z(tower_total_height + parapet_height),
|
.with_z(tower_total_height + parapet_height),
|
||||||
})),
|
})),
|
||||||
Fill::Block(Block::empty()),
|
Fill::Block(Block::empty()),
|
||||||
);
|
);
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: pos2.with_z(tower_total_height - 4),
|
min: pos2.with_z(tower_total_height - 4),
|
||||||
max: (pos2 + 1).with_z(tower_total_height - 2),
|
max: (pos2 + 1).with_z(tower_total_height - 2),
|
||||||
})),
|
})),
|
||||||
@ -263,8 +257,8 @@ impl Structure for Castle {
|
|||||||
KeepKind::Middle => {
|
KeepKind::Middle => {
|
||||||
for i in 0..keep_levels + 1 {
|
for i in 0..keep_levels + 1 {
|
||||||
let height = keep_level_height * i;
|
let height = keep_level_height * i;
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: wpos.with_z(self.alt + height),
|
min: wpos.with_z(self.alt + height),
|
||||||
max: (wpos + ts).with_z(self.alt + height + 1),
|
max: (wpos + ts).with_z(self.alt + height + 1),
|
||||||
})),
|
})),
|
||||||
@ -297,12 +291,12 @@ impl Structure for Castle {
|
|||||||
max: (site.tile_wpos(self.gate_aabr.max) - Vec2::unit_x())
|
max: (site.tile_wpos(self.gate_aabr.max) - Vec2::unit_x())
|
||||||
.with_z(self.alt + wall_height),
|
.with_z(self.alt + wall_height),
|
||||||
};
|
};
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(gate_aabb)),
|
painter.prim(Primitive::Aabb(gate_aabb)),
|
||||||
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
Fill::Brick(BlockKind::Rock, wall_rgb, 12),
|
||||||
);
|
);
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (gate_aabb.min + Vec3::unit_x() * 2 + Vec3::unit_z() * 2),
|
min: (gate_aabb.min + Vec3::unit_x() * 2 + Vec3::unit_z() * 2),
|
||||||
max: (gate_aabb.max - Vec3::unit_x() * 2 - Vec3::unit_z() * 16),
|
max: (gate_aabb.max - Vec3::unit_x() * 2 - Vec3::unit_z() * 16),
|
||||||
})),
|
})),
|
||||||
@ -310,8 +304,8 @@ impl Structure for Castle {
|
|||||||
);
|
);
|
||||||
let height = self.alt + wall_height - 17;
|
let height = self.alt + wall_height - 17;
|
||||||
for i in 1..5 {
|
for i in 1..5 {
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: Vec3::new(gate_aabb.min.x + 2 + i, gate_aabb.min.y, height + i as i32),
|
min: Vec3::new(gate_aabb.min.x + 2 + i, gate_aabb.min.y, height + i as i32),
|
||||||
max: Vec3::new(
|
max: Vec3::new(
|
||||||
gate_aabb.max.x - 2 - i,
|
gate_aabb.max.x - 2 - i,
|
||||||
@ -324,8 +318,8 @@ impl Structure for Castle {
|
|||||||
}
|
}
|
||||||
let height = self.alt + wall_height - 7;
|
let height = self.alt + wall_height - 7;
|
||||||
for x in (gate_aabb.min.x + 1..gate_aabb.max.x - 2).step_by(4) {
|
for x in (gate_aabb.min.x + 1..gate_aabb.max.x - 2).step_by(4) {
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: Vec3::new(x, gate_aabb.min.y + 1, height - 13),
|
min: Vec3::new(x, gate_aabb.min.y + 1, height - 13),
|
||||||
max: Vec3::new(x + 2, gate_aabb.min.y + 2, height),
|
max: Vec3::new(x + 2, gate_aabb.min.y + 2, height),
|
||||||
})),
|
})),
|
||||||
@ -333,8 +327,8 @@ impl Structure for Castle {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
for z in (height - 12..height).step_by(4) {
|
for z in (height - 12..height).step_by(4) {
|
||||||
fill(
|
painter.fill(
|
||||||
prim(Primitive::Aabb(Aabb {
|
painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: Vec3::new(gate_aabb.min.x + 2, gate_aabb.min.y + 1, z),
|
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),
|
max: Vec3::new(gate_aabb.max.x - 2, gate_aabb.min.y + 2, z + 2),
|
||||||
})),
|
})),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::SpawnRules;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
site::namegen::NameGen,
|
site::namegen::NameGen,
|
||||||
site2::{self, aabr_with_z, Fill, Primitive, Structure as SiteStructure},
|
site2::{self, aabr_with_z, Fill, Primitive, Structure as SiteStructure},
|
||||||
@ -1021,13 +1021,7 @@ pub fn make_wall_contours(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Floor {
|
impl Floor {
|
||||||
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Id<Primitive>, Fill)>(
|
fn render(&self, painter: &Painter, dungeon: &Dungeon, floor_z: i32) {
|
||||||
&self,
|
|
||||||
mut prim: F,
|
|
||||||
mut fill: G,
|
|
||||||
dungeon: &Dungeon,
|
|
||||||
floor_z: i32,
|
|
||||||
) {
|
|
||||||
// Calculate an AABB and corner for the AABB that covers the current floor.
|
// Calculate an AABB and corner for the AABB that covers the current floor.
|
||||||
let floor_corner = dungeon.origin + TILE_SIZE * self.tile_offset;
|
let floor_corner = dungeon.origin + TILE_SIZE * self.tile_offset;
|
||||||
let floor_aabb = Aabb {
|
let floor_aabb = Aabb {
|
||||||
@ -1035,7 +1029,7 @@ impl Floor {
|
|||||||
max: (floor_corner + TILE_SIZE * self.tiles.size())
|
max: (floor_corner + TILE_SIZE * self.tiles.size())
|
||||||
.with_z(floor_z + self.total_depth()),
|
.with_z(floor_z + self.total_depth()),
|
||||||
};
|
};
|
||||||
let floor_prim = prim(Primitive::Aabb(floor_aabb));
|
let floor_prim = painter.prim(Primitive::Aabb(floor_aabb));
|
||||||
|
|
||||||
// This is copied from `src/layer/mod.rs`. It should be moved into
|
// This is copied from `src/layer/mod.rs`. It should be moved into
|
||||||
// a util file somewhere
|
// a util file somewhere
|
||||||
@ -1060,7 +1054,7 @@ impl Floor {
|
|||||||
// produces a box of dots that will later get truncated to just the
|
// produces a box of dots that will later get truncated to just the
|
||||||
// floor, and the corresponding fill places the random kinds where the
|
// floor, and the corresponding fill places the random kinds where the
|
||||||
// mask says to
|
// mask says to
|
||||||
let floor_sprite = prim(Primitive::Sampling(
|
let floor_sprite = painter.prim(Primitive::sampling(
|
||||||
floor_prim,
|
floor_prim,
|
||||||
Box::new(|pos| RandomField::new(7331).chance(pos, 0.001)),
|
Box::new(|pos| RandomField::new(7331).chance(pos, 0.001)),
|
||||||
));
|
));
|
||||||
@ -1090,13 +1084,13 @@ impl Floor {
|
|||||||
// The way the ceiling is curved around corners and near hallways is intricate
|
// The way the ceiling is curved around corners and near hallways is intricate
|
||||||
// enough that it's easiest to do with a sampling primitive, this gets
|
// enough that it's easiest to do with a sampling primitive, this gets
|
||||||
// masked per room so that it's more efficient to query
|
// masked per room so that it's more efficient to query
|
||||||
let wall_contours = prim(Primitive::Sampling(floor_prim, {
|
let wall_contours = painter.prim(Primitive::sampling(floor_prim, {
|
||||||
let tiles = Arc::clone(&tiles);
|
let tiles = Arc::clone(&tiles);
|
||||||
make_wall_contours(tiles, floor_corner, floor_z, wall_thickness, tunnel_height)
|
make_wall_contours(tiles, floor_corner, floor_z, wall_thickness, tunnel_height)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// The surface 1 unit thicker than the walls is used to place the torches onto
|
// The surface 1 unit thicker than the walls is used to place the torches onto
|
||||||
let wall_contour_surface = prim(Primitive::Sampling(floor_prim, {
|
let wall_contour_surface = painter.prim(Primitive::sampling(floor_prim, {
|
||||||
let tiles = Arc::clone(&tiles);
|
let tiles = Arc::clone(&tiles);
|
||||||
make_wall_contours(
|
make_wall_contours(
|
||||||
tiles,
|
tiles,
|
||||||
@ -1135,27 +1129,27 @@ impl Floor {
|
|||||||
// tile grid, but offset by `lighting_offset`, used to space the torches
|
// tile grid, but offset by `lighting_offset`, used to space the torches
|
||||||
// on the walls/pillars/staircases
|
// on the walls/pillars/staircases
|
||||||
let lighting_mask = {
|
let lighting_mask = {
|
||||||
let mut lighting_mask_x = prim(Primitive::Empty);
|
let mut lighting_mask_x = painter.prim(Primitive::Empty);
|
||||||
let floor_w = floor_aabb.max.x - floor_aabb.min.x;
|
let floor_w = floor_aabb.max.x - floor_aabb.min.x;
|
||||||
for i in 0..floor_w / light_offset {
|
for i in 0..floor_w / light_offset {
|
||||||
let j = floor_corner.x + i * TILE_SIZE + light_offset;
|
let j = floor_corner.x + i * TILE_SIZE + light_offset;
|
||||||
let plane = prim(Primitive::Aabb(Aabb {
|
let plane = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: floor_aabb.min.with_x(j - 1),
|
min: floor_aabb.min.with_x(j - 1),
|
||||||
max: floor_aabb.max.with_x(j),
|
max: floor_aabb.max.with_x(j),
|
||||||
}));
|
}));
|
||||||
lighting_mask_x = prim(Primitive::Or(plane, lighting_mask_x));
|
lighting_mask_x = painter.prim(Primitive::or(plane, lighting_mask_x));
|
||||||
}
|
}
|
||||||
let mut lighting_mask_y = prim(Primitive::Empty);
|
let mut lighting_mask_y = painter.prim(Primitive::Empty);
|
||||||
let floor_h = floor_aabb.max.y - floor_aabb.min.y;
|
let floor_h = floor_aabb.max.y - floor_aabb.min.y;
|
||||||
for i in 0..floor_h / light_offset {
|
for i in 0..floor_h / light_offset {
|
||||||
let j = floor_corner.y + i * TILE_SIZE + light_offset;
|
let j = floor_corner.y + i * TILE_SIZE + light_offset;
|
||||||
let plane = prim(Primitive::Aabb(Aabb {
|
let plane = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: floor_aabb.min.with_y(j - 1),
|
min: floor_aabb.min.with_y(j - 1),
|
||||||
max: floor_aabb.max.with_y(j),
|
max: floor_aabb.max.with_y(j),
|
||||||
}));
|
}));
|
||||||
lighting_mask_y = prim(Primitive::Or(plane, lighting_mask_y));
|
lighting_mask_y = painter.prim(Primitive::or(plane, lighting_mask_y));
|
||||||
}
|
}
|
||||||
prim(Primitive::Xor(lighting_mask_x, lighting_mask_y))
|
painter.prim(Primitive::xor(lighting_mask_x, lighting_mask_y))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Declare collections of various disjoint primitives that need postprocessing
|
// Declare collections of various disjoint primitives that need postprocessing
|
||||||
@ -1184,18 +1178,18 @@ impl Floor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Sprites are contained to the level above the floor, and not within walls
|
// Sprites are contained to the level above the floor, and not within walls
|
||||||
let sprite_layer = prim(Primitive::Aabb(aabr_with_z(
|
let sprite_layer = painter.prim(Primitive::Aabb(aabr_with_z(
|
||||||
tile_aabr,
|
tile_aabr,
|
||||||
floor_z..floor_z + 1,
|
floor_z..floor_z + 1,
|
||||||
)));
|
)));
|
||||||
let sprite_layer = prim(Primitive::Diff(sprite_layer, wall_contours));
|
let sprite_layer = painter.prim(Primitive::diff(sprite_layer, wall_contours));
|
||||||
|
|
||||||
// Lights are 2 units above the floor, and aligned with the `lighting_mask` grid
|
// Lights are 2 units above the floor, and aligned with the `lighting_mask` grid
|
||||||
let lighting_plane = prim(Primitive::Aabb(aabr_with_z(
|
let lighting_plane = painter.prim(Primitive::Aabb(aabr_with_z(
|
||||||
tile_aabr,
|
tile_aabr,
|
||||||
floor_z + 1..floor_z + 2,
|
floor_z + 1..floor_z + 2,
|
||||||
)));
|
)));
|
||||||
let lighting_plane = prim(Primitive::And(lighting_plane, lighting_mask));
|
let lighting_plane = painter.prim(Primitive::and(lighting_plane, lighting_mask));
|
||||||
|
|
||||||
let mut chests = None;
|
let mut chests = None;
|
||||||
|
|
||||||
@ -1208,33 +1202,33 @@ impl Floor {
|
|||||||
let center = tile_center.with_z(floor_z);
|
let center = tile_center.with_z(floor_z);
|
||||||
let radius = TILE_SIZE as f32 / 2.0;
|
let radius = TILE_SIZE as f32 / 2.0;
|
||||||
let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth());
|
let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth());
|
||||||
let bb = prim(match kind {
|
let bb = painter.prim(match kind {
|
||||||
StairsKind::Spiral => Primitive::Cylinder(aabb),
|
StairsKind::Spiral => Primitive::Cylinder(aabb),
|
||||||
StairsKind::WallSpiral => Primitive::Aabb(aabb),
|
StairsKind::WallSpiral => Primitive::Aabb(aabb),
|
||||||
});
|
});
|
||||||
let stair = prim(Primitive::Sampling(bb, match kind {
|
let stair = painter.prim(Primitive::sampling(bb, match kind {
|
||||||
StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0),
|
StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0),
|
||||||
StairsKind::WallSpiral => wall_staircase(center, radius, 27.0),
|
StairsKind::WallSpiral => wall_staircase(center, radius, 27.0),
|
||||||
}));
|
}));
|
||||||
// Construct the lights that go inside the staircase, starting above the
|
// Construct the lights that go inside the staircase, starting above the
|
||||||
// ceiling to avoid placing them floating in mid-air
|
// ceiling to avoid placing them floating in mid-air
|
||||||
let mut lights = prim(Primitive::Empty);
|
let mut lights = painter.prim(Primitive::Empty);
|
||||||
for i in height..self.total_depth() {
|
for i in height..self.total_depth() {
|
||||||
if i % 9 == 0 {
|
if i % 9 == 0 {
|
||||||
let mut light = prim(Primitive::Aabb(Aabb {
|
let mut light = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: aabb.min.with_z(floor_z + i),
|
min: aabb.min.with_z(floor_z + i),
|
||||||
max: aabb.max.with_z(floor_z + i + 1),
|
max: aabb.max.with_z(floor_z + i + 1),
|
||||||
}));
|
}));
|
||||||
let inner = prim(Primitive::Aabb(Aabb {
|
let inner = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (aabb.min + Vec3::new(1, 1, 0)).with_z(floor_z + i),
|
min: (aabb.min + Vec3::new(1, 1, 0)).with_z(floor_z + i),
|
||||||
max: (aabb.max - Vec3::new(1, 1, 0)).with_z(floor_z + i + 1),
|
max: (aabb.max - Vec3::new(1, 1, 0)).with_z(floor_z + i + 1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
light = prim(Primitive::Diff(light, inner));
|
light = painter.prim(Primitive::diff(light, inner));
|
||||||
lights = prim(Primitive::Or(light, lights));
|
lights = painter.prim(Primitive::or(light, lights));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lights = prim(Primitive::And(lights, lighting_mask));
|
lights = painter.prim(Primitive::and(lights, lighting_mask));
|
||||||
stairs_bb.push(bb);
|
stairs_bb.push(bb);
|
||||||
stairs.push((stair, lights));
|
stairs.push((stair, lights));
|
||||||
}
|
}
|
||||||
@ -1245,7 +1239,7 @@ impl Floor {
|
|||||||
// Place chests with a random distribution based on the
|
// Place chests with a random distribution based on the
|
||||||
// room's loot density in valid sprite locations,
|
// room's loot density in valid sprite locations,
|
||||||
// filled based on the room's difficulty
|
// filled based on the room's difficulty
|
||||||
let chest_sprite = prim(Primitive::Sampling(
|
let chest_sprite = painter.prim(Primitive::sampling(
|
||||||
sprite_layer,
|
sprite_layer,
|
||||||
Box::new(move |pos| RandomField::new(seed).chance(pos, loot_density * 0.5)),
|
Box::new(move |pos| RandomField::new(seed).chance(pos, loot_density * 0.5)),
|
||||||
));
|
));
|
||||||
@ -1263,22 +1257,22 @@ impl Floor {
|
|||||||
// If a room has pits, place them
|
// If a room has pits, place them
|
||||||
if room.pits.is_some() {
|
if room.pits.is_some() {
|
||||||
// Make an air pit
|
// Make an air pit
|
||||||
let tile_pit = prim(Primitive::Aabb(aabr_with_z(
|
let tile_pit = painter.prim(Primitive::Aabb(aabr_with_z(
|
||||||
tile_aabr,
|
tile_aabr,
|
||||||
floor_z - 7..floor_z,
|
floor_z - 7..floor_z,
|
||||||
)));
|
)));
|
||||||
let tile_pit = prim(Primitive::Diff(tile_pit, wall_contours));
|
let tile_pit = painter.prim(Primitive::diff(tile_pit, wall_contours));
|
||||||
fill(tile_pit, Fill::Block(vacant));
|
painter.fill(tile_pit, Fill::Block(vacant));
|
||||||
|
|
||||||
// Fill with lava
|
// Fill with lava
|
||||||
let tile_lava = prim(Primitive::Aabb(aabr_with_z(
|
let tile_lava = painter.prim(Primitive::Aabb(aabr_with_z(
|
||||||
tile_aabr,
|
tile_aabr,
|
||||||
floor_z - 7..floor_z - 5,
|
floor_z - 7..floor_z - 5,
|
||||||
)));
|
)));
|
||||||
let tile_lava = prim(Primitive::Diff(tile_lava, wall_contours));
|
let tile_lava = painter.prim(Primitive::diff(tile_lava, wall_contours));
|
||||||
//pits.push(tile_pit);
|
//pits.push(tile_pit);
|
||||||
//pits.push(tile_lava);
|
//pits.push(tile_lava);
|
||||||
fill(tile_lava, Fill::Block(lava));
|
painter.fill(tile_lava, Fill::Block(lava));
|
||||||
}
|
}
|
||||||
if room
|
if room
|
||||||
.pits
|
.pits
|
||||||
@ -1287,12 +1281,12 @@ impl Floor {
|
|||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
let platform = prim(Primitive::Aabb(Aabb {
|
let platform = painter.prim(Primitive::Aabb(Aabb {
|
||||||
min: (tile_center - Vec2::broadcast(pillar_thickness - 1))
|
min: (tile_center - Vec2::broadcast(pillar_thickness - 1))
|
||||||
.with_z(floor_z - 7),
|
.with_z(floor_z - 7),
|
||||||
max: (tile_center + Vec2::broadcast(pillar_thickness)).with_z(floor_z),
|
max: (tile_center + Vec2::broadcast(pillar_thickness)).with_z(floor_z),
|
||||||
}));
|
}));
|
||||||
fill(platform, Fill::Block(stone));
|
painter.fill(platform, Fill::Block(stone));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a room has pillars, the current tile aligns with the pillar spacing, and
|
// If a room has pillars, the current tile aligns with the pillar spacing, and
|
||||||
@ -1313,13 +1307,13 @@ impl Floor {
|
|||||||
matches!(self.tiles.get(other_tile_pos), Some(Tile::Room(_)))
|
matches!(self.tiles.get(other_tile_pos), Some(Tile::Room(_)))
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
let mut pillar = prim(Primitive::Cylinder(Aabb {
|
let mut pillar = painter.prim(Primitive::Cylinder(Aabb {
|
||||||
min: (tile_center - Vec2::broadcast(pillar_thickness - 1))
|
min: (tile_center - Vec2::broadcast(pillar_thickness - 1))
|
||||||
.with_z(floor_z),
|
.with_z(floor_z),
|
||||||
max: (tile_center + Vec2::broadcast(pillar_thickness))
|
max: (tile_center + Vec2::broadcast(pillar_thickness))
|
||||||
.with_z(floor_z + height),
|
.with_z(floor_z + height),
|
||||||
}));
|
}));
|
||||||
let base = prim(Primitive::Cylinder(Aabb {
|
let base = painter.prim(Primitive::Cylinder(Aabb {
|
||||||
min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1))
|
min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1))
|
||||||
.with_z(floor_z),
|
.with_z(floor_z),
|
||||||
max: (tile_center + Vec2::broadcast(1 + pillar_thickness))
|
max: (tile_center + Vec2::broadcast(1 + pillar_thickness))
|
||||||
@ -1327,14 +1321,14 @@ impl Floor {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32;
|
let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32;
|
||||||
let mut lights =
|
let mut lights = painter
|
||||||
prim(Primitive::Scale(pillar, Vec2::broadcast(scale).with_z(1.0)));
|
.prim(Primitive::scale(pillar, Vec2::broadcast(scale).with_z(1.0)));
|
||||||
lights = prim(Primitive::And(lighting_plane, lights));
|
lights = painter.prim(Primitive::and(lighting_plane, lights));
|
||||||
// Only add the base (and shift the lights up)
|
// Only add the base (and shift the lights up)
|
||||||
// for boss-rooms pillars
|
// for boss-rooms pillars
|
||||||
if room.kind == RoomKind::Boss {
|
if room.kind == RoomKind::Boss {
|
||||||
lights = prim(Primitive::Translate(lights, 3 * Vec3::unit_z()));
|
lights = painter.prim(Primitive::translate(lights, 3 * Vec3::unit_z()));
|
||||||
pillar = prim(Primitive::Or(pillar, base));
|
pillar = painter.prim(Primitive::or(pillar, base));
|
||||||
}
|
}
|
||||||
pillars.push((tile_center, pillar, lights));
|
pillars.push((tile_center, pillar, lights));
|
||||||
}
|
}
|
||||||
@ -1348,39 +1342,39 @@ impl Floor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carve out the room's air inside the walls
|
// Carve out the room's air inside the walls
|
||||||
let tile_air = prim(Primitive::Aabb(aabr_with_z(
|
let tile_air = painter.prim(Primitive::Aabb(aabr_with_z(
|
||||||
tile_aabr,
|
tile_aabr,
|
||||||
floor_z..floor_z + height,
|
floor_z..floor_z + height,
|
||||||
)));
|
)));
|
||||||
let tile_air = prim(Primitive::Diff(tile_air, wall_contours));
|
let tile_air = painter.prim(Primitive::diff(tile_air, wall_contours));
|
||||||
fill(tile_air, Fill::Block(vacant));
|
painter.fill(tile_air, Fill::Block(vacant));
|
||||||
|
|
||||||
// Place torches on the walls with the aforementioned spacing
|
// Place torches on the walls with the aforementioned spacing
|
||||||
let sconces_layer = prim(Primitive::And(tile_air, lighting_plane));
|
let sconces_layer = painter.prim(Primitive::and(tile_air, lighting_plane));
|
||||||
let sconces_layer = prim(Primitive::And(sconces_layer, wall_contour_surface));
|
let sconces_layer = painter.prim(Primitive::and(sconces_layer, wall_contour_surface));
|
||||||
fill(sconces_layer, sconces_wall.clone());
|
painter.fill(sconces_layer, sconces_wall.clone());
|
||||||
|
|
||||||
// Defer chest/floor sprite placement
|
// Defer chest/floor sprite placement
|
||||||
if let Some((chest_sprite, chest_sprite_fill)) = chests {
|
if let Some((chest_sprite, chest_sprite_fill)) = chests {
|
||||||
let chest_sprite = prim(Primitive::Diff(chest_sprite, wall_contours));
|
let chest_sprite = painter.prim(Primitive::diff(chest_sprite, wall_contours));
|
||||||
sprites.push((chest_sprite, chest_sprite_fill));
|
sprites.push((chest_sprite, chest_sprite_fill));
|
||||||
}
|
}
|
||||||
|
|
||||||
let floor_sprite = prim(Primitive::And(sprite_layer, floor_sprite));
|
let floor_sprite = painter.prim(Primitive::and(sprite_layer, floor_sprite));
|
||||||
sprites.push((floor_sprite, floor_sprite_fill.clone()));
|
sprites.push((floor_sprite, floor_sprite_fill.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place a glowing purple septagonal star inscribed in a circle in the boss room
|
// Place a glowing purple septagonal star inscribed in a circle in the boss room
|
||||||
if let Some(boss_room_center) = boss_room_center {
|
if let Some(boss_room_center) = boss_room_center {
|
||||||
let magic_circle_bb = prim(Primitive::Cylinder(Aabb {
|
let magic_circle_bb = painter.prim(Primitive::Cylinder(Aabb {
|
||||||
min: (boss_room_center - 3 * Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z - 1),
|
min: (boss_room_center - 3 * Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z - 1),
|
||||||
max: (boss_room_center + 3 * Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z),
|
max: (boss_room_center + 3 * Vec2::broadcast(TILE_SIZE) / 2).with_z(floor_z),
|
||||||
}));
|
}));
|
||||||
let magic_circle = prim(Primitive::Sampling(
|
let magic_circle = painter.prim(Primitive::sampling(
|
||||||
magic_circle_bb,
|
magic_circle_bb,
|
||||||
inscribed_polystar(boss_room_center, 1.4 * TILE_SIZE as f32, 7),
|
inscribed_polystar(boss_room_center, 1.4 * TILE_SIZE as f32, 7),
|
||||||
));
|
));
|
||||||
fill(magic_circle, Fill::Block(stone_purple));
|
painter.fill(magic_circle, Fill::Block(stone_purple));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place pillars and pillar lights facing the pillars
|
// Place pillars and pillar lights facing the pillars
|
||||||
@ -1391,38 +1385,32 @@ impl Floor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fill(*lights, sconces_inward.clone());
|
painter.fill(*lights, sconces_inward.clone());
|
||||||
fill(*pillar, Fill::Block(stone));
|
painter.fill(*pillar, Fill::Block(stone));
|
||||||
}
|
}
|
||||||
// Carve out space for the stairs
|
// Carve out space for the stairs
|
||||||
for stair_bb in stairs_bb.iter() {
|
for stair_bb in stairs_bb.iter() {
|
||||||
fill(*stair_bb, Fill::Block(vacant));
|
painter.fill(*stair_bb, Fill::Block(vacant));
|
||||||
// Prevent sprites from floating above the stairs
|
// Prevent sprites from floating above the stairs
|
||||||
let stair_bb_up = prim(Primitive::Translate(*stair_bb, Vec3::unit_z()));
|
let stair_bb_up = painter.prim(Primitive::translate(*stair_bb, Vec3::unit_z()));
|
||||||
for (sprite, _) in sprites.iter_mut() {
|
for (sprite, _) in sprites.iter_mut() {
|
||||||
*sprite = prim(Primitive::Diff(*sprite, stair_bb_up));
|
*sprite = painter.prim(Primitive::diff(*sprite, stair_bb_up));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Place the stairs themselves, and lights within the stairwells
|
// Place the stairs themselves, and lights within the stairwells
|
||||||
for (stair, lights) in stairs.iter() {
|
for (stair, lights) in stairs.iter() {
|
||||||
fill(*lights, sconces_outward.clone());
|
painter.fill(*lights, sconces_outward.clone());
|
||||||
fill(*stair, Fill::Block(stone));
|
painter.fill(*stair, Fill::Block(stone));
|
||||||
}
|
}
|
||||||
// Place the sprites
|
// Place the sprites
|
||||||
for (sprite, sprite_fill) in sprites.into_iter() {
|
for (sprite, sprite_fill) in sprites.into_iter() {
|
||||||
fill(sprite, sprite_fill);
|
painter.fill(sprite, sprite_fill);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SiteStructure for Dungeon {
|
impl SiteStructure for Dungeon {
|
||||||
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Id<Primitive>, Fill)>(
|
fn render(&self, _site: &site2::Site, land: &Land, painter: &Painter) {
|
||||||
&self,
|
|
||||||
_site: &site2::Site,
|
|
||||||
land: &Land,
|
|
||||||
mut prim: F,
|
|
||||||
mut fill: G,
|
|
||||||
) {
|
|
||||||
let origin = (self.origin + Vec2::broadcast(TILE_SIZE / 2)).with_z(self.alt + ALT_OFFSET);
|
let origin = (self.origin + Vec2::broadcast(TILE_SIZE / 2)).with_z(self.alt + ALT_OFFSET);
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -1445,9 +1433,9 @@ impl SiteStructure for Dungeon {
|
|||||||
let entrances = entrances.read();
|
let entrances = entrances.read();
|
||||||
let entrance = entrances[self.seed as usize % entrances.len()].clone();
|
let entrance = entrances[self.seed as usize % entrances.len()].clone();
|
||||||
|
|
||||||
let entrance_prim = prim(Primitive::Prefab(Box::new(entrance.clone())));
|
let entrance_prim = painter.prim(Primitive::Prefab(Box::new(entrance.clone())));
|
||||||
let entrance_prim = prim(Primitive::Translate(entrance_prim, origin));
|
let entrance_prim = painter.prim(Primitive::translate(entrance_prim, origin));
|
||||||
fill(
|
painter.fill(
|
||||||
entrance_prim,
|
entrance_prim,
|
||||||
Fill::Prefab(Box::new(entrance), origin, self.seed),
|
Fill::Prefab(Box::new(entrance), origin, self.seed),
|
||||||
);
|
);
|
||||||
@ -1456,7 +1444,7 @@ impl SiteStructure for Dungeon {
|
|||||||
for floor in &self.floors {
|
for floor in &self.floors {
|
||||||
z -= floor.total_depth();
|
z -= floor.total_depth();
|
||||||
|
|
||||||
floor.render(&mut prim, &mut fill, self, z);
|
floor.render(painter, self, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
112
world/src/site2/plot/workshop.rs
Normal file
112
world/src/site2/plot/workshop.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::Land;
|
||||||
|
use common::terrain::{Block, BlockKind, SpriteKind};
|
||||||
|
use rand::prelude::*;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
/// Represents house data generated by the `generate()` method
|
||||||
|
pub struct Workshop {
|
||||||
|
/// Axis aligned bounding region for the house
|
||||||
|
bounds: Aabr<i32>,
|
||||||
|
/// Approximate altitude of the door tile
|
||||||
|
pub(crate) alt: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Workshop {
|
||||||
|
pub fn generate(
|
||||||
|
land: &Land,
|
||||||
|
_rng: &mut impl Rng,
|
||||||
|
site: &Site,
|
||||||
|
door_tile: Vec2<i32>,
|
||||||
|
door_dir: Vec2<i32>,
|
||||||
|
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(door_tile + door_dir)) as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Structure for Workshop {
|
||||||
|
fn render(&self, _site: &Site, _land: &Land, painter: &Painter) {
|
||||||
|
let brick = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
|
||||||
|
|
||||||
|
let base = self.alt + 1;
|
||||||
|
let center = (self.bounds.min + self.bounds.max) / 2;
|
||||||
|
|
||||||
|
// Base
|
||||||
|
painter
|
||||||
|
.aabb(Aabb {
|
||||||
|
min: self.bounds.min.with_z(base - 16),
|
||||||
|
max: self.bounds.max.with_z(base),
|
||||||
|
})
|
||||||
|
.fill(brick.clone());
|
||||||
|
|
||||||
|
let roof = base + 5;
|
||||||
|
|
||||||
|
painter
|
||||||
|
.aabb(Aabb {
|
||||||
|
min: self.bounds.min.with_z(base),
|
||||||
|
max: self.bounds.max.with_z(roof),
|
||||||
|
})
|
||||||
|
.clear();
|
||||||
|
|
||||||
|
// Supports
|
||||||
|
for pos in [
|
||||||
|
Vec2::new(self.bounds.min.x + 2, self.bounds.min.y + 2),
|
||||||
|
Vec2::new(self.bounds.max.x - 3, self.bounds.min.y + 2),
|
||||||
|
Vec2::new(self.bounds.min.x + 2, self.bounds.max.y - 3),
|
||||||
|
Vec2::new(self.bounds.max.x - 3, self.bounds.max.y - 3),
|
||||||
|
] {
|
||||||
|
painter
|
||||||
|
.line(pos.with_z(base), pos.with_z(roof), 1.0)
|
||||||
|
.fill(Fill::Block(Block::new(
|
||||||
|
BlockKind::Wood,
|
||||||
|
Rgb::new(55, 25, 8),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let roof_top = roof + 5;
|
||||||
|
|
||||||
|
// Roof
|
||||||
|
painter
|
||||||
|
.pyramid(Aabb {
|
||||||
|
min: (self.bounds.min + 1).with_z(roof),
|
||||||
|
max: (self.bounds.max - 1).with_z(roof_top),
|
||||||
|
})
|
||||||
|
.fill(Fill::Brick(BlockKind::Rock, Rgb::new(45, 28, 21), 24));
|
||||||
|
|
||||||
|
let chimney = roof_top + 2;
|
||||||
|
|
||||||
|
// Chimney
|
||||||
|
let chimney_radius = 3.0;
|
||||||
|
painter
|
||||||
|
.line(
|
||||||
|
center.with_z(base + 4),
|
||||||
|
center.with_z(chimney),
|
||||||
|
chimney_radius,
|
||||||
|
)
|
||||||
|
.fill(brick);
|
||||||
|
painter
|
||||||
|
.line(
|
||||||
|
center.with_z(base),
|
||||||
|
center.with_z(chimney + 2),
|
||||||
|
chimney_radius - 1.0,
|
||||||
|
)
|
||||||
|
.clear();
|
||||||
|
for x in -1..2 {
|
||||||
|
for y in -1..2 {
|
||||||
|
painter.sprite(
|
||||||
|
(center + Vec2::new(x, y)).with_z(base - 1),
|
||||||
|
SpriteKind::Ember,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -140,6 +140,7 @@ impl TileGrid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn grow_organic(
|
pub fn grow_organic(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
@ -192,6 +193,7 @@ pub enum TileKind {
|
|||||||
pub struct Tile {
|
pub struct Tile {
|
||||||
pub(crate) kind: TileKind,
|
pub(crate) kind: TileKind,
|
||||||
pub(crate) plot: Option<Id<Plot>>,
|
pub(crate) plot: Option<Id<Plot>>,
|
||||||
|
pub(crate) hard_alt: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tile {
|
impl Tile {
|
||||||
@ -199,20 +201,31 @@ impl Tile {
|
|||||||
Self {
|
Self {
|
||||||
kind: TileKind::Empty,
|
kind: TileKind::Empty,
|
||||||
plot: None,
|
plot: None,
|
||||||
|
hard_alt: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a tile that is not associated with any plot.
|
/// Create a tile that is not associated with any plot.
|
||||||
pub const fn free(kind: TileKind) -> Self { Self { kind, plot: None } }
|
pub const fn free(kind: TileKind) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
plot: None,
|
||||||
|
hard_alt: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
|
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
|
||||||
|
|
||||||
pub fn is_road(&self) -> bool { matches!(self.kind, 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 {
|
||||||
|
matches!(self.kind, TileKind::Hazard(_)) || self.is_building()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_building(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.kind,
|
self.kind,
|
||||||
TileKind::Hazard(_) | TileKind::Building | TileKind::Castle | TileKind::Wall(_)
|
TileKind::Building | TileKind::Castle | TileKind::Wall(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user