mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Rivers (hack, not great).
This commit is contained in:
parent
0b91f5ba90
commit
bc4600cf14
@ -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: (),
|
||||
)
|
||||
|
@ -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 {
|
||||
|
@ -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)?;
|
||||
|
@ -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| {
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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/* ,
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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*/ */
|
||||
);
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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| {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
@ -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(_) => {}, */
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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
@ -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))])
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user