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),
|
lava: (184, 39, 0),
|
||||||
vein: (61, 229, 198),
|
vein: (61, 229, 198),
|
||||||
),
|
),
|
||||||
site: (
|
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.
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,14 @@ pub struct Grid<T> {
|
|||||||
size: Vec2<i32>, // TODO: use u32
|
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> {
|
impl<T> Grid<T> {
|
||||||
pub fn from_raw(size: Vec2<i32>, raw: impl Into<Vec<T>>) -> Self {
|
pub fn from_raw(size: Vec2<i32>, raw: impl Into<Vec<T>>) -> Self {
|
||||||
let cells = raw.into();
|
let cells = raw.into();
|
||||||
@ -26,18 +34,24 @@ impl<T> Grid<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn populate_by_row<Row: FnMut(i32) -> Col, Col: FnMut(i32) -> T, const X: u32, const Y: u32>(
|
pub fn populate_by_row<'c, Gen/*, Row*//*, Col*/, const X: u32, const Y: u32>(
|
||||||
mut row: Row,
|
gen: &mut Gen,
|
||||||
|
/* mut row_gen: Row, */
|
||||||
|
/* mut col_gen: Col, */
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
T: Clone + Default,
|
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}]:
|
[(); {X as usize}]:
|
||||||
{
|
{
|
||||||
let mut cells = vec![T::default(); {X as usize * Y 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)| {
|
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)| {
|
cells.iter_mut().enumerate().for_each(|(x, cell)| {
|
||||||
*cell = col(x as i32);
|
*cell = Gen::col_gen(&mut row, x as i32);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
|
@ -62,7 +62,7 @@ use specs::{
|
|||||||
use std::{str::FromStr, sync::Arc};
|
use std::{str::FromStr, sync::Arc};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use wiring::{Circuit, Wire, WireNode, WiringAction, WiringActionEffect, WiringElement};
|
use wiring::{Circuit, Wire, WireNode, WiringAction, WiringActionEffect, WiringElement};
|
||||||
use world::util::Sampler;
|
use world::util::SamplerMut;
|
||||||
|
|
||||||
use common::comp::Alignment;
|
use common::comp::Alignment;
|
||||||
use tracing::{error, info, warn};
|
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 chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
||||||
let msg_generator = |/*calendar*/| {
|
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 alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?;
|
||||||
let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?;
|
let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?;
|
||||||
let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?;
|
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
|
/// Escape hatch for runtime creation of loadout not covered by entity
|
||||||
/// config.
|
/// config.
|
||||||
// NOTE: Signature is part of interface of EntityInfo
|
// NOTE: Signature is part of interface of EntityInfo
|
||||||
@ -159,7 +267,7 @@ impl Entity {
|
|||||||
let kind = self.kind;
|
let kind = self.kind;
|
||||||
|
|
||||||
if let RtSimEntityKind::Merchant = 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 {
|
} else {
|
||||||
|l, _| l
|
|l, _| l
|
||||||
}
|
}
|
||||||
@ -174,7 +282,7 @@ impl Entity {
|
|||||||
.civs()
|
.civs()
|
||||||
.sites
|
.sites
|
||||||
.iter()
|
.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)| {
|
.min_by_key(|(_, site)| {
|
||||||
let wpos = site.center.map2(TerrainChunk::RECT_SIZE, |e, sz| {
|
let wpos = site.center.map2(TerrainChunk::RECT_SIZE, |e, sz| {
|
||||||
e * sz as i32 + sz as i32 / 2
|
e * sz as i32 + sz as i32 / 2
|
||||||
@ -332,7 +440,7 @@ impl Entity {
|
|||||||
.civs()
|
.civs()
|
||||||
.sites
|
.sites
|
||||||
.iter()
|
.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)
|
.filter(|_| thread_rng().gen_range(0i32..4) == 0)
|
||||||
.min_by_key(|(_, site)| {
|
.min_by_key(|(_, site)| {
|
||||||
let wpos = site.center.map2(TerrainChunk::RECT_SIZE, |e, sz| {
|
let wpos = site.center.map2(TerrainChunk::RECT_SIZE, |e, sz| {
|
||||||
|
@ -547,42 +547,44 @@ impl<'a> Widget for Map<'a> {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.show_towns_text, ui);
|
.set(state.ids.show_towns_text, ui);
|
||||||
// Castles
|
// Castles
|
||||||
Image::new(self.imgs.mmap_site_castle)
|
if false {
|
||||||
.down_from(state.ids.show_towns_img, 10.0)
|
Image::new(self.imgs.mmap_site_castle)
|
||||||
.w_h(20.0, 20.0)
|
.down_from(state.ids.show_towns_img, 10.0)
|
||||||
.set(state.ids.show_castles_img, ui);
|
.w_h(20.0, 20.0)
|
||||||
if Button::image(if show_castles {
|
.set(state.ids.show_castles_img, ui);
|
||||||
self.imgs.checkbox_checked
|
if Button::image(if show_castles {
|
||||||
} else {
|
self.imgs.checkbox_checked
|
||||||
self.imgs.checkbox
|
} else {
|
||||||
})
|
self.imgs.checkbox
|
||||||
.w_h(18.0, 18.0)
|
})
|
||||||
.hover_image(if show_castles {
|
.w_h(18.0, 18.0)
|
||||||
self.imgs.checkbox_checked_mo
|
.hover_image(if show_castles {
|
||||||
} else {
|
self.imgs.checkbox_checked_mo
|
||||||
self.imgs.checkbox_mo
|
} else {
|
||||||
})
|
self.imgs.checkbox_mo
|
||||||
.press_image(if show_castles {
|
})
|
||||||
self.imgs.checkbox_checked
|
.press_image(if show_castles {
|
||||||
} else {
|
self.imgs.checkbox_checked
|
||||||
self.imgs.checkbox_press
|
} else {
|
||||||
})
|
self.imgs.checkbox_press
|
||||||
.right_from(state.ids.show_castles_img, 10.0)
|
})
|
||||||
.set(state.ids.show_castles_box, ui)
|
.right_from(state.ids.show_castles_img, 10.0)
|
||||||
.was_clicked()
|
.set(state.ids.show_castles_box, ui)
|
||||||
{
|
.was_clicked()
|
||||||
events.push(Event::SettingsChange(MapShowCastles(!show_castles)));
|
{
|
||||||
|
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
|
// Dungeons
|
||||||
Image::new(self.imgs.mmap_site_dungeon)
|
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)
|
.w_h(20.0, 20.0)
|
||||||
.set(state.ids.show_dungeons_img, ui);
|
.set(state.ids.show_dungeons_img, ui);
|
||||||
if Button::image(if show_dungeons {
|
if Button::image(if show_dungeons {
|
||||||
|
@ -117,7 +117,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
} else {
|
} else {
|
||||||
Some(Block::new(BlockKind::Earth, col))
|
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))
|
let grass_factor = (wposf.z as f32 - (alt - grass_depth))
|
||||||
.div(grass_depth)
|
.div(grass_depth)
|
||||||
.sqrt();
|
.sqrt();
|
||||||
@ -134,6 +134,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
}/* else if snow_cover {
|
}/* else if snow_cover {
|
||||||
//if temp < CONFIG.snow_temp + 0.031 {
|
//if temp < CONFIG.snow_temp + 0.031 {
|
||||||
Block::new(BlockKind::Snow, col.map(|e| (e * 255.0) as u8))
|
Block::new(BlockKind::Snow, col.map(|e| (e * 255.0) as u8))
|
||||||
|
//}
|
||||||
}*/ else {
|
}*/ else {
|
||||||
Block::new(BlockKind::Grass, col.map(|e| (e * 255.0) as u8))
|
Block::new(BlockKind::Grass, col.map(|e| (e * 255.0) as u8))
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
land::Land,
|
land::Land,
|
||||||
layer::spot::Spot,
|
layer::spot::Spot,
|
||||||
sim::{SimChunk, WorldSim},
|
sim::{SimChunk, WorldSim},
|
||||||
util::{Grid, Sampler},
|
util::{Grid, SamplerMut},
|
||||||
TerrainGrid,
|
TerrainGrid,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
@ -71,7 +71,7 @@ impl<'a> CanvasInfo<'a> {
|
|||||||
pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
|
pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
|
||||||
self.col_inner(wpos).map(Cow::Borrowed).or_else(|| {
|
self.col_inner(wpos).map(Cow::Borrowed).or_else(|| {
|
||||||
let chunk_pos = TerrainGrid::chunk_key(wpos);
|
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((
|
Some(Cow::Owned(column_gen.get((
|
||||||
wpos/* ,
|
wpos/* ,
|
||||||
|
@ -5,7 +5,7 @@ mod econ;
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::CONFIG,
|
config::CONFIG,
|
||||||
sim::WorldSim,
|
sim::WorldSim,
|
||||||
site::{namegen::NameGen, Castle, /*Settlement, */Site as WorldSite/*, Tree */},
|
site::{namegen::NameGen, /*Castle, *//*Settlement, */Site as WorldSite/*, Tree */},
|
||||||
site2,
|
site2,
|
||||||
util::{attempt, seed_expan, DHashMap, NEIGHBORS},
|
util::{attempt, seed_expan, DHashMap, NEIGHBORS},
|
||||||
Index, Land,
|
Index, Land,
|
||||||
@ -117,7 +117,8 @@ impl Civs {
|
|||||||
for _ in 0..initial_civ_count * 3 {
|
for _ in 0..initial_civ_count * 3 {
|
||||||
attempt(5, || {
|
attempt(5, || {
|
||||||
let (kind, avoid) = match ctx.rng.gen_range(0..64) {
|
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 => {
|
28..=31 => {
|
||||||
/*if index.features().site2_giant_trees */{
|
/*if index.features().site2_giant_trees */{
|
||||||
(SiteKind::GiantTree, (&tree_enemies, 40))
|
(SiteKind::GiantTree, (&tree_enemies, 40))
|
||||||
@ -126,17 +127,16 @@ impl Civs {
|
|||||||
}*/
|
}*/
|
||||||
},
|
},
|
||||||
32..=37 => (SiteKind::Gnarling, (&gnarling_enemies, 40)),
|
32..=37 => (SiteKind::Gnarling, (&gnarling_enemies, 40)),
|
||||||
// 32..=37 => (SiteKind::Citadel, 5, (&castle_enemies, 20)),
|
|
||||||
_ => (SiteKind::Dungeon, (&dungeon_enemies, 40)),
|
_ => (SiteKind::Dungeon, (&dungeon_enemies, 40)),
|
||||||
};
|
};
|
||||||
let loc = find_site_loc(&mut ctx, avoid, kind)?;
|
let loc = find_site_loc(&mut ctx, avoid, kind)?;
|
||||||
match kind {
|
match kind {
|
||||||
SiteKind::Castle => {
|
/* SiteKind::Castle => {
|
||||||
gnarling_enemies.push(loc);
|
gnarling_enemies.push(loc);
|
||||||
dungeon_enemies.push(loc);
|
dungeon_enemies.push(loc);
|
||||||
tree_enemies.push(loc);
|
tree_enemies.push(loc);
|
||||||
castle_enemies.push(loc);
|
castle_enemies.push(loc);
|
||||||
},
|
}, */
|
||||||
SiteKind::Gnarling => {
|
SiteKind::Gnarling => {
|
||||||
castle_enemies.push(loc);
|
castle_enemies.push(loc);
|
||||||
dungeon_enemies.push(loc);
|
dungeon_enemies.push(loc);
|
||||||
@ -168,7 +168,7 @@ impl Civs {
|
|||||||
let (radius, flatten_radius) = match &site.kind {
|
let (radius, flatten_radius) = match &site.kind {
|
||||||
/* SiteKind::Settlement => (32i32, 10.0f32), */
|
/* SiteKind::Settlement => (32i32, 10.0f32), */
|
||||||
SiteKind::Dungeon => (8i32, 3.0f32),
|
SiteKind::Dungeon => (8i32, 3.0f32),
|
||||||
SiteKind::Castle => (16i32, 5.0),
|
/* SiteKind::Castle => (16i32, 5.0), */
|
||||||
SiteKind::Refactor => (32i32, 10.0),
|
SiteKind::Refactor => (32i32, 10.0),
|
||||||
SiteKind::CliffTown => (32i32, 10.0),
|
SiteKind::CliffTown => (32i32, 10.0),
|
||||||
/* SiteKind::Tree => (12i32, 8.0), */
|
/* SiteKind::Tree => (12i32, 8.0), */
|
||||||
@ -177,9 +177,9 @@ impl Civs {
|
|||||||
SiteKind::Citadel => (16i32, 0.0),
|
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::Settlement => (10.0, 6, true), */
|
||||||
SiteKind::Castle => (0.0, 6, true),
|
/* SiteKind::Castle => (0.0, 6, true), */
|
||||||
_ => (0.0, 0, false),
|
_ => (0.0, 0, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ impl Civs {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place sites in world
|
// Place sites in world
|
||||||
@ -243,9 +243,9 @@ impl Civs {
|
|||||||
&mut rng,
|
&mut rng,
|
||||||
wpos,
|
wpos,
|
||||||
)),
|
)),
|
||||||
SiteKind::Castle => {
|
/* SiteKind::Castle => {
|
||||||
WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng))
|
WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng))
|
||||||
},
|
}, */
|
||||||
SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city(
|
SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city(
|
||||||
&Land::from_sim(ctx.sim),
|
&Land::from_sim(ctx.sim),
|
||||||
&mut rng,
|
&mut rng,
|
||||||
@ -976,7 +976,7 @@ impl Civs {
|
|||||||
SiteKind::Refactor
|
SiteKind::Refactor
|
||||||
/* | SiteKind::Settlement */
|
/* | SiteKind::Settlement */
|
||||||
| SiteKind::CliffTown
|
| SiteKind::CliffTown
|
||||||
| SiteKind::Castle
|
/* | SiteKind::Castle */
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt()))
|
.map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt()))
|
||||||
@ -984,7 +984,7 @@ impl Civs {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
nearby.sort_by_key(|(_, dist)| *dist as i32);
|
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
|
self.sites[site].kind
|
||||||
{
|
{
|
||||||
for (nearby, _) in nearby.into_iter().take(5) {
|
for (nearby, _) in nearby.into_iter().take(5) {
|
||||||
@ -1234,7 +1234,7 @@ impl fmt::Display for Site {
|
|||||||
pub enum SiteKind {
|
pub enum SiteKind {
|
||||||
/* Settlement, */
|
/* Settlement, */
|
||||||
Dungeon,
|
Dungeon,
|
||||||
Castle,
|
/* Castle, */
|
||||||
Refactor,
|
Refactor,
|
||||||
CliffTown,
|
CliffTown,
|
||||||
/* Tree, */
|
/* Tree, */
|
||||||
@ -1354,7 +1354,7 @@ impl SiteKind {
|
|||||||
&& chunk.near_cliffs()
|
&& chunk.near_cliffs()
|
||||||
&& suitable_for_town(4.0)
|
&& suitable_for_town(4.0)
|
||||||
},
|
},
|
||||||
SiteKind::Castle => {
|
/* SiteKind::Castle => {
|
||||||
if chunk.tree_density > 0.4 || chunk.river.near_water() || chunk.near_cliffs() {
|
if chunk.tree_density > 0.4 || chunk.river.near_water() || chunk.near_cliffs() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1380,7 +1380,7 @@ impl SiteKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
},
|
}, */
|
||||||
SiteKind::Refactor/* | SiteKind::Settlement*/ => suitable_for_town(6.7),
|
SiteKind::Refactor/* | SiteKind::Settlement*/ => suitable_for_town(6.7),
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
@ -1405,7 +1405,7 @@ impl Site {
|
|||||||
matches!(self.kind, /*SiteKind::Settlement | */SiteKind::Refactor)
|
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)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
all::ForestKind,
|
all::ForestKind,
|
||||||
sim::{local_cells, Cave, Path, RiverData, RiverKind, SimChunk, WorldSim},
|
sim::{local_cells, Cave, Path, RiverData, RiverKind, SimChunk, WorldSim},
|
||||||
site::SpawnRules,
|
site::SpawnRules,
|
||||||
util::{RandomField, Sampler},
|
util::{RandomField, RowGen, Sampler, SamplerMut},
|
||||||
IndexRef, CONFIG,
|
IndexRef, CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
@ -25,7 +25,7 @@ use tracing::error;
|
|||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub struct ColumnGen1D<'a, 'b> {
|
pub struct ColumnGen1D<'a, 'b> {
|
||||||
pub(crate) parent: &'b ColumnGen<'a>,
|
pub(crate) parent: &'b mut ColumnGen<'a>,
|
||||||
wpos_row: i32,
|
wpos_row: i32,
|
||||||
wposy: f32,
|
wposy: f32,
|
||||||
cubic_y: Cubic<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) neighbor_river_data: Vec<(Vec2<i32>, &'a SimChunk, &'a RiverData)>,
|
||||||
pub(crate) homogeneous_water_level: Option<Option<f32>>,
|
pub(crate) homogeneous_water_level: Option<Option<f32>>,
|
||||||
// pub(crate) spawn_rules: SpawnRules,
|
// 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)]
|
#[derive(Deserialize)]
|
||||||
pub struct Colors {
|
pub struct Colors {
|
||||||
pub cold_grass: (f32, f32, f32),
|
pub cold_grass: (f32, f32, f32),
|
||||||
@ -534,20 +545,43 @@ impl<'a> ColumnGen<'a> {
|
|||||||
.fold(SpawnRules::default(), |a, b| a.combine(b)); */
|
.fold(SpawnRules::default(), |a, b| a.combine(b)); */
|
||||||
|
|
||||||
let my_chunk_idx = vec2_as_uniform_idx(sim.map_size_lg(), chunk_pos);
|
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 =
|
let neighbor_river_data =
|
||||||
local_cells(sim.map_size_lg(), my_chunk_idx).filter_map(|neighbor_idx: usize| {
|
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_pos = uniform_idx_as_vec2(sim.map_size_lg(), neighbor_idx);
|
||||||
let neighbor_chunk = sim.get(neighbor_pos)?;
|
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(
|
neighbor_chunk.river.river_kind.and(
|
||||||
Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river))
|
Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river))
|
||||||
)
|
)
|
||||||
}).collect::<Vec<_>>();
|
}).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()
|
.iter()
|
||||||
.filter(|(pos, _, _)| (pos - chunk_pos).map(i32::abs).reduce_max() <= 1)
|
.filter(|(pos, _, _)| (pos - chunk_pos).map(i32::abs).reduce_max() <= 1)
|
||||||
.all(|(pos, chunk, river)| river.river_kind == river_kind ||
|
.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 base_sea_level = CONFIG.sea_level - 1.0 + 0.01;
|
||||||
let homogeneous_water_level = if homogeneous_water_level {
|
let homogeneous_water_level = if homogeneous_water_level {
|
||||||
match river_kind {
|
match river_kind {
|
||||||
@ -585,20 +619,20 @@ impl<'a> ColumnGen<'a> {
|
|||||||
|
|
||||||
sim_chunk,
|
sim_chunk,
|
||||||
catmull_rom_gen,
|
catmull_rom_gen,
|
||||||
|
river_scratch: Vec::with_capacity(neighbor_river_data.len()),
|
||||||
neighbor_river_data,
|
neighbor_river_data,
|
||||||
homogeneous_water_level,
|
homogeneous_water_level,
|
||||||
// spawn_rules,
|
// 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 - 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 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 wposy = rel_chunk_pos/*.y*/ as f32 / TerrainChunkSize::RECT_SIZE.y as f32;
|
||||||
let cubic_y = cubic(wposy);
|
let cubic_y = cubic(wposy);
|
||||||
|
|
||||||
ColumnGen1D {
|
ColumnGen1D {
|
||||||
parent: self,
|
|
||||||
wpos_row,
|
wpos_row,
|
||||||
wposy,
|
wposy,
|
||||||
cubic_y,
|
cubic_y,
|
||||||
@ -620,25 +654,27 @@ impl<'a> ColumnGen<'a> {
|
|||||||
surface_veg_spline: self.surface_veg_spline.eval_at_row(cubic_y),
|
surface_veg_spline: self.surface_veg_spline.eval_at_row(cubic_y),
|
||||||
cliff_height_spline: self.cliff_height_spline.eval_at_row(cubic_y),
|
cliff_height_spline: self.cliff_height_spline.eval_at_row(cubic_y),
|
||||||
/* spawn_rules, */
|
/* 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 Index = /*(Vec2<i32>, IndexRef<'a>, Option<&'a Calendar>)*//*Vec2<i32>*/i32;
|
||||||
type Sample = /*Option<*/ColumnSample/*<'a>*//*>*/;
|
type Sample = /*Option<*/ColumnSample/*<'a>*//*>*/;
|
||||||
|
|
||||||
/// Precondition: wpos should be inside the chunk associated with self.chunk_pos.
|
/// Precondition: wpos should be inside the chunk associated with self.chunk_pos.
|
||||||
fn get(&self, /*(wpos, index, calendar)*/wpos_col: Self::Index) -> ColumnSample/*<'a>*/ {
|
fn get(&'c mut self, /*(wpos, index, calendar)*/wpos_col: Self::Index) -> ColumnSample/*<'a>*/ {
|
||||||
let &Self {
|
let &mut Self {
|
||||||
parent,
|
parent: ref mut parent,
|
||||||
wpos_row,
|
wpos_row,
|
||||||
wposy,
|
wposy,
|
||||||
cubic_y,
|
cubic_y,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let &ColumnGen {
|
let &mut ColumnGen {
|
||||||
sim,
|
sim,
|
||||||
chunk_pos,
|
chunk_pos,
|
||||||
index,
|
index,
|
||||||
@ -646,8 +682,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
|
|||||||
sim_chunk,
|
sim_chunk,
|
||||||
homogeneous_water_level,
|
homogeneous_water_level,
|
||||||
ref neighbor_river_data,
|
ref neighbor_river_data,
|
||||||
|
ref mut river_scratch,
|
||||||
..
|
..
|
||||||
} = parent;
|
} = *parent;
|
||||||
|
|
||||||
let wpos = Vec2::new(wpos_col, wpos_row);
|
let wpos = Vec2::new(wpos_col, wpos_row);
|
||||||
let wposf = wpos.map(|e| e as f64);
|
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>| {
|
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
|
// 0.0f32
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -754,11 +795,15 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
|
|||||||
|
|
||||||
let riverless_alt = alt;
|
let riverless_alt = alt;
|
||||||
let (alt, water_level, water_dist, water_dist_) = if let Some(water_level) = homogeneous_water_level {
|
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 {
|
} else {
|
||||||
(alt, base_sea_level, None::<f32>, None::<f32>)
|
/* let mut lake_inner = || */{
|
||||||
/* let lake_width = (TerrainChunkSize::RECT_SIZE.x as f64 * 2.0f64.sqrt()) + 6.0;
|
/* return (alt, base_sea_level, None::<f32>, None::<f32>); */
|
||||||
let neighbor_river_data = neighbor_river_data
|
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()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.map(|(posj, chunkj, river)| {
|
.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| {
|
let downhill_pos = downhill_pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
|
||||||
e.div_euclid(sz as i32)
|
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 direction = neighbor_wpos - downhill_wpos;
|
||||||
let river_width_min = if let RiverKind::River { cross_section } = kind {
|
let river_width_min = if let RiverKind::River { cross_section } = kind {
|
||||||
cross_section.x as f64
|
cross_section.x as f64
|
||||||
@ -858,7 +903,7 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
|
|||||||
return (posj, chunkj, river, None);
|
return (posj, chunkj, river, None);
|
||||||
}
|
}
|
||||||
let neighbor_pass_wpos =
|
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
|
let neighbor_pass_pos = neighbor_pass_pos
|
||||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
|
||||||
let coeffs = river_spline_coeffs(
|
let coeffs = river_spline_coeffs(
|
||||||
@ -874,13 +919,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
|
|||||||
Some(RiverKind::Lake { .. } | RiverKind::Ocean)
|
Some(RiverKind::Lake { .. } | RiverKind::Ocean)
|
||||||
) {
|
) {
|
||||||
let water_chunk = posj.map(|e| e as f64);
|
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 {
|
let water_aabr = Aabr {
|
||||||
min: water_chunk * 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 + 1.0) * neighbor_coef - 4.0
|
max: (water_chunk + 0.5) * neighbor_coef - 4.0
|
||||||
+ lake_width_noise * 8.0,
|
+ lake_width_noise * 8.0,
|
||||||
};
|
};
|
||||||
let pos = water_aabr.projected_point(wposf);
|
let pos = water_aabr.projected_point(wposf);
|
||||||
@ -925,13 +966,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
|
|||||||
},
|
},
|
||||||
RiverKind::Ocean => {
|
RiverKind::Ocean => {
|
||||||
let water_chunk = posj.map(|e| e as f64);
|
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 {
|
let water_aabr = Aabr {
|
||||||
min: water_chunk * 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 + 1.0) * 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);
|
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
|
lake_width * 0.5
|
||||||
};
|
};
|
||||||
let river_width_noise =
|
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)
|
.max(-1.0)
|
||||||
.min(1.0)
|
.min(1.0)
|
||||||
.mul(0.5)
|
.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);
|
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
|
// has been carefully designed to handle innumeral edge cases. Please
|
||||||
// test any changes to this code extremely well to avoid regressions: some
|
// test any changes to this code extremely well to avoid regressions: some
|
||||||
// edge cases are very rare indeed!
|
// 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),
|
WeightedSum::default().with(riverless_alt, 1.0),
|
||||||
|alt, (river_chunk_idx, river_chunk, river, dist_info)| match (
|
|alt, (river_chunk_idx, river_chunk, river, dist_info)| match (
|
||||||
river.river_kind,
|
river.river_kind,
|
||||||
@ -1429,7 +1467,9 @@ impl<'a, 'b> Sampler<'a, 'b> for ColumnGen1D<'a, 'b> {
|
|||||||
f32::MIN
|
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)))
|
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 cliff_offset = cliff * cliff_height;
|
||||||
let riverless_alt_delta = riverless_alt_delta + (cliff - 0.5) * 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.
|
// NOTE: To disable warp, uncomment this line.
|
||||||
// let warp_factor = 0.0;
|
// 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 Index = /*(Vec2<i32>, IndexRef<'a>, Option<&'a Calendar>)*/Vec2<i32>;
|
||||||
type Sample = ColumnSample/*<'a>*/;
|
type Sample = ColumnSample/*<'a>*/;
|
||||||
|
|
||||||
/// Precondition: wpos should be inside the chunk associated with self.chunk_pos.
|
/// Precondition: wpos should be inside the chunk associated with self.chunk_pos.
|
||||||
fn get(&self, /*(wpos, index, calendar)*/wpos: Self::Index) -> ColumnSample/*<'a>*/ {
|
fn get(&'b mut self, /*(wpos, index, calendar)*/wpos: Self::Index) -> ColumnSample/*<'a>*/ {
|
||||||
let col = self.eval_at_row(wpos.y);
|
let mut col = self.eval_at_row(wpos.y);
|
||||||
col.get(wpos.x)
|
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)]
|
#[derive(Clone, Default)]
|
||||||
pub struct ColumnSample/*<'a>*/ {
|
pub struct ColumnSample/*<'a>*/ {
|
||||||
pub alt: f32,
|
pub alt: f32,
|
||||||
|
@ -1061,8 +1061,6 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
|||||||
max: aabr.min + 1,
|
max: aabr.min + 1,
|
||||||
}; */
|
}; */
|
||||||
canvas.foreach_col_area(aabr, /*Aabr { min: canvas.wpos(), max: canvas.wpos() + 1 }, */|canvas, wpos2d, col| {
|
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(
|
let /*kind*/(kind, water_mode) = /* scatter.iter().enumerate().find_map(
|
||||||
|(
|
|(
|
||||||
i,
|
i,
|
||||||
@ -1099,7 +1097,7 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
|||||||
.unwrap_or(density); */
|
.unwrap_or(density); */
|
||||||
if density > 0.0
|
if density > 0.0
|
||||||
&& rng.gen::<f32>() < density //RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
&& 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
|
let block_kind = canvas
|
||||||
.get(Vec3::new(wpos2d.x, wpos2d.y, col.alt as i32))
|
.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
|
// Blit base structure
|
||||||
if let Some(base_structures) = spot_config.base_structures {
|
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 structure = structures.choose(&mut rng).unwrap();
|
||||||
let origin = spot_wpos2d.with_z(
|
let origin = spot_wpos2d.with_z(
|
||||||
canvas
|
canvas
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
arbitrary_enum_discriminant,
|
arbitrary_enum_discriminant,
|
||||||
associated_const_equality,
|
associated_const_equality,
|
||||||
bool_to_option,
|
bool_to_option,
|
||||||
|
generic_associated_types,
|
||||||
label_break_value,
|
label_break_value,
|
||||||
option_zip,
|
option_zip,
|
||||||
portable_simd,
|
portable_simd,
|
||||||
@ -52,7 +53,7 @@ use crate::{
|
|||||||
index::Index,
|
index::Index,
|
||||||
layer::spot::Spot,
|
layer::spot::Spot,
|
||||||
site::{SiteKind, SpawnRules},
|
site::{SiteKind, SpawnRules},
|
||||||
util::{Grid, Sampler},
|
util::{Grid, Sampler, SamplerMut},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
@ -162,7 +163,7 @@ impl World {
|
|||||||
_ => 0,
|
_ => 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,
|
/* civ::SiteKind::Tree | */civ::SiteKind::GiantTree => world_msg::SiteKind::Tree,
|
||||||
// TODO: Maybe change?
|
// TODO: Maybe change?
|
||||||
civ::SiteKind::Gnarling | civ::SiteKind::Citadel => world_msg::SiteKind::Gnarling,
|
civ::SiteKind::Gnarling | civ::SiteKind::Citadel => world_msg::SiteKind::Gnarling,
|
||||||
@ -198,7 +199,7 @@ impl World {
|
|||||||
chunk_pos: Vec2<i32>,
|
chunk_pos: Vec2<i32>,
|
||||||
index: IndexRef<'a>,
|
index: IndexRef<'a>,
|
||||||
/* calendar: Option<&'_ Calendar>, */
|
/* 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>,
|
Index = /*(Vec2<i32>, IndexRef, Option<&'_ Calendar>)*/Vec2<i32>,
|
||||||
Sample = ColumnSample/*<'a>*/,
|
Sample = ColumnSample/*<'a>*/,
|
||||||
> + 'a> {
|
> + 'a> {
|
||||||
@ -227,9 +228,9 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::result_unit_err)]
|
#[allow(clippy::result_unit_err)]
|
||||||
pub fn generate_chunk(
|
pub fn generate_chunk<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
index: IndexRef,
|
index: IndexRef<'a>,
|
||||||
chunk_pos: Vec2<i32>,
|
chunk_pos: Vec2<i32>,
|
||||||
// TODO: misleading name
|
// TODO: misleading name
|
||||||
mut should_continue: impl FnMut() -> bool,
|
mut should_continue: impl FnMut() -> bool,
|
||||||
@ -239,7 +240,8 @@ impl World {
|
|||||||
let calendar = self.sim.calendar.as_ref();
|
let calendar = self.sim.calendar.as_ref();
|
||||||
|
|
||||||
// FIXME: Deal with this properly if it's not okay to exit early.
|
// 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);
|
// dbg!(&sampler.column_gen.chaos_spline);
|
||||||
|
|
||||||
let air = Block::air(SpriteKind::Empty);
|
let air = Block::air(SpriteKind::Empty);
|
||||||
@ -272,18 +274,45 @@ impl World {
|
|||||||
let grid_border = /*4*/0;
|
let grid_border = /*4*/0;
|
||||||
let chunk_wpos2d = chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
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 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> =
|
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, */
|
/* TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, */
|
||||||
|offs_y| {
|
column_gen,
|
||||||
let column_gen = sampler.column_gen.eval_at_row(chunk_wpos2d.y/* - grid_border*/ + offs_y);
|
/*constrain_(chunk_wpos2d.y)
|
||||||
move |offs_x| {
|
/*/*constrain_(*/move |column_gen: &mut crate::column::ColumnGen<'a>, offs_y| {
|
||||||
/* ZCache {
|
/*let mut column_gen = */column_gen.eval_at_row(chunk_wpos2d.y/* - grid_border*/ + offs_y)
|
||||||
sample: */column_gen.get(chunk_wpos2d.x/* - grid_border*/ + offs_x)/*,
|
}/*)*/*/,*/
|
||||||
calendar: column_gen.parent.sim.calendar.as_ref(),
|
/* /*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*/ */
|
/* |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs/*, index, calendar*/)/*None*/ */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ use crate::{
|
|||||||
layer::spot::Spot,
|
layer::spot::Spot,
|
||||||
site::Site,
|
site::Site,
|
||||||
util::{
|
util::{
|
||||||
seed_expan, DHashSet, FastNoise, FastNoise2d, RandomField, Sampler, StructureGen2d,
|
seed_expan, DHashSet, FastNoise, FastNoise2d, RandomField, Sampler, SamplerMut, StructureGen2d,
|
||||||
CARDINALS, LOCALITY, NEIGHBORS,
|
CARDINALS, LOCALITY, NEIGHBORS,
|
||||||
},
|
},
|
||||||
IndexRef, CONFIG,
|
IndexRef, CONFIG,
|
||||||
@ -136,6 +136,7 @@ pub(crate) struct GenCtx {
|
|||||||
pub uplift_nz: Worley,
|
pub uplift_nz: Worley,
|
||||||
|
|
||||||
pub fast_hill_nz: Value,
|
pub fast_hill_nz: Value,
|
||||||
|
pub fast_small_nz: /*FastNoise2d*/Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
@ -536,6 +537,7 @@ impl WorldSim {
|
|||||||
let uplift_scale = 128.0;
|
let uplift_scale = 128.0;
|
||||||
let uplift_turb_scale = uplift_scale / 4.0;
|
let uplift_turb_scale = uplift_scale / 4.0;
|
||||||
let hill_nz_seed;
|
let hill_nz_seed;
|
||||||
|
let small_nz_seed;
|
||||||
|
|
||||||
// NOTE: Changing order will significantly change WorldGen, so try not to!
|
// NOTE: Changing order will significantly change WorldGen, so try not to!
|
||||||
let gen_ctx = GenCtx {
|
let gen_ctx = GenCtx {
|
||||||
@ -561,7 +563,7 @@ impl WorldSim {
|
|||||||
.set_lacunarity(2.0)
|
.set_lacunarity(2.0)
|
||||||
.set_seed(rng.gen()),
|
.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()),
|
rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()),
|
||||||
tree_nz: BasicMulti::new()
|
tree_nz: BasicMulti::new()
|
||||||
.set_octaves(12)
|
.set_octaves(12)
|
||||||
@ -572,6 +574,7 @@ impl WorldSim {
|
|||||||
|
|
||||||
structure_gen: StructureGen2d::new(rng.gen(), 24, 10),
|
structure_gen: StructureGen2d::new(rng.gen(), 24, 10),
|
||||||
_big_structure_gen: StructureGen2d::new(rng.gen(), 768, 512),
|
_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),
|
_region_gen: StructureGen2d::new(rng.gen(), 400, 96),
|
||||||
humid_nz: Billow::new()
|
humid_nz: Billow::new()
|
||||||
.set_octaves(9)
|
.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
|
let in_bounds = chunk_pos
|
||||||
.map2(self.map_size_lg().chunks(), |e, sz| {
|
.map2(self.map_size_lg().chunks(), |e, sz| {
|
||||||
e > 0 && e < sz as i32 - 2
|
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)))
|
.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>
|
pub fn get_interpolated<T, F>(&self, pos: Vec2<i32>, mut f: F) -> Option<T>
|
||||||
where
|
where
|
||||||
@ -2309,7 +2312,7 @@ impl SimChunk {
|
|||||||
Some(
|
Some(
|
||||||
uniform_idx_as_vec2(map_size_lg, downhill_pre as usize)
|
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)
|
||||||
+ 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()
|
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 {
|
pub fn get_biome(&self) -> BiomeKind {
|
||||||
let savannah_hum_temp = [0.05..0.55, 0.3..1.6];
|
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> {
|
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);
|
let pos = uniform_idx_as_vec2(map_size_lg, posi);
|
||||||
// NOTE: want to keep this such that the chunk index is in ascending order!
|
// NOTE: want to keep this such that the chunk index is in ascending order!
|
||||||
let grid_size = 3i32;
|
let grid_size = 2i32;
|
||||||
let grid_bounds = 2 * grid_size + 1;
|
let grid_bounds = 2 * (grid_size + 1)/* + 1*/;
|
||||||
(0..grid_bounds * grid_bounds)
|
(0..grid_bounds * grid_bounds)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |index| {
|
.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 towns = EconStatistics::default();
|
||||||
let mut dungeons = EconStatistics::default();
|
let mut dungeons = EconStatistics::default();
|
||||||
for site in index.sites.ids() {
|
for site in index.sites.ids() {
|
||||||
@ -123,7 +123,7 @@ impl Environment {
|
|||||||
towns += site.economy.pop
|
towns += site.economy.pop
|
||||||
},
|
},
|
||||||
SiteKind::Dungeon(_) => dungeons += site.economy.pop,
|
SiteKind::Dungeon(_) => dungeons += site.economy.pop,
|
||||||
SiteKind::Castle(_) => castles += site.economy.pop,
|
/* SiteKind::Castle(_) => castles += site.economy.pop, */
|
||||||
/* SiteKind::Tree(_) => (), */
|
/* SiteKind::Tree(_) => (), */
|
||||||
SiteKind::GiantTree(_) => (),
|
SiteKind::GiantTree(_) => (),
|
||||||
SiteKind::Gnarling(_) => {},
|
SiteKind::Gnarling(_) => {},
|
||||||
@ -137,14 +137,14 @@ impl Environment {
|
|||||||
towns.sum / (towns.count as f32)
|
towns.sum / (towns.count as f32)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if castles.valid() {
|
/* if castles.valid() {
|
||||||
info!(
|
info!(
|
||||||
"Castles {:.0}-{:.0} avg {:.0}",
|
"Castles {:.0}-{:.0} avg {:.0}",
|
||||||
castles.min,
|
castles.min,
|
||||||
castles.max,
|
castles.max,
|
||||||
castles.sum / (castles.count as f32)
|
castles.sum / (castles.count as f32)
|
||||||
);
|
);
|
||||||
}
|
} */
|
||||||
if dungeons.valid() {
|
if dungeons.valid() {
|
||||||
info!(
|
info!(
|
||||||
"Dungeons {:.0}-{:.0} avg {:.0}",
|
"Dungeons {:.0}-{:.0} avg {:.0}",
|
||||||
@ -389,9 +389,9 @@ mod tests {
|
|||||||
crate::site::SiteKind::Dungeon(_) => {
|
crate::site::SiteKind::Dungeon(_) => {
|
||||||
common::terrain::site::SitesKind::Dungeon
|
common::terrain::site::SitesKind::Dungeon
|
||||||
},
|
},
|
||||||
crate::site::SiteKind::Castle(_) => {
|
/* crate::site::SiteKind::Castle(_) => {
|
||||||
common::terrain::site::SitesKind::Castle
|
common::terrain::site::SitesKind::Castle
|
||||||
},
|
}, */
|
||||||
_ => common::terrain::site::SitesKind::Void,
|
_ => common::terrain::site::SitesKind::Void,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -417,9 +417,9 @@ mod tests {
|
|||||||
// loading on demand using the public API. There is no way to set
|
// loading on demand using the public API. There is no way to set
|
||||||
// the name, do we care?
|
// the name, do we care?
|
||||||
let mut settlement = match i.kind {
|
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),
|
crate::site::Castle::generate(wpos, None, &mut rng),
|
||||||
),
|
), */
|
||||||
common::terrain::site::SitesKind::Dungeon => {
|
common::terrain::site::SitesKind::Dungeon => {
|
||||||
crate::site::Site::dungeon(crate::site2::Site::generate_dungeon(
|
crate::site::Site::dungeon(crate::site2::Site::generate_dungeon(
|
||||||
&crate::Land::empty(),
|
&crate::Land::empty(),
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
mod block_mask;
|
mod block_mask;
|
||||||
mod castle;
|
// mod castle;
|
||||||
pub mod economy;
|
pub mod economy;
|
||||||
pub mod namegen;
|
pub mod namegen;
|
||||||
pub mod settlement;
|
// pub mod settlement;
|
||||||
// mod tree;
|
// mod tree;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use self::{
|
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};
|
use crate::{column::ColumnSample, site2, Canvas};
|
||||||
@ -17,11 +17,7 @@ use serde::Deserialize;
|
|||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Colors {
|
pub struct Colors;
|
||||||
pub castle: castle::Colors,
|
|
||||||
pub dungeon: site2::plot::dungeon::Colors,
|
|
||||||
pub settlement: settlement::Colors,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SpawnRules {
|
pub struct SpawnRules {
|
||||||
pub trees: bool,
|
pub trees: bool,
|
||||||
@ -62,7 +58,7 @@ pub struct Site {
|
|||||||
pub enum SiteKind {
|
pub enum SiteKind {
|
||||||
/* Settlement(Settlement), */
|
/* Settlement(Settlement), */
|
||||||
Dungeon(site2::Site),
|
Dungeon(site2::Site),
|
||||||
Castle(Castle),
|
/* Castle(Castle), */
|
||||||
Refactor(site2::Site),
|
Refactor(site2::Site),
|
||||||
CliffTown(site2::Site),
|
CliffTown(site2::Site),
|
||||||
/* Tree(tree::Tree), */
|
/* Tree(tree::Tree), */
|
||||||
@ -92,12 +88,12 @@ impl Site {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn castle(c: Castle) -> Self {
|
/* pub fn castle(c: Castle) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: SiteKind::Castle(c),
|
kind: SiteKind::Castle(c),
|
||||||
economy: Economy::default(),
|
economy: Economy::default(),
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
pub fn refactor(s: site2::Site) -> Self {
|
pub fn refactor(s: site2::Site) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -131,7 +127,7 @@ impl Site {
|
|||||||
match &self.kind {
|
match &self.kind {
|
||||||
/* SiteKind::Settlement(s) => s.radius(), */
|
/* SiteKind::Settlement(s) => s.radius(), */
|
||||||
SiteKind::Dungeon(d) => d.radius(),
|
SiteKind::Dungeon(d) => d.radius(),
|
||||||
SiteKind::Castle(c) => c.radius(),
|
/* SiteKind::Castle(c) => c.radius(), */
|
||||||
SiteKind::Refactor(s) => s.radius(),
|
SiteKind::Refactor(s) => s.radius(),
|
||||||
SiteKind::CliffTown(ct) => ct.radius(),
|
SiteKind::CliffTown(ct) => ct.radius(),
|
||||||
/* SiteKind::Tree(t) => t.radius(), */
|
/* SiteKind::Tree(t) => t.radius(), */
|
||||||
@ -144,7 +140,7 @@ impl Site {
|
|||||||
match &self.kind {
|
match &self.kind {
|
||||||
/* SiteKind::Settlement(s) => s.get_origin(), */
|
/* SiteKind::Settlement(s) => s.get_origin(), */
|
||||||
SiteKind::Dungeon(d) => d.origin,
|
SiteKind::Dungeon(d) => d.origin,
|
||||||
SiteKind::Castle(c) => c.get_origin(),
|
/* SiteKind::Castle(c) => c.get_origin(), */
|
||||||
SiteKind::Refactor(s) => s.origin,
|
SiteKind::Refactor(s) => s.origin,
|
||||||
SiteKind::CliffTown(ct) => ct.origin,
|
SiteKind::CliffTown(ct) => ct.origin,
|
||||||
/* SiteKind::Tree(t) => t.origin, */
|
/* SiteKind::Tree(t) => t.origin, */
|
||||||
@ -157,7 +153,7 @@ impl Site {
|
|||||||
match &self.kind {
|
match &self.kind {
|
||||||
/* SiteKind::Settlement(s) => s.spawn_rules(wpos), */
|
/* SiteKind::Settlement(s) => s.spawn_rules(wpos), */
|
||||||
SiteKind::Dungeon(d) => d.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::Refactor(s) => s.spawn_rules(wpos),
|
||||||
SiteKind::CliffTown(ct) => ct.spawn_rules(wpos),
|
SiteKind::CliffTown(ct) => ct.spawn_rules(wpos),
|
||||||
/* SiteKind::Tree(t) => t.spawn_rules(wpos), */
|
/* SiteKind::Tree(t) => t.spawn_rules(wpos), */
|
||||||
@ -170,7 +166,7 @@ impl Site {
|
|||||||
match &self.kind {
|
match &self.kind {
|
||||||
/* SiteKind::Settlement(s) => s.name(), */
|
/* SiteKind::Settlement(s) => s.name(), */
|
||||||
SiteKind::Dungeon(d) => d.name(),
|
SiteKind::Dungeon(d) => d.name(),
|
||||||
SiteKind::Castle(c) => c.name(),
|
/* SiteKind::Castle(c) => c.name(), */
|
||||||
SiteKind::Refactor(s) => s.name(),
|
SiteKind::Refactor(s) => s.name(),
|
||||||
SiteKind::CliffTown(ct) => ct.name(),
|
SiteKind::CliffTown(ct) => ct.name(),
|
||||||
/* SiteKind::Tree(_) => "Giant Tree", */
|
/* 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) {
|
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 info = canvas.info();
|
||||||
let get_col = |wpos| info.col(wpos + info.wpos);
|
// let get_col = |wpos| info.col(wpos + info.wpos);
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
/* SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), */
|
/* SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), */
|
||||||
SiteKind::Dungeon(d) => d.render(canvas, arena, dynamic_rng),
|
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::Refactor(s) => s.render(canvas, arena, dynamic_rng),
|
||||||
SiteKind::CliffTown(ct) => ct.render(canvas, arena, dynamic_rng),
|
SiteKind::CliffTown(ct) => ct.render(canvas, arena, dynamic_rng),
|
||||||
/* SiteKind::Tree(t) => t.render(canvas, 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)
|
s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy)
|
||||||
}, */
|
}, */
|
||||||
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement),
|
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement),
|
||||||
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::Refactor(_) => {},
|
||||||
SiteKind::CliffTown(_) => {},
|
SiteKind::CliffTown(_) => {},
|
||||||
/* SiteKind::Tree(_) => {}, */
|
/* 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,
|
unit_chooser::UnitChooser,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use common::grid::Grid;
|
pub use common::grid::{Grid, RowGen};
|
||||||
|
|
||||||
use fxhash::FxHasher32;
|
use fxhash::FxHasher32;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
@ -5,9 +5,9 @@ pub trait Sampler<'a, 'b>: Sized {
|
|||||||
fn get(&'b self, index: Self::Index) -> Self::Sample;
|
fn get(&'b self, index: Self::Index) -> Self::Sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SamplerMut<'a>: Sized {
|
pub trait SamplerMut<'a, 'b>: Sized {
|
||||||
type Index: 'a;
|
type Index: 'a;
|
||||||
type Sample: '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