Rivers (hack, not great).

This commit is contained in:
Joshua Yanovski 2022-07-25 18:28:27 -07:00
parent 0b91f5ba90
commit bc4600cf14
26 changed files with 367 additions and 3410 deletions

View File

@ -76,94 +76,5 @@
lava: (184, 39, 0),
vein: (61, 229, 198),
),
site: (
castle: (),
dungeon: (
stone: (150, 150, 175),
),
settlement: (
building: (
archetype: (
keep: (
brick_base: (80, 80, 80),
floor_base: (80, 60, 10),
pole: (90, 70, 50),
flag: (
Evil: (80, 10, 130),
Good: (150, 20, 0),
),
stone: (
Evil: (65, 60, 55),
Good: (70, 75, 80),
),
),
house: (
foundation: (70, 70, 70),
floor: (100, 75, 50),
roof: (
Roof1: (0x99, 0x5E, 0x54),
Roof2: (0x43, 0x63, 0x64),
Roof3: (0x76, 0x6D, 0x68),
Roof4: (0x55, 0x25, 0x41),
Roof5: (0x52, 0x20, 0x20),
Roof6: (0x05, 0x3A, 0x40),
Roof7: (0xCC, 0x56, 0x3E),
Roof8: (0x05, 0x48, 0x40),
// (0x1D, 0x4D, 0x45),
// (0xB3, 0x7D, 0x60),
// (0xAC, 0x5D, 0x26),
// (0x32, 0x46, 0x6B),
// (0x2B, 0x19, 0x0F),
// (0x93, 0x78, 0x51),
// (0x92, 0x57, 0x24),
// (0x4A, 0x4E, 0x4E),
// (0x2F, 0x32, 0x47),
// (0x8F, 0x35, 0x43),
// (0x6D, 0x1E, 0x3A),
// (0x6D, 0xA7, 0x80),
// (0x4F, 0xA0, 0x95),
// (0xE2, 0xB9, 0x99),
// (0x7A, 0x30, 0x22),
// (0x4A, 0x06, 0x08),
// (0x8E, 0xB4, 0x57),
),
wall: (
Wall1: (200, 180, 150),
Wall2: (0xB8, 0xB4, 0xA4),
Wall3: (0x76, 0x6D, 0x68),
Wall4: (0xF3, 0xC9, 0x8F),
Wall5: (0xD3, 0xB7, 0x99),
Wall6: (0xE1, 0xAB, 0x91),
Wall7: (0x82, 0x57, 0x4C),
Wall8: (0xB9, 0x96, 0x77),
Wall9: (0xAE, 0x8D, 0x9C),
),
support: (
Support1: (65, 30, 0),
Support2: (0x35, 0x25, 0x26),
Support3: (0x53, 0x33, 0x13),
Support4: (0x65, 0x30, 0),
),
),
),
),
plot_town_path: (80, 40, 20),
plot_field_dirt: (55, 20, 5),
plot_field_mound: (40, 60, 10),
wall_low: (130, 100, 0),
wall_high :(90, 70, 50),
tower_color: (50, 50, 50),
// NOTE: Ideally these would be part of a make_case_elim, but we can't use it beacuse
// it doesn't support struct variants yet.
plot_dirt: (90, 70, 50),
plot_grass: (100, 200, 0),
plot_water: (100, 150, 250),
plot_town: (80, 40, 20),
// TODO: Add field furrow stuff.
),
),
site: (),
)

View File

@ -8,6 +8,14 @@ pub struct Grid<T> {
size: Vec2<i32>, // TODO: use u32
}
pub trait RowGen<'a>/*<'a, 'b: 'a>*/ {
type Row<'b> where Self: 'b;
type Col;
fn row_gen<'b>(&'b mut self, yoff: i32) -> Self::Row<'b>;
fn col_gen<'b>(row: &mut Self::Row<'b>, xoff: i32) -> Self::Col where Self: 'b;
}
impl<T> Grid<T> {
pub fn from_raw(size: Vec2<i32>, raw: impl Into<Vec<T>>) -> Self {
let cells = raw.into();
@ -26,18 +34,24 @@ impl<T> Grid<T> {
}
#[inline]
pub fn populate_by_row<Row: FnMut(i32) -> Col, Col: FnMut(i32) -> T, const X: u32, const Y: u32>(
mut row: Row,
pub fn populate_by_row<'c, Gen/*, Row*//*, Col*/, const X: u32, const Y: u32>(
gen: &mut Gen,
/* mut row_gen: Row, */
/* mut col_gen: Col, */
) -> Self
where
T: Clone + Default,
/* for<'a> Gen: RowGen<'a, 'c>, */
Gen: RowGen<'c, Col=T>,
/* for<'a> Row: FnMut(&'a mut Gen, i32) -> <Gen as RowGen<'c>/*<'a, 'c>*/>::Row<'a>, */
// for<'a, 'b> Col: FnMut(&'b mut <Gen as RowGen<'c>/*<'a, 'c>*/>::Row<'a>, i32) -> T,
[(); {X as usize}]:
{
let mut cells = vec![T::default(); {X as usize * Y as usize}];
cells.array_chunks_mut::<{X as usize}>().enumerate().for_each(|(y, cells)| {
let mut col = row(y as i32);
let mut row = gen.row_gen(y as i32);
cells.iter_mut().enumerate().for_each(|(x, cell)| {
*cell = col(x as i32);
*cell = Gen::col_gen(&mut row, x as i32);
});
});
Self {

View File

@ -62,7 +62,7 @@ use specs::{
use std::{str::FromStr, sync::Arc};
use vek::*;
use wiring::{Circuit, Wire, WireNode, WiringAction, WiringActionEffect, WiringElement};
use world::util::Sampler;
use world::util::SamplerMut;
use common::comp::Alignment;
use tracing::{error, info, warn};
@ -2722,7 +2722,7 @@ fn handle_debug_column(
};
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
let msg_generator = |/*calendar*/| {
let sampler = server.world.sample_columns(chunk_pos, server.index.as_index_ref())?;
let mut sampler = server.world.sample_columns(chunk_pos, server.index.as_index_ref())?;
let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?;
let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?;
let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?;

View File

@ -150,6 +150,114 @@ impl Entity {
}
}
fn merchant_loadout(
loadout_builder: LoadoutBuilder,
economy: Option<&trade::SiteInformation>,
) -> LoadoutBuilder {
use common::comp::{
inventory::{
slot::ArmorSlot,
trade_pricing::TradePricing,
},
Item,
};
use hashbrown::HashMap;
use trade::Good;
fn sort_wares(bag: &mut [Item]) {
use common::comp::item::TagExampleInfo;
bag.sort_by(|a, b| {
a.quality()
.cmp(&b.quality())
// sort by kind
.then(
Ord::cmp(
a.tags().first().map_or("", |tag| tag.name()),
b.tags().first().map_or("", |tag| tag.name()),
)
)
// sort by name
.then(Ord::cmp(&a.name(), &b.name()))
});
}
fn transfer(wares: &mut Vec<Item>, bag: &mut Item) {
let capacity = bag.slots().len();
for (s, w) in bag
.slots_mut()
.iter_mut()
.zip(wares.drain(0..wares.len().min(capacity)))
{
*s = Some(w);
}
}
let rng = &mut rand::thread_rng();
let mut backpack = Item::new_from_asset_expect("common.items.armor.misc.back.backpack");
let mut bag1 = Item::new_from_asset_expect("common.items.armor.misc.bag.sturdy_red_backpack");
let mut bag2 = Item::new_from_asset_expect("common.items.armor.misc.bag.sturdy_red_backpack");
let mut bag3 = Item::new_from_asset_expect("common.items.armor.misc.bag.sturdy_red_backpack");
let mut bag4 = Item::new_from_asset_expect("common.items.armor.misc.bag.sturdy_red_backpack");
let slots = backpack.slots().len() + 4 * bag1.slots().len();
let mut stockmap: HashMap<Good, f32> = economy
.map(|e| e.unconsumed_stock.clone())
.unwrap_or_default();
// modify stock for better gameplay
// TODO: currently econsim spends all its food on population, resulting in none
// for the players to buy; the `.max` is temporary to ensure that there's some
// food for sale at every site, to be used until we have some solution like NPC
// houses as a limit on econsim population growth
stockmap
.entry(Good::Food)
.and_modify(|e| *e = e.max(10_000.0))
.or_insert(10_000.0);
// Reduce amount of potions so merchants do not oversupply potions.
// TODO: Maybe remove when merchants and their inventories are rtsim?
// Note: Likely without effect now that potions are counted as food
stockmap
.entry(Good::Potions)
.and_modify(|e| *e = e.powf(0.25));
// It's safe to truncate here, because coins clamped to 3000 max
// also we don't really want negative values here
stockmap
.entry(Good::Coin)
.and_modify(|e| *e = e.min(rng.gen_range(1000.0..3000.0)));
// assume roughly 10 merchants sharing a town's stock (other logic for coins)
stockmap
.iter_mut()
.filter(|(good, _amount)| **good != Good::Coin)
.for_each(|(_good, amount)| *amount *= 0.1);
// Fill bags with stuff according to unclaimed stock
let mut wares: Vec<Item> =
TradePricing::random_items(&mut stockmap, slots as u32, true, true, 16)
.iter()
.map(|(n, a)| {
let mut i = Item::new_from_asset_expect(n);
i.set_amount(*a)
.map_err(|_| tracing::error!("merchant loadout amount failure"))
.ok();
i
})
.collect();
sort_wares(&mut wares);
transfer(&mut wares, &mut backpack);
transfer(&mut wares, &mut bag1);
transfer(&mut wares, &mut bag2);
transfer(&mut wares, &mut bag3);
transfer(&mut wares, &mut bag4);
loadout_builder
.with_asset_expect("common.loadout.village.merchant", rng)
.back(Some(backpack))
.bag(ArmorSlot::Bag1, Some(bag1))
.bag(ArmorSlot::Bag2, Some(bag2))
.bag(ArmorSlot::Bag3, Some(bag3))
.bag(ArmorSlot::Bag4, Some(bag4))
}
/// Escape hatch for runtime creation of loadout not covered by entity
/// config.
// NOTE: Signature is part of interface of EntityInfo
@ -159,7 +267,7 @@ impl Entity {
let kind = self.kind;
if let RtSimEntityKind::Merchant = kind {
|l, trade| l.with_creator(world::site::settlement::merchant_loadout, trade)
|l, trade| l.with_creator(/*world::site::settlement*/Self::merchant_loadout, trade)
} else {
|l, _| l
}
@ -174,7 +282,7 @@ impl Entity {
.civs()
.sites
.iter()
.filter(|s| s.1.is_settlement() || s.1.is_castle())
.filter(|s| s.1.is_settlement()/* || s.1.is_castle() */)
.min_by_key(|(_, site)| {
let wpos = site.center.map2(TerrainChunk::RECT_SIZE, |e, sz| {
e * sz as i32 + sz as i32 / 2
@ -332,7 +440,7 @@ impl Entity {
.civs()
.sites
.iter()
.filter(|s| s.1.is_settlement() | s.1.is_castle())
.filter(|s| s.1.is_settlement()/* | s.1.is_castle() */)
.filter(|_| thread_rng().gen_range(0i32..4) == 0)
.min_by_key(|(_, site)| {
let wpos = site.center.map2(TerrainChunk::RECT_SIZE, |e, sz| {

View File

@ -547,42 +547,44 @@ impl<'a> Widget for Map<'a> {
.color(TEXT_COLOR)
.set(state.ids.show_towns_text, ui);
// Castles
Image::new(self.imgs.mmap_site_castle)
.down_from(state.ids.show_towns_img, 10.0)
.w_h(20.0, 20.0)
.set(state.ids.show_castles_img, ui);
if Button::image(if show_castles {
self.imgs.checkbox_checked
} else {
self.imgs.checkbox
})
.w_h(18.0, 18.0)
.hover_image(if show_castles {
self.imgs.checkbox_checked_mo
} else {
self.imgs.checkbox_mo
})
.press_image(if show_castles {
self.imgs.checkbox_checked
} else {
self.imgs.checkbox_press
})
.right_from(state.ids.show_castles_img, 10.0)
.set(state.ids.show_castles_box, ui)
.was_clicked()
{
events.push(Event::SettingsChange(MapShowCastles(!show_castles)));
if false {
Image::new(self.imgs.mmap_site_castle)
.down_from(state.ids.show_towns_img, 10.0)
.w_h(20.0, 20.0)
.set(state.ids.show_castles_img, ui);
if Button::image(if show_castles {
self.imgs.checkbox_checked
} else {
self.imgs.checkbox
})
.w_h(18.0, 18.0)
.hover_image(if show_castles {
self.imgs.checkbox_checked_mo
} else {
self.imgs.checkbox_mo
})
.press_image(if show_castles {
self.imgs.checkbox_checked
} else {
self.imgs.checkbox_press
})
.right_from(state.ids.show_castles_img, 10.0)
.set(state.ids.show_castles_box, ui)
.was_clicked()
{
events.push(Event::SettingsChange(MapShowCastles(!show_castles)));
}
Text::new(i18n.get("hud.map.castles"))
.right_from(state.ids.show_castles_box, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.show_castles_box)
.color(TEXT_COLOR)
.set(state.ids.show_castles_text, ui);
}
Text::new(i18n.get("hud.map.castles"))
.right_from(state.ids.show_castles_box, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.show_castles_box)
.color(TEXT_COLOR)
.set(state.ids.show_castles_text, ui);
// Dungeons
Image::new(self.imgs.mmap_site_dungeon)
.down_from(state.ids.show_castles_img, 10.0)
.down_from(state.ids.show_towns_img, 10.0)
.w_h(20.0, 20.0)
.set(state.ids.show_dungeons_img, ui);
if Button::image(if show_dungeons {

View File

@ -117,7 +117,7 @@ impl<'a> BlockGen<'a> {
} else {
Some(Block::new(BlockKind::Earth, col))
}
} else if wposf.z as i32 <= alt as i32 {
} else if wposf.z as i32 <= alt as i32/* && wposf.z as f32 >= water_level */{
let grass_factor = (wposf.z as f32 - (alt - grass_depth))
.div(grass_depth)
.sqrt();
@ -134,6 +134,7 @@ impl<'a> BlockGen<'a> {
}/* else if snow_cover {
//if temp < CONFIG.snow_temp + 0.031 {
Block::new(BlockKind::Snow, col.map(|e| (e * 255.0) as u8))
//}
}*/ else {
Block::new(BlockKind::Grass, col.map(|e| (e * 255.0) as u8))
}

View File

@ -6,7 +6,7 @@ use crate::{
land::Land,
layer::spot::Spot,
sim::{SimChunk, WorldSim},
util::{Grid, Sampler},
util::{Grid, SamplerMut},
TerrainGrid,
};
use common::{
@ -71,7 +71,7 @@ impl<'a> CanvasInfo<'a> {
pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
self.col_inner(wpos).map(Cow::Borrowed).or_else(|| {
let chunk_pos = TerrainGrid::chunk_key(wpos);
let column_gen = ColumnGen::new(self.chunks(), chunk_pos, self.index())?;
let mut column_gen = ColumnGen::new(self.chunks(), chunk_pos, self.index())?;
Some(Cow::Owned(column_gen.get((
wpos/* ,

View File

@ -5,7 +5,7 @@ mod econ;
use crate::{
config::CONFIG,
sim::WorldSim,
site::{namegen::NameGen, Castle, /*Settlement, */Site as WorldSite/*, Tree */},
site::{namegen::NameGen, /*Castle, *//*Settlement, */Site as WorldSite/*, Tree */},
site2,
util::{attempt, seed_expan, DHashMap, NEIGHBORS},
Index, Land,
@ -117,7 +117,8 @@ impl Civs {
for _ in 0..initial_civ_count * 3 {
attempt(5, || {
let (kind, avoid) = match ctx.rng.gen_range(0..64) {
0..=5 => (SiteKind::Castle, (&castle_enemies, 40)),
/* 0..=5 => (SiteKind::Castle, (&castle_enemies, 40)), */
// 0..=5 => (SiteKind::Citadel, (&castle_enemies, 20)),
28..=31 => {
/*if index.features().site2_giant_trees */{
(SiteKind::GiantTree, (&tree_enemies, 40))
@ -126,17 +127,16 @@ impl Civs {
}*/
},
32..=37 => (SiteKind::Gnarling, (&gnarling_enemies, 40)),
// 32..=37 => (SiteKind::Citadel, 5, (&castle_enemies, 20)),
_ => (SiteKind::Dungeon, (&dungeon_enemies, 40)),
};
let loc = find_site_loc(&mut ctx, avoid, kind)?;
match kind {
SiteKind::Castle => {
/* SiteKind::Castle => {
gnarling_enemies.push(loc);
dungeon_enemies.push(loc);
tree_enemies.push(loc);
castle_enemies.push(loc);
},
}, */
SiteKind::Gnarling => {
castle_enemies.push(loc);
dungeon_enemies.push(loc);
@ -168,7 +168,7 @@ impl Civs {
let (radius, flatten_radius) = match &site.kind {
/* SiteKind::Settlement => (32i32, 10.0f32), */
SiteKind::Dungeon => (8i32, 3.0f32),
SiteKind::Castle => (16i32, 5.0),
/* SiteKind::Castle => (16i32, 5.0), */
SiteKind::Refactor => (32i32, 10.0),
SiteKind::CliffTown => (32i32, 10.0),
/* SiteKind::Tree => (12i32, 8.0), */
@ -177,9 +177,9 @@ impl Civs {
SiteKind::Citadel => (16i32, 0.0),
};
let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind {
/* let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind {
/* SiteKind::Settlement => (10.0, 6, true), */
SiteKind::Castle => (0.0, 6, true),
/* SiteKind::Castle => (0.0, 6, true), */
_ => (0.0, 0, false),
};
@ -220,7 +220,7 @@ impl Civs {
}
});
}
}
} */
}
// Place sites in world
@ -243,9 +243,9 @@ impl Civs {
&mut rng,
wpos,
)),
SiteKind::Castle => {
/* SiteKind::Castle => {
WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng))
},
}, */
SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city(
&Land::from_sim(ctx.sim),
&mut rng,
@ -976,7 +976,7 @@ impl Civs {
SiteKind::Refactor
/* | SiteKind::Settlement */
| SiteKind::CliffTown
| SiteKind::Castle
/* | SiteKind::Castle */
)
})
.map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt()))
@ -984,7 +984,7 @@ impl Civs {
.collect::<Vec<_>>();
nearby.sort_by_key(|(_, dist)| *dist as i32);
if let SiteKind::Refactor | /* SiteKind::Settlement | */SiteKind::CliffTown | SiteKind::Castle =
if let SiteKind::Refactor | /* SiteKind::Settlement | */SiteKind::CliffTown/* | SiteKind::Castle*/ =
self.sites[site].kind
{
for (nearby, _) in nearby.into_iter().take(5) {
@ -1234,7 +1234,7 @@ impl fmt::Display for Site {
pub enum SiteKind {
/* Settlement, */
Dungeon,
Castle,
/* Castle, */
Refactor,
CliffTown,
/* Tree, */
@ -1354,7 +1354,7 @@ impl SiteKind {
&& chunk.near_cliffs()
&& suitable_for_town(4.0)
},
SiteKind::Castle => {
/* SiteKind::Castle => {
if chunk.tree_density > 0.4 || chunk.river.near_water() || chunk.near_cliffs() {
return false;
}
@ -1380,7 +1380,7 @@ impl SiteKind {
}
}
true
},
}, */
SiteKind::Refactor/* | SiteKind::Settlement*/ => suitable_for_town(6.7),
_ => true,
}
@ -1405,7 +1405,7 @@ impl Site {
matches!(self.kind, /*SiteKind::Settlement | */SiteKind::Refactor)
}
pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) }
/* pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) } */
}
#[derive(PartialEq, Debug, Clone)]

View File

@ -2,7 +2,7 @@ use crate::{
all::ForestKind,
sim::{local_cells, Cave, Path, RiverData, RiverKind, SimChunk, WorldSim},
site::SpawnRules,
util::{RandomField, Sampler},
util::{RandomField, RowGen, Sampler, SamplerMut},
IndexRef, CONFIG,
};
use common::{
@ -25,7 +25,7 @@ use tracing::error;
use vek::*;
pub struct ColumnGen1D<'a, 'b> {
pub(crate) parent: &'b ColumnGen<'a>,
pub(crate) parent: &'b mut ColumnGen<'a>,
wpos_row: i32,
wposy: f32,
cubic_y: Cubic<f32>,
@ -80,8 +80,19 @@ pub struct ColumnGen<'a> {
pub(crate) neighbor_river_data: Vec<(Vec2<i32>, &'a SimChunk, &'a RiverData)>,
pub(crate) homogeneous_water_level: Option<Option<f32>>,
// pub(crate) spawn_rules: SpawnRules,
/// Scratch data
river_scratch: /*[RiverScratchData<'a>; 7 * 7]*/Vec<RiverScratchData<'a>>,
}
type RiverScratchData<'a> =
(
Vec2<i32>,
&'a SimChunk,
&'a RiverData,
Option<(Vec2<f64>, Vec2<f64>, f64, (f64, (Vec2<f64>, Vec3<Vec2<f64>>), &'a SimChunk))>,
);
#[derive(Deserialize)]
pub struct Colors {
pub cold_grass: (f32, f32, f32),
@ -534,20 +545,43 @@ impl<'a> ColumnGen<'a> {
.fold(SpawnRules::default(), |a, b| a.combine(b)); */
let my_chunk_idx = vec2_as_uniform_idx(sim.map_size_lg(), chunk_pos);
let mut homogeneous_water_level = true;
let river_kind = sim_chunk.river.river_kind;
let neighbor_river_data =
local_cells(sim.map_size_lg(), my_chunk_idx).filter_map(|neighbor_idx: usize| {
let neighbor_pos = uniform_idx_as_vec2(sim.map_size_lg(), neighbor_idx);
let neighbor_chunk = sim.get(neighbor_pos)?;
match (neighbor_chunk.river.river_kind, river_kind) {
(None, None) => {},
/* (_, Some(RiverKind::Ocean)) => {}, */
/* (_, Some(RiverKind::Lake { .. })) => {
return None;
}, */
(_, Some(RiverKind::River { .. })) | (Some(RiverKind::River { .. }), _) => {
homogeneous_water_level = false;
},
(_, _) if (2 * (neighbor_pos - chunk_pos) - 1).magnitude_squared() > 18/*(neighbor_pos - chunk_pos).map(i32::abs).reduce_max() > 2*/ => {
return None;
},
/* (Some(RiverKind::Ocean), Some(RiverKind::Ocean)) => {}, */
(Some(RiverKind::Lake { .. } | RiverKind::Ocean), Some(RiverKind::Lake { .. } | RiverKind::Ocean)) if neighbor_chunk.water_alt == sim_chunk.water_alt => {},
(_, _) => {
homogeneous_water_level = false;
}
}
/* if /*(neighbor_pos - chunk_pos).map(i32::abs).reduce_max() <= 1 &&*/
!(neighbor_chunk.river.river_kind.is_some() == river_kind.is_some() && neighbor_chunk.water_alt == sim_chunk.water_alt) {
homogeneous_water_level = false;
} */
neighbor_chunk.river.river_kind.and(
Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river))
)
}).collect::<Vec<_>>();
let river_kind = sim_chunk.river.river_kind;
let homogeneous_water_level = neighbor_river_data
/* let homogeneous_water_level = neighbor_river_data
.iter()
.filter(|(pos, _, _)| (pos - chunk_pos).map(i32::abs).reduce_max() <= 1)
.all(|(pos, chunk, river)| river.river_kind == river_kind ||
chunk.water_alt == sim_chunk.water_alt);
chunk.water_alt == sim_chunk.water_alt); */
let base_sea_level = CONFIG.sea_level - 1.0 + 0.01;
let homogeneous_water_level = if homogeneous_water_level {
match river_kind {
@ -585,20 +619,20 @@ impl<'a> ColumnGen<'a> {
sim_chunk,
catmull_rom_gen,
river_scratch: Vec::with_capacity(neighbor_river_data.len()),
neighbor_river_data,
homogeneous_water_level,
// spawn_rules,
})
}
pub fn eval_at_row<'b>(&'b self, wpos_row: i32) -> ColumnGen1D<'a, 'b> {
pub fn eval_at_row<'b>(&'b mut self, wpos_row: i32) -> ColumnGen1D<'a, 'b> {
let rel_chunk_pos = wpos_row - self.chunk_pos.y * TerrainChunkSize::RECT_SIZE.y as i32;
// let rel_chunk_pos = wpos_row - chunk_pos.y * TerrainChunkSize::RECT_SIZE.as_::<i32>();
let wposy = rel_chunk_pos/*.y*/ as f32 / TerrainChunkSize::RECT_SIZE.y as f32;
let cubic_y = cubic(wposy);
ColumnGen1D {
parent: self,
wpos_row,
wposy,
cubic_y,
@ -620,25 +654,27 @@ impl<'a> ColumnGen<'a> {
surface_veg_spline: self.surface_veg_spline.eval_at_row(cubic_y),
cliff_height_spline: self.cliff_height_spline.eval_at_row(cubic_y),
/* spawn_rules, */
parent: self,
}
}
}
impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
impl<'a, 'b, 'c> SamplerMut<'a, 'c> for ColumnGen1D<'a, 'b> {
type Index = /*(Vec2<i32>, IndexRef<'a>, Option<&'a Calendar>)*//*Vec2<i32>*/i32;
type Sample = /*Option<*/ColumnSample/*<'a>*//*>*/;
/// Precondition: wpos should be inside the chunk associated with self.chunk_pos.
fn get(&self, /*(wpos, index, calendar)*/wpos_col: Self::Index) -> ColumnSample/*<'a>*/ {
let &Self {
parent,
fn get(&'c mut self, /*(wpos, index, calendar)*/wpos_col: Self::Index) -> ColumnSample/*<'a>*/ {
let &mut Self {
parent: ref mut parent,
wpos_row,
wposy,
cubic_y,
..
} = self;
let &ColumnGen {
let &mut ColumnGen {
sim,
chunk_pos,
index,
@ -646,8 +682,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
sim_chunk,
homogeneous_water_level,
ref neighbor_river_data,
ref mut river_scratch,
..
} = parent;
} = *parent;
let wpos = Vec2::new(wpos_col, wpos_row);
let wposf = wpos.map(|e| e as f64);
@ -728,7 +765,11 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
};
let small_nz = |wposf: Vec2<f64>| {
sim.gen_ctx.small_nz.get(wposf.into_array()) as f32
sim.gen_ctx.fast_small_nz.get(wposf.into_array()) as f32
// 0.0f32
};
let small_nz_f64 = |wposf: Vec2<f64>| {
sim.gen_ctx.fast_small_nz.get(wposf.into_array())/* as f64*/
// 0.0f32
};
@ -754,11 +795,15 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
let riverless_alt = alt;
let (alt, water_level, water_dist, water_dist_) = if let Some(water_level) = homogeneous_water_level {
(alt, water_level.unwrap_or(base_sea_level), water_level.and(Some(0.0)), None::<f32>)
(water_level.map(|water_level| alt.min(water_level - 1.0)).unwrap_or(alt), water_level.unwrap_or(base_sea_level), water_level.and(Some(0.0)), /*None::<f32>*/water_level.and(Some(0.0)))
} else {
(alt, base_sea_level, None::<f32>, None::<f32>)
/* let lake_width = (TerrainChunkSize::RECT_SIZE.x as f64 * 2.0f64.sqrt()) + 6.0;
let neighbor_river_data = neighbor_river_data
/* let mut lake_inner = || */{
/* return (alt, base_sea_level, None::<f32>, None::<f32>); */
let lake_width = (TerrainChunkSize::RECT_SIZE.x as f64 * 2.0f64.sqrt()) + 6.0;
let lake_width_noise = small_nz_f64((wposf.map(|e| e as f64).div(32.0)));
river_scratch.clear();
river_scratch.extend(
/*let neighbor_river_data = */neighbor_river_data
.iter()
.copied()
.map(|(posj, chunkj, river)| {
@ -786,7 +831,7 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
let downhill_pos = downhill_pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
e.div_euclid(sz as i32)
});
let neighbor_wpos = posj.map(|e| e as f64) * neighbor_coef + neighbor_coef * 0.5;
let neighbor_wpos = posj.map(|e| e as f64) * neighbor_coef/* + neighbor_coef * 0.5*/;
let direction = neighbor_wpos - downhill_wpos;
let river_width_min = if let RiverKind::River { cross_section } = kind {
cross_section.x as f64
@ -858,7 +903,7 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
return (posj, chunkj, river, None);
}
let neighbor_pass_wpos =
neighbor_pass_pos.map(|e| e as f64) + neighbor_coef * 0.5;
neighbor_pass_pos.map(|e| e as f64)/* + neighbor_coef * 0.5*/;
let neighbor_pass_pos = neighbor_pass_pos
.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
let coeffs = river_spline_coeffs(
@ -874,13 +919,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
Some(RiverKind::Lake { .. } | RiverKind::Ocean)
) {
let water_chunk = posj.map(|e| e as f64);
let lake_width_noise = sim
.gen_ctx
.small_nz
.get((wposf.map(|e| e as f64).div(32.0)).into_array());
let water_aabr = Aabr {
min: water_chunk * neighbor_coef + 4.0 - lake_width_noise * 8.0,
max: (water_chunk + 1.0) * neighbor_coef - 4.0
min: (water_chunk - 0.5) * neighbor_coef + 4.0 - lake_width_noise * 8.0,
max: (water_chunk + 0.5) * neighbor_coef - 4.0
+ lake_width_noise * 8.0,
};
let pos = water_aabr.projected_point(wposf);
@ -925,13 +966,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
},
RiverKind::Ocean => {
let water_chunk = posj.map(|e| e as f64);
let lake_width_noise = sim
.gen_ctx
.small_nz
.get((wposf.map(|e| e as f64).div(32.0)).into_array());
let water_aabr = Aabr {
min: water_chunk * neighbor_coef + 4.0 - lake_width_noise * 8.0,
max: (water_chunk + 1.0) * neighbor_coef - 4.0 + lake_width_noise * 8.0,
min: (water_chunk - 0.5) * neighbor_coef + 4.0 - lake_width_noise * 8.0,
max: (water_chunk + 0.5) * neighbor_coef - 4.0 + lake_width_noise * 8.0,
};
let pos = water_aabr.projected_point(wposf);
(
@ -958,7 +995,7 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
lake_width * 0.5
};
let river_width_noise =
(sim.gen_ctx.small_nz.get((river_pos.div(16.0)).into_array()))
(small_nz_f64(river_pos.div(16.0)))
.max(-1.0)
.min(1.0)
.mul(0.5)
@ -989,7 +1026,8 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
)),
)
})
.collect::<Vec<_>>();
/*.collect::<Vec<_>>()*/);
let neighbor_river_data = &*river_scratch;
debug_assert!(sim_chunk.water_alt >= CONFIG.sea_level);
@ -1277,7 +1315,7 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
// has been carefully designed to handle innumeral edge cases. Please
// test any changes to this code extremely well to avoid regressions: some
// edge cases are very rare indeed!
let alt = neighbor_river_data.into_iter().fold(
let alt = neighbor_river_data.into_iter().copied().fold(
WeightedSum::default().with(riverless_alt, 1.0),
|alt, (river_chunk_idx, river_chunk, river, dist_info)| match (
river.river_kind,
@ -1429,7 +1467,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
f32::MIN
});
(alt, water_level, water_dist) */
(alt, water_level, water_dist, water_dist)
}/*;
lake_inner()*/
};
let riverless_alt_delta = small_nz(wposf_turb.div(200.0 * (32.0 / TerrainChunkSize::RECT_SIZE.x as f64)))
@ -1464,7 +1504,7 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
let cliff_offset = cliff * cliff_height;
let riverless_alt_delta = riverless_alt_delta + (cliff - 0.5) * cliff_height;
let warp_factor = water_dist_.map_or(1.0, |d| ((d - 0.0) / 64.0).clamped(0.0, 1.0));
let warp_factor = water_dist_.map_or(1.0, |d| ((d - 0.0) / 19.0).clamped(0.0, 1.0));
// NOTE: To disable warp, uncomment this line.
// let warp_factor = 0.0;
@ -1832,17 +1872,32 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
}
}
impl<'a, 'b> Sampler<'a, 'b> for ColumnGen<'a> {
impl<'a, 'b> SamplerMut<'a, 'b> for ColumnGen<'a> {
type Index = /*(Vec2<i32>, IndexRef<'a>, Option<&'a Calendar>)*/Vec2<i32>;
type Sample = ColumnSample/*<'a>*/;
/// Precondition: wpos should be inside the chunk associated with self.chunk_pos.
fn get(&self, /*(wpos, index, calendar)*/wpos: Self::Index) -> ColumnSample/*<'a>*/ {
let col = self.eval_at_row(wpos.y);
fn get(&'b mut self, /*(wpos, index, calendar)*/wpos: Self::Index) -> ColumnSample/*<'a>*/ {
let mut col = self.eval_at_row(wpos.y);
col.get(wpos.x)
}
}
impl<'a/*: 'b, 'b*/> RowGen<'a> for ColumnGen<'a> {
type Row<'b> = ColumnGen1D<'a, 'b> where Self: 'b;
type Col = ColumnSample;
#[inline(always)]
fn row_gen<'b>(&'b mut self, offs_y: i32) -> Self::Row<'b> {
self.eval_at_row(/* - grid_border*/offs_y + self.chunk_pos.y * TerrainChunkSize::RECT_SIZE.y as i32)
}
#[inline(always)]
fn col_gen<'b>(row: &mut Self::Row<'b>, offs_x: i32) -> Self::Col where Self: 'b {
row.get(/* - grid_border + */offs_x + row.parent.chunk_pos.x * TerrainChunkSize::RECT_SIZE.y as i32)
}
}
#[derive(Clone, Default)]
pub struct ColumnSample/*<'a>*/ {
pub alt: f32,

View File

@ -1061,8 +1061,6 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
max: aabr.min + 1,
}; */
canvas.foreach_col_area(aabr, /*Aabr { min: canvas.wpos(), max: canvas.wpos() + 1 }, */|canvas, wpos2d, col| {
let underwater = col.water_level.floor() > col.alt;
let /*kind*/(kind, water_mode) = /* scatter.iter().enumerate().find_map(
|(
i,
@ -1099,7 +1097,7 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
.unwrap_or(density); */
if density > 0.0
&& rng.gen::<f32>() < density //RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
&& matches!(&water_mode, Underwater | Floating) == underwater
&& matches!(&water_mode, Underwater | Floating) == { col.water_level.floor() > col.alt }
{
let block_kind = canvas
.get(Vec3::new(wpos2d.x, wpos2d.y, col.alt as i32))

View File

@ -558,7 +558,7 @@ pub fn apply_spots_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) {
};
// Blit base structure
if let Some(base_structures) = spot_config.base_structures {
let structures = Structure::load_group(base_structures).read();
let structures = Structure::load_group(base_structures).get();
let structure = structures.choose(&mut rng).unwrap();
let origin = spot_wpos2d.with_z(
canvas

View File

@ -11,6 +11,7 @@
arbitrary_enum_discriminant,
associated_const_equality,
bool_to_option,
generic_associated_types,
label_break_value,
option_zip,
portable_simd,
@ -52,7 +53,7 @@ use crate::{
index::Index,
layer::spot::Spot,
site::{SiteKind, SpawnRules},
util::{Grid, Sampler},
util::{Grid, Sampler, SamplerMut},
};
use common::{
assets,
@ -162,7 +163,7 @@ impl World {
_ => 0,
},
},
civ::SiteKind::Castle => world_msg::SiteKind::Castle,
/* civ::SiteKind::Castle => world_msg::SiteKind::Castle, */
/* civ::SiteKind::Tree | */civ::SiteKind::GiantTree => world_msg::SiteKind::Tree,
// TODO: Maybe change?
civ::SiteKind::Gnarling | civ::SiteKind::Citadel => world_msg::SiteKind::Gnarling,
@ -198,7 +199,7 @@ impl World {
chunk_pos: Vec2<i32>,
index: IndexRef<'a>,
/* calendar: Option<&'_ Calendar>, */
) -> Option<impl for<'b> Sampler<'a, 'b,
) -> Option<impl for<'b> SamplerMut<'a, 'b,
Index = /*(Vec2<i32>, IndexRef, Option<&'_ Calendar>)*/Vec2<i32>,
Sample = ColumnSample/*<'a>*/,
> + 'a> {
@ -227,9 +228,9 @@ impl World {
}
#[allow(clippy::result_unit_err)]
pub fn generate_chunk(
&self,
index: IndexRef,
pub fn generate_chunk<'a>(
&'a self,
index: IndexRef<'a>,
chunk_pos: Vec2<i32>,
// TODO: misleading name
mut should_continue: impl FnMut() -> bool,
@ -239,7 +240,8 @@ impl World {
let calendar = self.sim.calendar.as_ref();
// FIXME: Deal with this properly if it's not okay to exit early.
let sampler = self.sample_blocks(chunk_pos, index/*, calendar*/);
/* let mut sampler = self.sample_blocks(chunk_pos, index/*, calendar*/); */
let mut sampler = ColumnGen::new(&self.sim, chunk_pos, index/*, calendar*/).map(BlockGen::new);
// dbg!(&sampler.column_gen.chaos_spline);
let air = Block::air(SpriteKind::Empty);
@ -272,18 +274,45 @@ impl World {
let grid_border = /*4*/0;
let chunk_wpos2d = chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
let chunk_center_wpos2d = chunk_wpos2d + TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2);
let column_gen = &mut sampler.column_gen;
/* #[inline(always)]
fn constrain_<'a>(chunk_wpos2d_y: i32) ->
impl for<'b> FnMut(&'b mut ColumnGen<'a>, i32) -> crate::column::ColumnGen1D<'a, 'b>
{
move |column_gen: &mut crate::column::ColumnGen<'a>, offs_y| {
column_gen.eval_at_row(chunk_wpos2d_y/* - grid_border*/ + offs_y)
}
} */
/* #[inline(always)]
fn constrain<'a>(chunk_wpos2d_x: i32) ->
impl for<'b> FnMut(&mut <crate::column::ColumnGen<'a> as crate::util::RowGen<'a>>::Row<'b>, i32) -> ColumnSample
{
move |column_gen, offs_x| {
column_gen.get(chunk_wpos2d_x/* - grid_border*/ + offs_x)
}
} */
/* fn constrain<'a, F, S, T>(f: F) -> F
where
F: for<'b, 'c> FnMut(&'c mut crate::column::ColumnGen1D<'a, 'b>, S) -> T,
{
f
} */
let zcache_grid: Grid<ColumnSample> =
Grid::populate_by_row::<_, _, {TerrainChunkSize::RECT_SIZE.x}, {TerrainChunkSize::RECT_SIZE.y}>(
Grid::populate_by_row::<ColumnGen<'a>, /*_, */{TerrainChunkSize::RECT_SIZE.x}, {TerrainChunkSize::RECT_SIZE.y}>(
/* TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, */
|offs_y| {
let column_gen = sampler.column_gen.eval_at_row(chunk_wpos2d.y/* - grid_border*/ + offs_y);
move |offs_x| {
/* ZCache {
sample: */column_gen.get(chunk_wpos2d.x/* - grid_border*/ + offs_x)/*,
calendar: column_gen.parent.sim.calendar.as_ref(),
}*/
}
},
column_gen,
/*constrain_(chunk_wpos2d.y)
/*/*constrain_(*/move |column_gen: &mut crate::column::ColumnGen<'a>, offs_y| {
/*let mut column_gen = */column_gen.eval_at_row(chunk_wpos2d.y/* - grid_border*/ + offs_y)
}/*)*/*/,*/
/* /*constrain(*/move |column_gen: &mut crate::column::ColumnGen1D<'a, '_>, offs_x| {
/* ZCache {
sample: */column_gen.get(chunk_wpos2d.x/* - grid_border*/ + offs_x)/*,
calendar: column_gen.parent.sim.calendar.as_ref(),
}*/
}/*)*/*//*constrain(chunk_wpos2d.x),*/
/* |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs/*, index, calendar*/)/*None*/ */
);

View File

@ -33,7 +33,7 @@ use crate::{
layer::spot::Spot,
site::Site,
util::{
seed_expan, DHashSet, FastNoise, FastNoise2d, RandomField, Sampler, StructureGen2d,
seed_expan, DHashSet, FastNoise, FastNoise2d, RandomField, Sampler, SamplerMut, StructureGen2d,
CARDINALS, LOCALITY, NEIGHBORS,
},
IndexRef, CONFIG,
@ -136,6 +136,7 @@ pub(crate) struct GenCtx {
pub uplift_nz: Worley,
pub fast_hill_nz: Value,
pub fast_small_nz: /*FastNoise2d*/Value,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -536,6 +537,7 @@ impl WorldSim {
let uplift_scale = 128.0;
let uplift_turb_scale = uplift_scale / 4.0;
let hill_nz_seed;
let small_nz_seed;
// NOTE: Changing order will significantly change WorldGen, so try not to!
let gen_ctx = GenCtx {
@ -561,7 +563,7 @@ impl WorldSim {
.set_lacunarity(2.0)
.set_seed(rng.gen()),
small_nz: BasicMulti::new().set_octaves(2).set_seed(rng.gen()),
small_nz: BasicMulti::new().set_octaves(2).set_seed( { small_nz_seed = rng.gen(); small_nz_seed }),
rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()),
tree_nz: BasicMulti::new()
.set_octaves(12)
@ -572,6 +574,7 @@ impl WorldSim {
structure_gen: StructureGen2d::new(rng.gen(), 24, 10),
_big_structure_gen: StructureGen2d::new(rng.gen(), 768, 512),
fast_small_nz: Value::new().set_seed(small_nz_seed)/*FastNoise2d::new(small_nz_seed)*/,
_region_gen: StructureGen2d::new(rng.gen(), 400, 96),
humid_nz: Billow::new()
.set_octaves(9)
@ -1835,7 +1838,7 @@ impl WorldSim {
}
}
pub fn get_base_z(&self, chunk_pos: Vec2<i32>) -> Option<f32> {
/* pub fn get_base_z(&self, chunk_pos: Vec2<i32>) -> Option<f32> {
let in_bounds = chunk_pos
.map2(self.map_size_lg().chunks(), |e, sz| {
e > 0 && e < sz as i32 - 2
@ -1859,7 +1862,7 @@ impl WorldSim {
}
})
.fold(None, |a: Option<f32>, x| a.map(|a| a.min(x)).or(Some(x)))
}
} */
pub fn get_interpolated<T, F>(&self, pos: Vec2<i32>, mut f: F) -> Option<T>
where
@ -2309,7 +2312,7 @@ impl SimChunk {
Some(
uniform_idx_as_vec2(map_size_lg, downhill_pre as usize)
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32)
+ TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2),
/* + TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2) */,
)
};
@ -2483,7 +2486,7 @@ impl SimChunk {
self.water_alt > self.alt || self.river.river_kind.is_some()
}
pub fn get_base_z(&self) -> f32 { self.alt/* - self.chaos * 16.0 - self.cliff_height.max(0.0) * /*3.125.powf(1.5)*/5.625 - 4.0/*/* * 50.0 - 16.0 */- 7.5*/*/ - 6.0 }
/* pub fn get_base_z(&self) -> f32 { self.alt/* - self.chaos * 16.0 - self.cliff_height.max(0.0) * /*3.125.powf(1.5)*/5.625 - 4.0/*/* * 50.0 - 16.0 */- 7.5*/*/ - 6.0 } */
pub fn get_biome(&self) -> BiomeKind {
let savannah_hum_temp = [0.05..0.55, 0.3..1.6];

View File

@ -215,8 +215,8 @@ pub fn uniform_noise<F: Float + Send>(
pub fn local_cells(map_size_lg: MapSizeLg, posi: usize) -> impl Clone + Iterator<Item = usize> {
let pos = uniform_idx_as_vec2(map_size_lg, posi);
// NOTE: want to keep this such that the chunk index is in ascending order!
let grid_size = 3i32;
let grid_bounds = 2 * grid_size + 1;
let grid_size = 2i32;
let grid_bounds = 2 * (grid_size + 1)/* + 1*/;
(0..grid_bounds * grid_bounds)
.into_iter()
.map(move |index| {

View File

@ -1,441 +0,0 @@
use super::SpawnRules;
use crate::{
column::ColumnSample,
sim::WorldSim,
site::{
namegen::NameGen,
settlement::building::{
archetype::keep::{Attr, FlagColor, Keep as KeepArchetype, StoneColor},
Archetype, Ori,
},
},
IndexRef,
};
use common::{
generation::ChunkSupplement,
terrain::{Block, BlockKind, SpriteKind},
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
};
use core::f32;
use rand::prelude::*;
use serde::Deserialize;
use vek::*;
struct Keep {
offset: Vec2<i32>,
locus: i32,
storeys: i32,
is_tower: bool,
alt: i32,
}
struct Tower {
offset: Vec2<i32>,
alt: i32,
}
pub struct Castle {
name: String,
origin: Vec2<i32>,
//seed: u32,
radius: i32,
towers: Vec<Tower>,
keeps: Vec<Keep>,
rounded_towers: bool,
ridged: bool,
flags: bool,
evil: bool,
}
pub struct GenCtx<'a, R: Rng> {
sim: Option<&'a mut WorldSim>,
rng: &'a mut R,
}
#[derive(Deserialize)]
pub struct Colors;
impl Castle {
pub fn generate(wpos: Vec2<i32>, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self {
let ctx = GenCtx { sim, rng };
let boundary_towers = ctx.rng.gen_range(5..10);
let keep_count = ctx.rng.gen_range(1..4);
let boundary_noise = ctx.rng.gen_range(-2i32..8).max(1) as f32;
let radius = 150;
let this = Self {
name: {
let name = NameGen::location(ctx.rng).generate();
match ctx.rng.gen_range(0..6) {
0 => format!("Fort {}", name),
1 => format!("{} Citadel", name),
2 => format!("{} Castle", name),
3 => format!("{} Stronghold", name),
4 => format!("{} Fortress", name),
_ => format!("{} Keep", name),
}
},
origin: wpos,
// alt: ctx
// .sim
// .as_ref()
// .and_then(|sim| sim.get_alt_approx(wpos))
// .unwrap_or(0.0) as i32
// + 6,
//seed: ctx.rng.gen(),
radius,
towers: (0..boundary_towers)
.map(|i| {
let angle = (i as f32 / boundary_towers as f32) * f32::consts::PI * 2.0;
let dir = Vec2::new(angle.cos(), angle.sin());
let dist = radius as f32 + ((angle * boundary_noise).sin() - 1.0) * 40.0;
let mut offset = (dir * dist).map(|e| e as i32);
// Try to move the tower around until it's not intersecting a path
for i in (1..80).step_by(5) {
if ctx
.sim
.as_ref()
.and_then(|sim| sim.get_nearest_path(wpos + offset))
.map(|(dist, _, _, _)| dist > 24.0)
.unwrap_or(true)
{
break;
}
offset = (dir * dist)
.map(|e| (e + ctx.rng.gen_range(-1.0..1.0) * i as f32) as i32);
}
Tower {
offset,
alt: ctx
.sim
.as_ref()
.and_then(|sim| sim.get_alt_approx(wpos + offset))
.unwrap_or(0.0) as i32
+ 2,
}
})
.collect(),
rounded_towers: ctx.rng.gen(),
ridged: ctx.rng.gen(),
flags: ctx.rng.gen(),
evil: ctx.rng.gen(),
keeps: (0..keep_count)
.map(|i| {
let angle = (i as f32 / keep_count as f32) * f32::consts::PI * 2.0;
let dir = Vec2::new(angle.cos(), angle.sin());
let dist =
(radius as f32 + ((angle * boundary_noise).sin() - 1.0) * 40.0) * 0.3;
let locus = ctx.rng.gen_range(20..26);
let offset = (dir * dist).map(|e| e as i32);
let storeys = ctx.rng.gen_range(1..8).clamped(3, 5);
Keep {
offset,
locus,
storeys,
is_tower: true,
alt: ctx
.sim
.as_ref()
.and_then(|sim| sim.get_alt_approx(wpos + offset))
.unwrap_or(0.0) as i32
+ 2,
}
})
.collect(),
};
this
}
pub fn name(&self) -> &str { &self.name }
pub fn contains_point(&self, wpos: Vec2<i32>) -> bool {
let lpos = wpos - self.origin;
for i in 0..self.towers.len() {
let tower0 = &self.towers[i];
let tower1 = &self.towers[(i + 1) % self.towers.len()];
if lpos.determine_side(Vec2::zero(), tower0.offset) > 0
&& lpos.determine_side(Vec2::zero(), tower1.offset) <= 0
&& lpos.determine_side(tower0.offset, tower1.offset) > 0
{
return true;
}
}
false
}
pub fn get_origin(&self) -> Vec2<i32> { self.origin }
pub fn radius(&self) -> f32 { 200.0 }
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules {
trees: wpos.distance_squared(self.origin) > self.radius.pow(2),
..SpawnRules::default()
}
}
pub fn apply_to<'a>(
&'a self,
index: IndexRef,
wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
) {
for y in 0..vol.size_xy().y as i32 {
for x in 0..vol.size_xy().x as i32 {
let offs = Vec2::new(x, y);
let wpos2d = wpos2d + offs;
let rpos = wpos2d - self.origin;
if rpos.magnitude_squared() > (self.radius + 64).pow(2) {
continue;
}
let col_sample = if let Some(col) = get_column(offs) {
col
} else {
continue;
};
// Inner ground
if self.contains_point(wpos2d) {
let surface_z = col_sample.alt as i32;
for z in -5..3 {
let pos = Vec3::new(offs.x, offs.y, surface_z + z);
if z > 0 {
if vol.get(pos).unwrap().kind() != BlockKind::Water {
// TODO: Take environment into account.
let _ = vol.set(pos, Block::air(SpriteKind::Empty));
}
} else {
let _ = vol.set(
pos,
Block::new(
BlockKind::Earth,
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
),
);
}
}
}
let (wall_dist, wall_pos, wall_alt, wall_ori, _towers) = (0..self.towers.len())
.map(|i| {
let tower0 = &self.towers[i];
let tower1 = &self.towers[(i + 1) % self.towers.len()];
let wall = LineSegment2 {
start: tower0.offset.map(|e| e as f32),
end: tower1.offset.map(|e| e as f32),
};
let projected = wall
.projected_point(rpos.map(|e| e as f32))
.map(|e| e.floor() as i32);
let tower0_dist = tower0
.offset
.map(|e| e as f32)
.distance(projected.map(|e| e as f32));
let tower1_dist = tower1
.offset
.map(|e| e as f32)
.distance(projected.map(|e| e as f32));
let tower_lerp = tower0_dist / (tower0_dist + tower1_dist);
let wall_ori = if (tower0.offset.x - tower1.offset.x).abs()
< (tower0.offset.y - tower1.offset.y).abs()
{
Ori::North
} else {
Ori::East
};
(
wall.distance_to_point(rpos.map(|e| e as f32)) as i32,
projected,
Lerp::lerp(tower0.alt as f32, tower1.alt as f32, tower_lerp) as i32,
wall_ori,
[tower0, tower1],
)
})
.min_by_key(|x| x.0)
.unwrap();
let border_pos = (wall_pos - rpos).map(|e| e.abs());
let wall_rpos = if wall_ori == Ori::East {
rpos
} else {
rpos.yx()
};
let head_space = col_sample
.path
.map(|(dist, _, path, _)| path.head_space(dist))
.unwrap_or(0);
let wall_sample = /* if let Some(col) = get_column(offs + wall_pos - rpos) {
col
} else {
col_sample
}*/col_sample;
// Make sure particularly weird terrain doesn't give us underground walls
let wall_alt = wall_alt + (wall_sample.alt as i32 - wall_alt - 10).max(0);
let keep_archetype = KeepArchetype {
flag_color: if self.evil {
FlagColor::Evil
} else {
FlagColor::Good
},
stone_color: if self.evil {
StoneColor::Evil
} else {
StoneColor::Good
},
};
for z in -10..64 {
let wpos = Vec3::new(wpos2d.x, wpos2d.y, col_sample.alt as i32 + z);
// Boundary wall
let wall_z = wpos.z - wall_alt;
if z < head_space {
continue;
}
let mut mask = keep_archetype.draw(
index,
Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt,
wall_dist,
border_pos,
rpos - wall_pos,
wall_z,
wall_ori,
4,
0,
&Attr {
storeys: 2,
is_tower: false,
flag: self.flags,
ridged: false,
rounded: true,
has_doors: false,
},
);
// Boundary towers
for tower in &self.towers {
let tower_wpos = Vec3::new(
self.origin.x + tower.offset.x,
self.origin.y + tower.offset.y,
tower.alt,
);
let tower_locus = 10;
let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs());
mask = mask.resolve_with(keep_archetype.draw(
index,
if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() {
wpos - tower_wpos
} else {
Vec3::new(
wpos.y - tower_wpos.y,
wpos.x - tower_wpos.x,
wpos.z - tower_wpos.z,
)
},
border_pos.reduce_max() - tower_locus,
Vec2::new(border_pos.reduce_min(), border_pos.reduce_max()),
(wpos - tower_wpos).xy(),
wpos.z - tower.alt,
if border_pos.x > border_pos.y {
Ori::East
} else {
Ori::North
},
tower_locus,
0,
&Attr {
storeys: 3,
is_tower: true,
flag: self.flags,
ridged: self.ridged,
rounded: self.rounded_towers,
has_doors: false,
},
));
}
// Keeps
for keep in &self.keeps {
let keep_wpos = Vec3::new(
self.origin.x + keep.offset.x,
self.origin.y + keep.offset.y,
keep.alt,
);
let border_pos = (keep_wpos - wpos).xy().map(|e| e.abs());
mask = mask.resolve_with(keep_archetype.draw(
index,
if (keep_wpos.x - wpos.x).abs() < (keep_wpos.y - wpos.y).abs() {
wpos - keep_wpos
} else {
Vec3::new(
wpos.y - keep_wpos.y,
wpos.x - keep_wpos.x,
wpos.z - keep_wpos.z,
)
},
border_pos.reduce_max() - keep.locus,
Vec2::new(border_pos.reduce_min(), border_pos.reduce_max()),
(wpos - keep_wpos).xy(),
wpos.z - keep.alt,
if border_pos.x > border_pos.y {
Ori::East
} else {
Ori::North
},
keep.locus,
0,
&Attr {
storeys: keep.storeys,
is_tower: keep.is_tower,
flag: self.flags,
ridged: self.ridged,
rounded: self.rounded_towers,
has_doors: true,
},
));
}
if let Some(block) = mask.finish() {
let _ = vol.set(Vec3::new(offs.x, offs.y, wpos.z), block);
}
}
}
}
}
pub fn apply_supplement<'a>(
&'a self,
// NOTE: Used only for dynamic elements like chests and entities!
_dynamic_rng: &mut impl Rng,
_wpos2d: Vec2<i32>,
_get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
_supplement: &mut ChunkSupplement,
) {
// TODO
}
}

View File

@ -113,7 +113,7 @@ impl Environment {
}
{
let mut castles = EconStatistics::default();
/* let mut castles = EconStatistics::default(); */
let mut towns = EconStatistics::default();
let mut dungeons = EconStatistics::default();
for site in index.sites.ids() {
@ -123,7 +123,7 @@ impl Environment {
towns += site.economy.pop
},
SiteKind::Dungeon(_) => dungeons += site.economy.pop,
SiteKind::Castle(_) => castles += site.economy.pop,
/* SiteKind::Castle(_) => castles += site.economy.pop, */
/* SiteKind::Tree(_) => (), */
SiteKind::GiantTree(_) => (),
SiteKind::Gnarling(_) => {},
@ -137,14 +137,14 @@ impl Environment {
towns.sum / (towns.count as f32)
);
}
if castles.valid() {
/* if castles.valid() {
info!(
"Castles {:.0}-{:.0} avg {:.0}",
castles.min,
castles.max,
castles.sum / (castles.count as f32)
);
}
} */
if dungeons.valid() {
info!(
"Dungeons {:.0}-{:.0} avg {:.0}",
@ -389,9 +389,9 @@ mod tests {
crate::site::SiteKind::Dungeon(_) => {
common::terrain::site::SitesKind::Dungeon
},
crate::site::SiteKind::Castle(_) => {
/* crate::site::SiteKind::Castle(_) => {
common::terrain::site::SitesKind::Castle
},
}, */
_ => common::terrain::site::SitesKind::Void,
},
};
@ -417,9 +417,9 @@ mod tests {
// loading on demand using the public API. There is no way to set
// the name, do we care?
let mut settlement = match i.kind {
common::terrain::site::SitesKind::Castle => crate::site::Site::castle(
/* common::terrain::site::SitesKind::Castle => crate::site::Site::castle(
crate::site::Castle::generate(wpos, None, &mut rng),
),
), */
common::terrain::site::SitesKind::Dungeon => {
crate::site::Site::dungeon(crate::site2::Site::generate_dungeon(
&crate::Land::empty(),

View File

@ -1,13 +1,13 @@
mod block_mask;
mod castle;
// mod castle;
pub mod economy;
pub mod namegen;
pub mod settlement;
// pub mod settlement;
// mod tree;
// Reexports
pub use self::{
block_mask::BlockMask, castle::Castle, economy::Economy/* , settlement::Settlement, tree::Tree, */
block_mask::BlockMask, /* castle::Castle, */economy::Economy/* , settlement::Settlement, tree::Tree, */
};
use crate::{column::ColumnSample, site2, Canvas};
@ -17,11 +17,7 @@ use serde::Deserialize;
use vek::*;
#[derive(Deserialize)]
pub struct Colors {
pub castle: castle::Colors,
pub dungeon: site2::plot::dungeon::Colors,
pub settlement: settlement::Colors,
}
pub struct Colors;
pub struct SpawnRules {
pub trees: bool,
@ -62,7 +58,7 @@ pub struct Site {
pub enum SiteKind {
/* Settlement(Settlement), */
Dungeon(site2::Site),
Castle(Castle),
/* Castle(Castle), */
Refactor(site2::Site),
CliffTown(site2::Site),
/* Tree(tree::Tree), */
@ -92,12 +88,12 @@ impl Site {
}
}
pub fn castle(c: Castle) -> Self {
/* pub fn castle(c: Castle) -> Self {
Self {
kind: SiteKind::Castle(c),
economy: Economy::default(),
}
}
} */
pub fn refactor(s: site2::Site) -> Self {
Self {
@ -131,7 +127,7 @@ impl Site {
match &self.kind {
/* SiteKind::Settlement(s) => s.radius(), */
SiteKind::Dungeon(d) => d.radius(),
SiteKind::Castle(c) => c.radius(),
/* SiteKind::Castle(c) => c.radius(), */
SiteKind::Refactor(s) => s.radius(),
SiteKind::CliffTown(ct) => ct.radius(),
/* SiteKind::Tree(t) => t.radius(), */
@ -144,7 +140,7 @@ impl Site {
match &self.kind {
/* SiteKind::Settlement(s) => s.get_origin(), */
SiteKind::Dungeon(d) => d.origin,
SiteKind::Castle(c) => c.get_origin(),
/* SiteKind::Castle(c) => c.get_origin(), */
SiteKind::Refactor(s) => s.origin,
SiteKind::CliffTown(ct) => ct.origin,
/* SiteKind::Tree(t) => t.origin, */
@ -157,7 +153,7 @@ impl Site {
match &self.kind {
/* SiteKind::Settlement(s) => s.spawn_rules(wpos), */
SiteKind::Dungeon(d) => d.spawn_rules(wpos),
SiteKind::Castle(c) => c.spawn_rules(wpos),
/* SiteKind::Castle(c) => c.spawn_rules(wpos), */
SiteKind::Refactor(s) => s.spawn_rules(wpos),
SiteKind::CliffTown(ct) => ct.spawn_rules(wpos),
/* SiteKind::Tree(t) => t.spawn_rules(wpos), */
@ -170,7 +166,7 @@ impl Site {
match &self.kind {
/* SiteKind::Settlement(s) => s.name(), */
SiteKind::Dungeon(d) => d.name(),
SiteKind::Castle(c) => c.name(),
/* SiteKind::Castle(c) => c.name(), */
SiteKind::Refactor(s) => s.name(),
SiteKind::CliffTown(ct) => ct.name(),
/* SiteKind::Tree(_) => "Giant Tree", */
@ -196,11 +192,11 @@ impl Site {
pub fn apply_to<'a>(&'a self, canvas: &mut Canvas<'a>, arena: &mut bumpalo::Bump, dynamic_rng: &mut impl Rng) {
let info = canvas.info();
let get_col = |wpos| info.col(wpos + info.wpos);
// let get_col = |wpos| info.col(wpos + info.wpos);
match &self.kind {
/* SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), */
SiteKind::Dungeon(d) => d.render(canvas, arena, dynamic_rng),
SiteKind::Castle(c) => c.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk),
/* SiteKind::Castle(c) => /*c.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk)*/{}, */
SiteKind::Refactor(s) => s.render(canvas, arena, dynamic_rng),
SiteKind::CliffTown(ct) => ct.render(canvas, arena, dynamic_rng),
/* SiteKind::Tree(t) => t.render(canvas, dynamic_rng), */
@ -226,7 +222,7 @@ impl Site {
s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy)
}, */
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement),
SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
/* SiteKind::Castle(c) => /*c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement)*/{}, */
SiteKind::Refactor(_) => {},
SiteKind::CliffTown(_) => {},
/* SiteKind::Tree(_) => {}, */

View File

@ -1,673 +0,0 @@
#![allow(dead_code)]
use super::{super::skeleton::*, Archetype};
use crate::{
site::BlockMask,
util::{RandomField, Sampler},
IndexRef,
};
use common::{
calendar::{Calendar, CalendarEvent},
make_case_elim,
terrain::{Block, BlockKind, SpriteKind},
};
use rand::prelude::*;
use serde::Deserialize;
use vek::*;
#[derive(Deserialize)]
pub struct Colors {
pub foundation: (u8, u8, u8),
pub floor: (u8, u8, u8),
pub roof: roof_color::PureCases<(u8, u8, u8)>,
pub wall: wall_color::PureCases<(u8, u8, u8)>,
pub support: support_color::PureCases<(u8, u8, u8)>,
}
pub struct ColorTheme {
roof: RoofColor,
wall: WallColor,
support: SupportColor,
}
make_case_elim!(
roof_color,
#[repr(u32)]
#[derive(Clone, Copy)]
pub enum RoofColor {
Roof1 = 0,
Roof2 = 1,
Roof3 = 2,
Roof4 = 3,
Roof5 = 4,
Roof6 = 5,
Roof7 = 6,
}
);
make_case_elim!(
wall_color,
#[repr(u32)]
#[derive(Clone, Copy)]
pub enum WallColor {
Wall1 = 0,
Wall2 = 1,
Wall3 = 2,
Wall4 = 3,
Wall5 = 4,
Wall6 = 5,
Wall7 = 6,
Wall8 = 7,
Wall9 = 8,
}
);
make_case_elim!(
support_color,
#[repr(u32)]
#[derive(Clone, Copy)]
pub enum SupportColor {
Support1 = 0,
Support2 = 1,
Support3 = 2,
Support4 = 3,
}
);
const ROOF_COLORS: [RoofColor; roof_color::NUM_VARIANTS] = [
RoofColor::Roof1,
RoofColor::Roof2,
RoofColor::Roof3,
RoofColor::Roof4,
RoofColor::Roof4,
RoofColor::Roof6,
RoofColor::Roof7,
];
const WALL_COLORS: [WallColor; wall_color::NUM_VARIANTS] = [
WallColor::Wall1,
WallColor::Wall2,
WallColor::Wall3,
WallColor::Wall4,
WallColor::Wall5,
WallColor::Wall6,
WallColor::Wall7,
WallColor::Wall8,
WallColor::Wall9,
];
const SUPPORT_COLORS: [SupportColor; support_color::NUM_VARIANTS] = [
SupportColor::Support1,
SupportColor::Support2,
SupportColor::Support3,
SupportColor::Support4,
];
pub struct House {
pub colors: ColorTheme,
pub noise: RandomField,
pub roof_ribbing: bool,
pub roof_ribbing_diagonal: bool,
pub christmas_decorations: bool,
}
#[derive(Copy, Clone)]
pub enum Pillar {
None,
Chimney(i32),
Tower(i32),
}
#[derive(Copy, Clone)]
pub enum RoofStyle {
Hip,
Gable,
Rounded,
}
#[derive(Copy, Clone)]
pub enum StoreyFill {
None,
Upper,
All,
}
impl StoreyFill {
fn has_lower(&self) -> bool { matches!(self, StoreyFill::All) }
fn has_upper(&self) -> bool { !matches!(self, StoreyFill::None) }
}
#[derive(Copy, Clone)]
pub struct Attr {
pub central_supports: bool,
pub storey_fill: StoreyFill,
pub roof_style: RoofStyle,
pub mansard: i32,
pub pillar: Pillar,
pub levels: i32,
pub window: SpriteKind,
}
impl Attr {
pub fn generate<R: Rng>(rng: &mut R, _locus: i32) -> Self {
Self {
central_supports: rng.gen(),
storey_fill: match rng.gen_range(0..3) {
0 => StoreyFill::None,
1 => StoreyFill::Upper,
_ => StoreyFill::All,
},
roof_style: match rng.gen_range(0..3) {
0 => RoofStyle::Hip,
1 => RoofStyle::Gable,
_ => RoofStyle::Rounded,
},
mansard: rng.gen_range(-7..4).max(0),
pillar: match rng.gen_range(0..4) {
0 => Pillar::Chimney(rng.gen_range(2..6)),
_ => Pillar::None,
},
levels: rng.gen_range(1..3),
window: match rng.gen_range(0..4) {
0 => SpriteKind::Window1,
1 => SpriteKind::Window2,
2 => SpriteKind::Window3,
_ => SpriteKind::Window4,
},
}
}
}
impl Archetype for House {
type Attr = Attr;
fn generate<R: Rng>(rng: &mut R, calendar: Option<&Calendar>) -> (Self, Skeleton<Self::Attr>) {
let len = rng.gen_range(-8..24).clamped(0, 20);
let locus = 6 + rng.gen_range(0..5);
let branches_per_side = 1 + len as usize / 20;
let levels = rng.gen_range(1..3);
let skel = Skeleton {
offset: -rng.gen_range(0..len + 7).clamped(0, len),
ori: if rng.gen() { Ori::East } else { Ori::North },
root: Branch {
len,
attr: Attr {
storey_fill: StoreyFill::All,
mansard: 0,
pillar: match rng.gen_range(0..3) {
0 => Pillar::Chimney(rng.gen_range(2..6)),
1 => Pillar::Tower(5 + rng.gen_range(1..5)),
_ => Pillar::None,
},
levels,
..Attr::generate(rng, locus)
},
locus,
border: 4,
children: [1, -1]
.iter()
.flat_map(|flip| (0..branches_per_side).map(move |i| (i, *flip)))
.filter_map(|(i, flip)| {
if rng.gen() {
Some((
i as i32 * len / (branches_per_side - 1).max(1) as i32,
Branch {
len: rng.gen_range(8..16) * flip,
attr: Attr {
levels: rng.gen_range(1..4).min(levels),
..Attr::generate(rng, locus)
},
locus: (6 + rng.gen_range(0..3)).min(locus),
border: 4,
children: Vec::new(),
},
))
} else {
None
}
})
.collect(),
},
};
let this = Self {
colors: ColorTheme {
roof: *ROOF_COLORS.choose(rng).unwrap(),
wall: *WALL_COLORS.choose(rng).unwrap(),
support: *SUPPORT_COLORS.choose(rng).unwrap(),
},
noise: RandomField::new(rng.gen()),
roof_ribbing: rng.gen(),
roof_ribbing_diagonal: rng.gen(),
christmas_decorations: calendar
.map(|c| c.is_event(CalendarEvent::Christmas))
.unwrap_or_default(),
};
(this, skel)
}
fn draw(
&self,
index: IndexRef,
_pos: Vec3<i32>,
dist: i32,
bound_offset: Vec2<i32>,
center_offset: Vec2<i32>,
z: i32,
ori: Ori,
locus: i32,
_len: i32,
attr: &Self::Attr,
) -> BlockMask {
let colors = &index.colors.site.settlement.building.archetype.house;
let roof_color = *self.colors.roof.elim_case_pure(&colors.roof);
let wall_color = *self.colors.wall.elim_case_pure(&colors.wall);
let support_color = *self.colors.support.elim_case_pure(&colors.support);
let christmas_theme = self.christmas_decorations;
let profile = Vec2::new(bound_offset.x, z);
let make_block = |(r, g, b)| {
let nz = self
.noise
.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
BlockMask::new(
Block::new(
BlockKind::Misc,
// TODO: Clarify exactly how this affects the color.
Rgb::new(r, g, b)
.map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)),
),
2,
)
};
let facade_layer = 3;
let structural_layer = facade_layer + 1;
let internal_layer = structural_layer + 1;
let foundation_layer = internal_layer + 1;
let floor_layer = foundation_layer + 1;
let foundation = make_block(colors.foundation).with_priority(foundation_layer);
let log = make_block(support_color);
let floor = make_block(colors.floor);
let wall = make_block(wall_color).with_priority(facade_layer);
let roof = make_block(roof_color).with_priority(facade_layer - 1);
const EMPTY: BlockMask = BlockMask::nothing();
// TODO: Take environment into account.
let internal = BlockMask::new(Block::air(SpriteKind::Empty), internal_layer);
let end_ori = match ori {
Ori::East => 2,
Ori::North => 4,
};
let end_window = BlockMask::new(
Block::air(attr.window).with_ori(end_ori).unwrap(),
structural_layer,
);
let fire = BlockMask::new(Block::air(SpriteKind::Ember), foundation_layer);
let storey_height = 6;
let storey = ((z - 1) / storey_height).min(attr.levels - 1);
let floor_height = storey_height * storey;
let ceil_height = storey_height * (storey + 1);
let lower_width = locus - 1;
let upper_width = locus;
let width = if profile.y >= ceil_height {
upper_width
} else {
lower_width
};
let foundation_height = 0 - (dist - width - 1).max(0);
let roof_top = storey_height * attr.levels + 2 + width;
let edge_ori = if bound_offset.x.abs() > bound_offset.y.abs() {
if center_offset.x > 0 { 6 } else { 2 }
} else if (center_offset.y > 0) ^ (ori == Ori::East) {
0
} else {
4
};
let edge_ori = if ori == Ori::East {
(edge_ori + 2) % 8
} else {
edge_ori
};
if let Pillar::Chimney(chimney_height) = attr.pillar {
let chimney_top = roof_top + chimney_height;
// Chimney shaft
if center_offset.map(|e| e.abs()).reduce_max() == 0 && profile.y > foundation_height {
return if profile.y == foundation_height + 1 {
fire
} else {
internal.with_priority(foundation_layer)
};
}
// Chimney
if center_offset.map(|e| e.abs()).reduce_max() <= 1 && profile.y < chimney_top {
// Fireplace
if center_offset.product() == 0
&& profile.y > foundation_height + 1
&& profile.y <= foundation_height + 3
{
return internal;
} else {
return foundation;
}
}
}
if profile.y <= foundation_height && dist < width + 3 {
// Foundations
if attr.storey_fill.has_lower() {
if dist == width - 1 {
// Floor lining
return log.with_priority(floor_layer);
} else if dist < width - 1 && profile.y == foundation_height {
// Floor
return floor.with_priority(floor_layer);
}
}
if dist < width && profile.y < foundation_height && profile.y >= foundation_height - 3 {
// Basement
return internal;
} else {
return foundation.with_priority(1);
}
}
// Roofs and walls
let do_roof_wall =
|profile: Vec2<i32>, width, dist, bound_offset: Vec2<i32>, roof_top, mansard| {
// Roof
let (roof_profile, roof_dist) = match &attr.roof_style {
RoofStyle::Hip => (Vec2::new(dist, profile.y), dist),
RoofStyle::Gable => (profile, dist),
RoofStyle::Rounded => {
let circular_dist = (bound_offset.map(|e| e.pow(4) as f32).sum().powf(0.25)
- 0.5)
.ceil() as i32;
(Vec2::new(circular_dist, profile.y), circular_dist)
},
};
let roof_level = roof_top - roof_profile.x.max(mansard);
if profile.y > roof_level {
return EMPTY;
}
// Roof
if profile.y == roof_level && roof_dist <= width + 2 {
let is_ribbing = ((profile.y - ceil_height) % 3 == 0 && self.roof_ribbing)
|| (bound_offset.x == bound_offset.y && self.roof_ribbing_diagonal);
if (roof_profile.x == 0 && mansard == 0) || roof_dist == width + 2 || is_ribbing
{
// Eaves
return log;
} else {
return roof;
}
}
// Wall
if dist == width && profile.y < roof_level {
// Doors
if center_offset.x > 0
&& center_offset.y > 0
&& bound_offset.x > 0
&& bound_offset.x < width
&& profile.y < ceil_height
&& attr.storey_fill.has_lower()
&& storey == 0
{
return if (bound_offset.x == (width - 1) / 2
|| bound_offset.x == (width - 1) / 2 + 1)
&& profile.y <= foundation_height + 3
{
// Doors on first floor only
if profile.y == foundation_height + 1 {
BlockMask::new(
Block::air(SpriteKind::Door)
.with_ori(
match ori {
Ori::East => 2,
Ori::North => 0,
} + if bound_offset.x == (width - 1) / 2 {
0
} else {
4
},
)
.unwrap(),
structural_layer,
)
} else {
EMPTY.with_priority(structural_layer)
}
} else {
wall
};
}
if bound_offset.x == bound_offset.y || profile.y == ceil_height {
// Support beams
return log;
} else if !attr.storey_fill.has_lower() && profile.y < ceil_height
|| !attr.storey_fill.has_upper()
{
return EMPTY;
} else {
let (frame_bounds, frame_borders) = if profile.y >= ceil_height {
(
Aabr {
min: Vec2::new(-1, ceil_height + 2),
max: Vec2::new(1, ceil_height + 5),
},
Vec2::new(1, 1),
)
} else {
(
Aabr {
min: Vec2::new(2, floor_height + 2),
max: Vec2::new(width - 2, ceil_height - 2),
},
Vec2::new(1, 0),
)
};
let window_bounds = Aabr {
min: (frame_bounds.min + frame_borders)
.map2(frame_bounds.center(), |a, b| a.min(b)),
max: (frame_bounds.max - frame_borders)
.map2(frame_bounds.center(), |a, b| a.max(b)),
};
// Window
if (frame_bounds.size() + 1).reduce_min() > 2 {
// Window frame is large enough for a window
let surface_pos = Vec2::new(bound_offset.x, profile.y);
if window_bounds.contains_point(surface_pos) {
return end_window;
} else if frame_bounds.contains_point(surface_pos) {
return log.with_priority(structural_layer);
};
}
// Wall
return if attr.central_supports && profile.x == 0 {
// Support beams
log.with_priority(structural_layer)
} else {
wall
};
}
}
if dist < width {
// Internals
if profile.y == ceil_height {
if profile.x == 0 {
// Rafters
return log;
} else if attr.storey_fill.has_upper() {
// Ceiling
return floor;
}
} else if !attr.storey_fill.has_lower()
&& center_offset.sum() % 2 == 0
&& profile.y == 1
&& center_offset.map(|e| e % 3 == 0).reduce_and()
&& self
.noise
.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.8)
{
let furniture = match self.noise.get(Vec3::new(
center_offset.x,
center_offset.y,
z + 100,
)) % 13
{
0..=1 => SpriteKind::Crate,
2 => SpriteKind::Bench,
3 => SpriteKind::Anvil,
4 => SpriteKind::CookingPot,
5 => SpriteKind::CraftingBench,
6 => SpriteKind::FireBowlGround,
7 => SpriteKind::Cauldron,
8 => SpriteKind::Forge,
9 => SpriteKind::Loom,
10 => SpriteKind::SpinningWheel,
11 => SpriteKind::TanningRack,
12 => SpriteKind::DismantlingBench,
_ => unreachable!(),
};
return BlockMask::new(Block::air(furniture).with_ori(end_ori).unwrap(), 1);
} else if (!attr.storey_fill.has_lower() && profile.y < ceil_height)
|| (!attr.storey_fill.has_upper() && profile.y >= ceil_height)
{
return EMPTY;
// Furniture
} else if dist == width - 1
&& center_offset.sum() % 2 == 0
&& profile.y == floor_height + 1
&& self
.noise
.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.2)
{
// NOTE: Used only for dynamic elements like chests and entities!
let mut dynamic_rng = rand::thread_rng();
let furniture = match self.noise.get(Vec3::new(
center_offset.x,
center_offset.y,
z + 100,
)) % 12
{
0 => SpriteKind::Planter,
1 => SpriteKind::ChairSingle,
2 => SpriteKind::ChairDouble,
3 => SpriteKind::CoatRack,
4 => {
if dynamic_rng.gen_range(0..8) == 0 {
SpriteKind::Chest
} else {
SpriteKind::Crate
}
},
6 => SpriteKind::DrawerMedium,
7 => SpriteKind::DrawerSmall,
8 => SpriteKind::TableSide,
9 => SpriteKind::WardrobeSingle,
10 => {
if dynamic_rng.gen_range(0..10) == 0 {
SpriteKind::PotionMinor
} else {
SpriteKind::VialEmpty
}
},
_ => {
if dynamic_rng.gen_range(0..2) == 0 {
SpriteKind::Bowl
} else {
SpriteKind::Pot
}
},
};
return BlockMask::new(
Block::air(furniture).with_ori(edge_ori).unwrap(),
internal_layer,
);
} else {
return internal;
}
}
// Wall ornaments
if dist == width + 1
&& center_offset.map(|e| e.abs()).reduce_min() == 0
&& profile.y == floor_height + 3
&& self.noise.chance(
Vec3::new(center_offset.x, center_offset.y, z),
if christmas_theme { 0.70 } else { 0.35 },
)
&& attr.storey_fill.has_lower()
{
let ornament = if christmas_theme {
match self
.noise
.get(Vec3::new(center_offset.x, center_offset.y, z + 100))
% 4
{
0 => SpriteKind::ChristmasWreath,
_ => SpriteKind::ChristmasOrnament,
}
} else {
match self
.noise
.get(Vec3::new(center_offset.x, center_offset.y, z + 100))
% 6
{
0 => SpriteKind::HangingSign,
1 | 2 | 3 => SpriteKind::HangingBasket,
4 => SpriteKind::WallSconce,
5 => SpriteKind::WallLampSmall,
_ => SpriteKind::DungeonWallDecor,
}
};
BlockMask::new(
Block::air(ornament).with_ori((edge_ori + 4) % 8).unwrap(),
internal_layer,
)
} else {
EMPTY
}
};
let mut cblock = do_roof_wall(profile, width, dist, bound_offset, roof_top, attr.mansard);
if let Pillar::Tower(tower_height) = attr.pillar {
let tower_top = roof_top + tower_height;
let profile = Vec2::new(center_offset.x.abs(), profile.y);
let dist = center_offset.map(|e| e.abs()).reduce_max();
cblock = cblock.resolve_with(do_roof_wall(
profile,
4,
dist,
center_offset.map(|e| e.abs()),
tower_top,
attr.mansard,
));
}
cblock
}
}

View File

@ -1,287 +0,0 @@
use super::{super::skeleton::*, Archetype};
use crate::{
site::BlockMask,
util::{RandomField, Sampler},
IndexRef,
};
use common::{
calendar::Calendar,
make_case_elim,
terrain::{Block, BlockKind, SpriteKind},
};
use rand::prelude::*;
use serde::Deserialize;
use vek::*;
#[derive(Deserialize)]
pub struct Colors {
pub brick_base: (u8, u8, u8),
pub floor_base: (u8, u8, u8),
pub pole: (u8, u8, u8),
pub flag: flag_color::PureCases<(u8, u8, u8)>,
pub stone: stone_color::PureCases<(u8, u8, u8)>,
}
pub struct Keep {
pub flag_color: FlagColor,
pub stone_color: StoneColor,
}
pub struct Attr {
pub storeys: i32,
pub is_tower: bool,
pub flag: bool,
pub ridged: bool,
pub rounded: bool,
pub has_doors: bool,
}
make_case_elim!(
flag_color,
#[repr(u32)]
pub enum FlagColor {
Good = 0,
Evil = 1,
}
);
make_case_elim!(
stone_color,
#[repr(u32)]
pub enum StoneColor {
Good = 0,
Evil = 1,
}
);
impl Archetype for Keep {
type Attr = Attr;
fn generate<R: Rng>(rng: &mut R, _calendar: Option<&Calendar>) -> (Self, Skeleton<Self::Attr>) {
let len = rng.gen_range(-8..24).max(0);
let storeys = rng.gen_range(1..3);
let skel = Skeleton {
offset: -rng.gen_range(0..len + 7).clamped(0, len),
ori: if rng.gen() { Ori::East } else { Ori::North },
root: Branch {
len,
attr: Attr {
storeys,
is_tower: false,
flag: false,
ridged: false,
rounded: true,
has_doors: true,
},
locus: 10 + rng.gen_range(0..5),
border: 3,
children: (0..1)
.map(|_| {
(
rng.gen_range(-5..len + 5).clamped(0, len.max(1) - 1),
Branch {
len: 0,
attr: Attr {
storeys: storeys + rng.gen_range(1..3),
is_tower: true,
flag: true,
ridged: false,
rounded: true,
has_doors: false,
},
locus: 6 + rng.gen_range(0..3),
border: 3,
children: Vec::new(),
},
)
})
.collect(),
},
};
(
Self {
flag_color: FlagColor::Good,
stone_color: StoneColor::Good,
},
skel,
)
}
fn draw(
&self,
index: IndexRef,
pos: Vec3<i32>,
_dist: i32,
bound_offset: Vec2<i32>,
center_offset: Vec2<i32>,
z: i32,
ori: Ori,
locus: i32,
_len: i32,
attr: &Self::Attr,
) -> BlockMask {
let dungeon_stone = index.colors.site.dungeon.stone;
let colors = &index.colors.site.settlement.building.archetype.keep;
let flag_color = self.flag_color.elim_case_pure(&colors.flag);
let stone_color = self.stone_color.elim_case_pure(&colors.stone);
let profile = Vec2::new(bound_offset.x, z);
let weak_layer = 1;
let normal_layer = weak_layer + 1;
let important_layer = normal_layer + 1;
let internal_layer = important_layer + 1;
let make_block =
|r, g, b| BlockMask::new(Block::new(BlockKind::Rock, Rgb::new(r, g, b)), normal_layer);
let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24;
let foundation = make_block(
colors.brick_base.0 + brick_tex,
colors.brick_base.1 + brick_tex,
colors.brick_base.2 + brick_tex,
);
let wall = make_block(
stone_color.0 + brick_tex,
stone_color.1 + brick_tex,
stone_color.2 + brick_tex,
);
let window = BlockMask::new(
Block::air(SpriteKind::Window1)
.with_ori(match ori {
Ori::East => 2,
Ori::North => 0,
})
.unwrap(),
normal_layer,
);
let floor = make_block(
colors.floor_base.0 + (pos.y.abs() % 2) as u8 * 15,
colors.floor_base.1 + (pos.y.abs() % 2) as u8 * 15,
colors.floor_base.2 + (pos.y.abs() % 2) as u8 * 15,
)
.with_priority(important_layer);
let pole =
make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer);
let flag =
make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer);
const AIR: Block = Block::air(SpriteKind::Empty);
const EMPTY: BlockMask = BlockMask::nothing();
let internal = BlockMask::new(AIR, internal_layer);
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
let stone = BlockMask::new(Block::new(BlockKind::Rock, dungeon_stone.into()), 5);
if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) {
stone
} else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) {
if ((pos.x as f32).atan2(pos.y as f32) / (std::f32::consts::PI * 2.0) * stretch
+ pos.z as f32)
.rem_euclid(stretch)
< 1.5
{
stone
} else {
internal
}
} else {
EMPTY
}
};
let ridge_x = (center_offset.map(|e| e.abs()).reduce_min() + 2) % 8;
let width = locus
+ if ridge_x < 4 && attr.ridged && !attr.rounded {
1
} else {
0
};
let rampart_width = 2 + width;
let storey_height = 9;
let roof_height = attr.storeys * storey_height;
let storey_y = profile.y % storey_height;
let door_height = 6;
let rampart_height = roof_height + if ridge_x % 2 == 0 { 3 } else { 4 };
let min_dist = if attr.rounded {
bound_offset.map(|e| e.pow(2) as f32).sum().sqrt() as i32
} else {
bound_offset.map(|e| e.abs()).reduce_max()
};
if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 {
// Foundations
foundation
} else if (0..=roof_height).contains(&profile.y) && storey_y == 0 && min_dist <= width + 1 {
if min_dist < width { floor } else { wall }
} else if bound_offset.x.abs() < 3
&& profile.y < door_height - bound_offset.x.abs()
&& profile.y > 0
&& min_dist >= width - 2
&& min_dist <= width + 1
&& attr.has_doors
{
internal
} else if (min_dist == width || (!attr.is_tower && min_dist == width + 1))
&& profile.y <= roof_height
{
if attr.is_tower
&& (3..7).contains(&storey_y)
&& bound_offset.x.abs() < width - 2
&& (5..7).contains(&ridge_x)
{
window
} else {
wall
}
} else if profile.y >= roof_height {
if profile.y > roof_height
&& (min_dist < rampart_width - 1 || (attr.is_tower && min_dist < rampart_width))
{
if attr.is_tower
&& attr.flag
&& center_offset == Vec2::zero()
&& profile.y < roof_height + 16
{
pole
} else if attr.is_tower
&& attr.flag
&& center_offset.x == 0
&& center_offset.y > 0
&& center_offset.y < 8
&& profile.y > roof_height + 8
&& profile.y < roof_height + 14
{
flag
} else {
EMPTY
}
} else if min_dist <= rampart_width {
if profile.y < rampart_height {
wall
} else {
EMPTY
}
} else {
EMPTY
}
} else if profile.y < roof_height && min_dist < width {
internal
} else {
EMPTY
}
.resolve_with(
if attr.is_tower && profile.y > 0 && profile.y <= roof_height {
make_staircase(
Vec3::new(center_offset.x, center_offset.y, pos.z),
7.0f32.min(width as f32 - 1.0),
0.5,
9.0,
)
} else {
EMPTY
},
)
}
}

View File

@ -1,37 +0,0 @@
pub mod house;
pub mod keep;
use super::skeleton::*;
use crate::{site::BlockMask, IndexRef};
use common::calendar::Calendar;
use rand::prelude::*;
use serde::Deserialize;
use vek::*;
#[derive(Deserialize)]
pub struct Colors {
pub house: house::Colors,
pub keep: keep::Colors,
}
pub trait Archetype {
type Attr;
fn generate<R: Rng>(rng: &mut R, calendar: Option<&Calendar>) -> (Self, Skeleton<Self::Attr>)
where
Self: Sized;
fn draw(
&self,
index: IndexRef,
pos: Vec3<i32>,
dist: i32,
bound_offset: Vec2<i32>,
center_offset: Vec2<i32>,
z: i32,
ori: Ori,
locus: i32,
len: i32,
attr: &Self::Attr,
) -> BlockMask;
}

View File

@ -1,78 +0,0 @@
pub mod archetype;
pub mod skeleton;
// Reexports
pub use self::{
archetype::{house::House, keep::Keep, Archetype},
skeleton::*,
};
use crate::IndexRef;
use common::{calendar::Calendar, terrain::Block};
use rand::prelude::*;
use serde::Deserialize;
use vek::*;
#[derive(Deserialize)]
pub struct Colors {
pub archetype: archetype::Colors,
}
pub struct Building<A: Archetype> {
skel: Skeleton<A::Attr>,
archetype: A,
origin: Vec3<i32>,
}
impl<A: Archetype> Building<A> {
pub fn generate(rng: &mut impl Rng, origin: Vec3<i32>, calendar: Option<&Calendar>) -> Self
where
A: Sized,
{
let (archetype, skel) = A::generate(rng, calendar);
Self {
skel,
archetype,
origin,
}
}
pub fn bounds_2d(&self) -> Aabr<i32> {
let b = self.skel.bounds();
Aabr {
min: Vec2::from(self.origin) + b.min,
max: Vec2::from(self.origin) + b.max,
}
}
pub fn bounds(&self) -> Aabb<i32> {
let aabr = self.bounds_2d();
Aabb {
min: Vec3::from(aabr.min) + Vec3::unit_z() * (self.origin.z - 8),
max: Vec3::from(aabr.max) + Vec3::unit_z() * (self.origin.z + 48),
}
}
pub fn sample(&self, index: IndexRef, pos: Vec3<i32>) -> Option<Block> {
let rpos = pos - self.origin;
self.skel
.sample_closest(
rpos,
|pos, dist, bound_offset, center_offset, ori, branch| {
self.archetype.draw(
index,
pos,
dist,
bound_offset,
center_offset,
rpos.z,
ori,
branch.locus,
branch.len,
&branch.attr,
)
},
)
.finish()
}
}

View File

@ -1,119 +0,0 @@
use crate::site::BlockMask;
use vek::*;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Ori {
East,
North,
}
impl Ori {
#[must_use]
pub fn flip(self) -> Self {
match self {
Ori::East => Ori::North,
Ori::North => Ori::East,
}
}
pub fn dir(self) -> Vec2<i32> {
match self {
Ori::East => Vec2::unit_x(),
Ori::North => Vec2::unit_y(),
}
}
}
pub struct Branch<T> {
pub len: i32,
pub attr: T,
pub locus: i32,
pub border: i32,
pub children: Vec<(i32, Branch<T>)>,
}
impl<T> Branch<T> {
fn for_each<'a>(
&'a self,
node: Vec2<i32>,
ori: Ori,
is_child: bool,
parent_locus: i32,
f: &mut impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32),
) {
f(node, ori, self, is_child, parent_locus);
for (offset, child) in &self.children {
child.for_each(node + ori.dir() * *offset, ori.flip(), true, self.locus, f);
}
}
}
pub struct Skeleton<T> {
pub offset: i32,
pub ori: Ori,
pub root: Branch<T>,
}
impl<T> Skeleton<T> {
pub fn for_each<'a>(&'a self, mut f: impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32)) {
self.root
.for_each(self.ori.dir() * self.offset, self.ori, false, 0, &mut f);
}
pub fn bounds(&self) -> Aabr<i32> {
let mut bounds = Aabr::new_empty(self.ori.dir() * self.offset);
self.for_each(|node, ori, branch, _, _| {
let node2 = node + ori.dir() * branch.len;
let a = node.map2(node2, |a, b| a.min(b)) - (branch.locus + branch.border);
let b = node.map2(node2, |a, b| a.max(b)) + (branch.locus + branch.border);
bounds.expand_to_contain_point(a);
bounds.expand_to_contain_point(b);
});
bounds
}
pub fn sample_closest(
&self,
pos: Vec3<i32>,
mut f: impl FnMut(Vec3<i32>, i32, Vec2<i32>, Vec2<i32>, Ori, &Branch<T>) -> BlockMask,
) -> BlockMask {
let mut min = None::<(_, BlockMask)>;
self.for_each(|node, ori, branch, is_child, parent_locus| {
let node2 = node + ori.dir() * branch.len;
let node = node
+ if is_child {
ori.dir()
* branch.len.signum()
* (branch.locus - parent_locus).clamped(0, branch.len.abs())
} else {
Vec2::zero()
};
let bounds = Aabr::new_empty(node).expanded_to_contain_point(node2);
let bound_offset = if ori == Ori::East {
Vec2::new(
node.y - pos.y,
pos.x - pos.x.clamped(bounds.min.x, bounds.max.x),
)
} else {
Vec2::new(
node.x - pos.x,
pos.y - pos.y.clamped(bounds.min.y, bounds.max.y),
)
}
.map(|e| e.abs());
let center_offset = if ori == Ori::East {
Vec2::new(pos.y - bounds.center().y, pos.x - bounds.center().x)
} else {
Vec2::new(pos.x - bounds.center().x, pos.y - bounds.center().y)
};
let dist = bound_offset.reduce_max();
let dist_locus = dist - branch.locus;
let new_bm = f(pos, dist, bound_offset, center_offset, ori, branch);
min = min
.map(|(_, bm)| (dist_locus, bm.resolve_with(new_bm)))
.or(Some((dist_locus, new_bm)));
});
min.map(|(_, bm)| bm).unwrap_or_else(BlockMask::nothing)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,82 +0,0 @@
use super::{GenCtx, AREA_SIZE};
use common::store::Store;
use rand::prelude::*;
use vek::*;
pub struct Town {
pub base_tile: Vec2<i32>,
radius: i32,
districts: Store<District>,
}
impl Town {
pub fn districts(&self) -> &Store<District> { &self.districts }
pub fn generate(origin: Vec2<i32>, base_tile: Vec2<i32>, ctx: &mut GenCtx<impl Rng>) -> Self {
let mut this = Self {
base_tile,
radius: 4,
districts: Store::default(),
};
this.generate_districts(origin, ctx);
this
}
fn generate_districts(&mut self, origin: Vec2<i32>, ctx: &mut GenCtx<impl Rng>) {
let base_aabr = Aabr {
min: self.base_tile - self.radius,
max: self.base_tile + self.radius,
};
gen_plot(base_aabr, ctx).for_each(base_aabr, &mut |aabr| {
if aabr.center().distance_squared(self.base_tile) < self.radius.pow(2) {
self.districts.insert(District {
seed: ctx.rng.gen(),
aabr,
alt: ctx
.sim
.and_then(|sim| {
sim.get_alt_approx(
origin + aabr.center() * AREA_SIZE as i32 + AREA_SIZE as i32 / 2,
)
})
.unwrap_or(0.0) as i32,
});
}
});
}
}
pub struct District {
pub seed: u32,
pub aabr: Aabr<i32>,
pub alt: i32,
}
enum Plot {
District,
Parent(Vec<(Aabr<i32>, Plot)>),
}
impl Plot {
fn for_each(&self, aabr: Aabr<i32>, f: &mut impl FnMut(Aabr<i32>)) {
match self {
Plot::District => f(aabr),
Plot::Parent(children) => children.iter().for_each(|(aabr, p)| p.for_each(*aabr, f)),
}
}
}
fn gen_plot(aabr: Aabr<i32>, ctx: &mut GenCtx<impl Rng>) -> Plot {
if aabr.size().product() <= 9 {
Plot::District
} else if aabr.size().w < aabr.size().h {
let [a, b] = aabr.split_at_y(aabr.min.y + ctx.rng.gen_range(1..aabr.size().h));
Plot::Parent(vec![(a, gen_plot(a, ctx)), (b, gen_plot(b, ctx))])
} else {
let [a, b] = aabr.split_at_x(aabr.min.x + ctx.rng.gen_range(1..aabr.size().w));
Plot::Parent(vec![(a, gen_plot(a, ctx)), (b, gen_plot(b, ctx))])
}
}

View File

@ -21,7 +21,7 @@ pub use self::{
unit_chooser::UnitChooser,
};
pub use common::grid::Grid;
pub use common::grid::{Grid, RowGen};
use fxhash::FxHasher32;
use hashbrown::{HashMap, HashSet};

View File

@ -5,9 +5,9 @@ pub trait Sampler<'a, 'b>: Sized {
fn get(&'b self, index: Self::Index) -> Self::Sample;
}
pub trait SamplerMut<'a>: Sized {
pub trait SamplerMut<'a, 'b>: Sized {
type Index: 'a;
type Sample: 'a;
fn get(&mut self, index: Self::Index) -> Self::Sample;
fn get(&'b mut self, index: Self::Index) -> Self::Sample;
}