Added labour value to economy, castle improvements

This commit is contained in:
Joshua Barretto 2020-07-14 16:21:57 +01:00
parent bdb39b8e7f
commit 1763693f5f
10 changed files with 226 additions and 149 deletions

View File

@ -1 +0,0 @@
,joshua,archbox.localdomain,17.06.2020 16:07,file:///home/joshua/.config/libreoffice/4;

View File

@ -15,7 +15,7 @@ use common::{
sync::{Uid, WorldSyncExt},
terrain::{Block, BlockKind, TerrainChunkSize},
util::Dir,
vol::{RectVolSize, WriteVol},
vol::RectVolSize,
LoadoutBuilder,
};
use rand::Rng;

View File

@ -127,7 +127,7 @@ impl Civs {
let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32);
let flatten_radius = match &site.kind {
SiteKind::Settlement => 8.0,
SiteKind::Settlement => 10.0,
SiteKind::Dungeon => 2.0,
SiteKind::Castle => 5.0,
};
@ -148,9 +148,10 @@ impl Civs {
0.0
}; // Raise the town centre up a little
let pos = site.center + offs;
let factor = (1.0
let factor = ((1.0
- (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius)
* 0.8;
* 1.25)
.min(1.0);
ctx.sim
.get_mut(pos)
// Don't disrupt chunks that are near water
@ -170,9 +171,11 @@ impl Civs {
let mut cnt = 0;
for sim_site in this.sites.values() {
cnt += 1;
let wpos = sim_site.center.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
e * sz as i32 + sz as i32 / 2
});
let wpos = sim_site
.center
.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
e * sz as i32 + sz as i32 / 2
});
let mut rng = ctx.reseed().rng;
let site = index.sites.insert(match &sim_site.kind {

View File

@ -26,6 +26,7 @@ pub use self::{
use crate::{
all::ForestKind,
civ::Place,
column::{ColumnGen, ColumnSample},
site::Site,
util::{seed_expan, FastNoise, RandomField, StructureGen2d, LOCALITY, NEIGHBORS},
Index, CONFIG,
@ -1699,8 +1700,9 @@ impl WorldSim {
Some(z0 + z1 + z2 + z3)
}
/// Return the distance to the nearest path in blocks, along with the closest point on the
/// path, the path metadata, and the tangent vector of that path.
/// Return the distance to the nearest path in blocks, along with the
/// closest point on the path, the path metadata, and the tangent vector
/// of that path.
pub fn get_nearest_path(&self, wpos: Vec2<i32>) -> Option<(f32, Vec2<f32>, Path, Vec2<f32>)> {
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
e.div_euclid(sz as i32)

View File

@ -23,7 +23,7 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) {
write!(f, "{:?} Value,", g).unwrap();
}
for g in Good::list() {
write!(f, "{:?} Price,", g).unwrap();
write!(f, "{:?} LaborVal,", g).unwrap();
}
for g in Good::list() {
write!(f, "{:?} Stock,", g).unwrap();
@ -37,6 +37,9 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) {
for l in Labor::list() {
write!(f, "{:?} Productivity,", l).unwrap();
}
for l in Labor::list() {
write!(f, "{:?} Yields,", l).unwrap();
}
writeln!(f, "").unwrap();
for i in 0..(HISTORY_DAYS / TICK_PERIOD) as i32 {
@ -53,7 +56,7 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) {
write!(f, "{:?},", site.economy.values[*g].unwrap_or(-1.0)).unwrap();
}
for g in Good::list() {
write!(f, "{:?},", site.economy.prices[*g]).unwrap();
write!(f, "{:?},", site.economy.labor_values[*g]).unwrap();
}
for g in Good::list() {
write!(f, "{:?},", site.economy.stocks[*g]).unwrap();
@ -67,6 +70,9 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) {
for l in Labor::list() {
write!(f, "{:?},", site.economy.productivity[*l]).unwrap();
}
for l in Labor::list() {
write!(f, "{:?},", site.economy.yields[*l]).unwrap();
}
writeln!(f, "").unwrap();
}
}
@ -144,11 +150,6 @@ pub fn tick_site_economy(index: &mut Index, site: Id<Site>, dt: f32) {
};
});
site.economy.prices = site.economy.stocks.clone().map(|g, stock| {
// Price rationalisation
demand[g] / (supply[g] + stocks[g])
});
// Update export targets based on relative values
let value_avg = values
.iter()
@ -169,7 +170,8 @@ pub fn tick_site_economy(index: &mut Index, site: Id<Site>, dt: f32) {
// Redistribute workforce according to relative good values
let labor_ratios = productivity.clone().map(|labor, (output_good, _)| {
site.economy.values[output_good].unwrap_or(0.0) * site.economy.productivity[labor]
site.economy.values[output_good].unwrap_or(0.0) * site.economy.yields[labor]
//(site.economy.prices[output_good] - site.economy.material_costs[output_good]) * site.economy.yields[labor]
//* demand[output_good] / supply[output_good].max(0.001)
});
let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::<f32>().max(0.01);
@ -182,6 +184,7 @@ pub fn tick_site_economy(index: &mut Index, site: Id<Site>, dt: f32) {
// Production
let stocks_before = site.economy.stocks.clone();
site.economy.labor_values = Default::default();
for (labor, orders) in orders.iter() {
let scale = if let Some(labor) = labor {
site.economy.labors[*labor]
@ -207,12 +210,16 @@ pub fn tick_site_economy(index: &mut Index, site: Id<Site>, dt: f32) {
.min_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor));
let mut total_materials_cost = 0.0;
for (good, amount) in orders {
// What quantity is this order requesting?
let quantity = *amount * scale;
// What amount gets actually used in production?
let used = quantity * labor_productivity;
// Material cost of each factor of production
total_materials_cost += used * site.economy.labor_values[*good];
// Deplete stocks accordingly
site.economy.stocks[*good] = (site.economy.stocks[*good] - used).max(0.0);
}
@ -222,10 +229,19 @@ pub fn tick_site_economy(index: &mut Index, site: Id<Site>, dt: f32) {
let (stock, rate) = productivity[*labor];
let workers = site.economy.labors[*labor] * site.economy.pop;
let final_rate = rate;
let yield_per_worker = labor_productivity * final_rate;
let yield_per_worker = labor_productivity * final_rate * (1.0 + workers / 100.0);
site.economy.yields[*labor] = yield_per_worker;
site.economy.productivity[*labor] = labor_productivity;
site.economy.stocks[stock] += yield_per_worker * workers.powf(1.1);
let total_output = yield_per_worker * workers;
site.economy.stocks[stock] += total_output;
// Materials cost per unit
let material_cost_per_unit = total_materials_cost / total_output.max(0.001);
site.economy.material_costs[stock] = material_cost_per_unit;
site.economy.labor_values[stock] += material_cost_per_unit;
// Labor costs
let wages = 1.0;
site.economy.labor_values[stock] += workers * wages / total_output.max(0.001);
}
}

View File

@ -69,19 +69,6 @@ impl Castle {
let radius = 150;
// Adjust ground
if let Some(sim) = ctx.sim.as_mut() {
for rpos in Spiral2d::new()
.radius((radius as f32 * 0.7) as i32 / TerrainChunkSize::RECT_SIZE.x as i32)
{
sim.get_mut(wpos / TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + rpos)
.map(|chunk| {
chunk.surface_veg = 0.0;
chunk.path.clear();
});
}
}
let this = Self {
origin: wpos,
alt: ctx
@ -157,6 +144,23 @@ impl Castle {
this
}
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 { 1200.0 }
@ -182,13 +186,38 @@ impl Castle {
let wpos2d = wpos2d + offs;
let rpos = wpos2d - self.origin;
// Apply the dungeon entrance
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 {
let _ = vol.set(pos, Block::empty());
}
} else {
let _ = vol.set(
pos,
Block::new(
BlockKind::Normal,
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];

View File

@ -35,7 +35,9 @@ pub struct Economy {
pub surplus: MapVec<Good, f32>,
pub marginal_surplus: MapVec<Good, f32>,
pub values: MapVec<Good, Option<f32>>,
pub prices: MapVec<Good, f32>,
pub labor_values: MapVec<Good, f32>,
pub material_costs: MapVec<Good, f32>,
pub labors: MapVec<Labor, f32>,
pub yields: MapVec<Labor, f32>,
@ -51,7 +53,9 @@ impl Default for Economy {
surplus: Default::default(),
marginal_surplus: Default::default(),
values: Default::default(),
prices: Default::default(),
labor_values: Default::default(),
material_costs: Default::default(),
labors: Default::default(),
yields: Default::default(),

View File

@ -12,28 +12,60 @@ use common::{
use rand::prelude::*;
use vek::*;
const COLOR_THEMES: [Rgb<u8>; 17] = [
Rgb::new(0x1D, 0x4D, 0x45),
Rgb::new(0xB3, 0x7D, 0x60),
Rgb::new(0xAC, 0x5D, 0x26),
Rgb::new(0x32, 0x46, 0x6B),
Rgb::new(0x2B, 0x19, 0x0F),
Rgb::new(0x93, 0x78, 0x51),
Rgb::new(0x92, 0x57, 0x24),
Rgb::new(0x4A, 0x4E, 0x4E),
Rgb::new(0x2F, 0x32, 0x47),
Rgb::new(0x8F, 0x35, 0x43),
Rgb::new(0x6D, 0x1E, 0x3A),
Rgb::new(0x6D, 0xA7, 0x80),
Rgb::new(0x4F, 0xA0, 0x95),
Rgb::new(0xE2, 0xB9, 0x99),
Rgb::new(0x7A, 0x30, 0x22),
Rgb::new(0x4A, 0x06, 0x08),
Rgb::new(0x8E, 0xB4, 0x57),
pub struct ColorTheme {
roof: Rgb<u8>,
wall: Rgb<u8>,
support: Rgb<u8>,
}
const ROOF_COLORS: &[Rgb<u8>] = &[
// Rgb::new(0x1D, 0x4D, 0x45),
// Rgb::new(0xB3, 0x7D, 0x60),
// Rgb::new(0xAC, 0x5D, 0x26),
// Rgb::new(0x32, 0x46, 0x6B),
// Rgb::new(0x2B, 0x19, 0x0F),
// Rgb::new(0x93, 0x78, 0x51),
// Rgb::new(0x92, 0x57, 0x24),
// Rgb::new(0x4A, 0x4E, 0x4E),
// Rgb::new(0x2F, 0x32, 0x47),
// Rgb::new(0x8F, 0x35, 0x43),
// Rgb::new(0x6D, 0x1E, 0x3A),
// Rgb::new(0x6D, 0xA7, 0x80),
// Rgb::new(0x4F, 0xA0, 0x95),
// Rgb::new(0xE2, 0xB9, 0x99),
// Rgb::new(0x7A, 0x30, 0x22),
// Rgb::new(0x4A, 0x06, 0x08),
// Rgb::new(0x8E, 0xB4, 0x57),
Rgb::new(0x99, 0x5E, 0x54),
Rgb::new(0x43, 0x63, 0x64),
Rgb::new(0x76, 0x6D, 0x68),
Rgb::new(0x7B, 0x41, 0x61),
Rgb::new(0x52, 0x20, 0x20),
Rgb::new(0x1A, 0x4A, 0x59),
Rgb::new(0xCC, 0x76, 0x4E),
];
const WALL_COLORS: &[Rgb<u8>] = &[
Rgb::new(200, 180, 150),
Rgb::new(0xB8, 0xB4, 0xA4),
Rgb::new(0x76, 0x6D, 0x68),
Rgb::new(0xF3, 0xC9, 0x8F),
Rgb::new(0xD3, 0xB7, 0x99),
Rgb::new(0xE1, 0xAB, 0x91),
Rgb::new(0x82, 0x57, 0x4C),
Rgb::new(0xB9, 0x96, 0x77),
Rgb::new(0x6E, 0x4D, 0x3C),
];
const SUPPORT_COLORS: &[Rgb<u8>] = &[
Rgb::new(60, 45, 30),
Rgb::new(0x65, 0x55, 0x56),
Rgb::new(0x53, 0x33, 0x13),
Rgb::new(0x58, 0x42, 0x33),
];
pub struct House {
pub roof_color: Rgb<u8>,
pub colors: ColorTheme,
pub noise: RandomField,
pub roof_ribbing: bool,
pub roof_ribbing_diagonal: bool,
@ -126,6 +158,7 @@ impl Archetype for House {
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 },
@ -139,7 +172,7 @@ impl Archetype for House {
1 => Pillar::Tower(5 + rng.gen_range(1, 5)),
_ => Pillar::None,
},
levels: rng.gen_range(1, 4),
levels,
..Attr::generate(rng, locus)
},
locus,
@ -154,7 +187,10 @@ impl Archetype for House {
i as i32 * len / (branches_per_side - 1).max(1) as i32,
Branch {
len: rng.gen_range(8, 16) * flip,
attr: Attr::generate(rng, locus),
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(),
@ -169,7 +205,11 @@ impl Archetype for House {
};
let this = Self {
roof_color: *COLOR_THEMES.choose(rng).unwrap(),
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(),
@ -205,7 +245,7 @@ impl Archetype for House {
)
};
let make_block = |r, g, b| {
let make_block = |(r, g, b)| {
let nz = self
.noise
.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
@ -225,12 +265,11 @@ impl Archetype for House {
let foundation_layer = internal_layer + 1;
let floor_layer = foundation_layer + 1;
let foundation = make_block(100, 100, 100).with_priority(foundation_layer);
let log = make_block(60, 45, 30);
let floor = make_block(100, 75, 50);
let wall = make_block(200, 180, 150).with_priority(facade_layer);
let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b)
.with_priority(facade_layer - 1);
let foundation = make_block((100, 100, 100)).with_priority(foundation_layer);
let log = make_block(self.colors.support.into_tuple());
let floor = make_block((100, 75, 50));
let wall = make_block(self.colors.wall.into_tuple()).with_priority(facade_layer);
let roof = make_block(self.colors.roof.into_tuple()).with_priority(facade_layer - 1);
let empty = BlockMask::nothing();
let internal = BlockMask::new(Block::empty(), internal_layer);
let end_window = BlockMask::new(

View File

@ -2,15 +2,15 @@ pub mod archetype;
pub mod skeleton;
// Reexports
pub use self::{archetype::Archetype, skeleton::*};
pub use self::{
archetype::{house::House, keep::Keep, Archetype},
skeleton::*,
};
use common::terrain::Block;
use rand::prelude::*;
use vek::*;
pub type HouseBuilding = Building<archetype::house::House>;
pub type KeepBuilding = Building<archetype::keep::Keep>;
pub struct Building<A: Archetype> {
skel: Skeleton<A::Attr>,
archetype: A,

View File

@ -2,7 +2,7 @@ pub mod building;
mod town;
use self::{
building::{HouseBuilding, KeepBuilding},
building::{Building, House, Keep},
town::{District, Town},
};
use super::SpawnRules;
@ -86,8 +86,8 @@ const AREA_SIZE: u32 = 32;
fn to_tile(e: i32) -> i32 { ((e as f32).div_euclid(AREA_SIZE as f32)).floor() as i32 }
pub enum StructureKind {
House(HouseBuilding),
Keep(KeepBuilding),
House(Building<House>),
Keep(Building<Keep>),
}
pub struct Structure {
@ -403,12 +403,12 @@ impl Settlement {
let structure = Structure {
kind: if tile == town_center && i == 0 {
StructureKind::Keep(KeepBuilding::generate(
StructureKind::Keep(Building::<Keep>::generate(
ctx.rng,
Vec3::new(house_pos.x, house_pos.y, alt),
))
} else {
StructureKind::House(HouseBuilding::generate(
StructureKind::House(Building::<House>::generate(
ctx.rng,
Vec3::new(house_pos.x, house_pos.y, alt),
))
@ -671,94 +671,79 @@ impl Settlement {
}))
},
Some(Plot::Field { seed, crop, .. }) => {
if let Some(color) = col_sample.path.and_then(|(dist, _, path, _)| {
if dist < path.width {
Some(path.surface_color(
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
))
} else {
None
}
}) {
Some(color)
} else {
let furrow_dirs = [
Vec2::new(1, 0),
Vec2::new(0, 1),
Vec2::new(1, 1),
Vec2::new(-1, 1),
];
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
let furrow_dirs = [
Vec2::new(1, 0),
Vec2::new(0, 1),
Vec2::new(1, 1),
Vec2::new(-1, 1),
];
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
let dirt = Rgb::new(80, 55, 35).map(|e| {
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32))
let dirt = Rgb::new(80, 55, 35).map(|e| {
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32)
as u8
});
let mound =
Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| {
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32))
% 32) as u8
});
let mound =
Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| {
e + (self
.noise
.get(Vec3::broadcast((seed % 4096 + 1) as i32))
% 32) as u8
});
if in_furrow {
if roll(0, 5) == 0 {
surface_block = match crop {
Crop::Corn => Some(BlockKind::Corn),
Crop::Wheat if roll(1, 2) == 0 => {
Some(BlockKind::WheatYellow)
},
Crop::Wheat => Some(BlockKind::WheatGreen),
Crop::Cabbage if roll(2, 2) == 0 => {
Some(BlockKind::Cabbage)
},
Crop::Pumpkin if roll(3, 2) == 0 => {
Some(BlockKind::Pumpkin)
},
Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax),
Crop::Carrot if roll(5, 2) == 0 => {
Some(BlockKind::Carrot)
},
Crop::Tomato if roll(6, 2) == 0 => {
Some(BlockKind::Tomato)
},
Crop::Radish if roll(7, 2) == 0 => {
Some(BlockKind::Radish)
},
Crop::Turnip if roll(8, 2) == 0 => {
Some(BlockKind::Turnip)
},
Crop::Sunflower => Some(BlockKind::Sunflower),
_ => None,
}
.or_else(|| {
if roll(9, 400) == 0 {
Some(BlockKind::Scarecrow)
} else {
None
}
})
.map(|kind| Block::new(kind, Rgb::white()));
if in_furrow {
if roll(0, 5) == 0 {
surface_block = match crop {
Crop::Corn => Some(BlockKind::Corn),
Crop::Wheat if roll(1, 2) == 0 => {
Some(BlockKind::WheatYellow)
},
Crop::Wheat => Some(BlockKind::WheatGreen),
Crop::Cabbage if roll(2, 2) == 0 => {
Some(BlockKind::Cabbage)
},
Crop::Pumpkin if roll(3, 2) == 0 => {
Some(BlockKind::Pumpkin)
},
Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax),
Crop::Carrot if roll(5, 2) == 0 => Some(BlockKind::Carrot),
Crop::Tomato if roll(6, 2) == 0 => Some(BlockKind::Tomato),
Crop::Radish if roll(7, 2) == 0 => Some(BlockKind::Radish),
Crop::Turnip if roll(8, 2) == 0 => Some(BlockKind::Turnip),
Crop::Sunflower => Some(BlockKind::Sunflower),
_ => None,
}
} else if roll(0, 20) == 0 {
surface_block =
Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
} else if roll(1, 30) == 0 {
surface_block =
Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
.or_else(|| {
if roll(9, 400) == 0 {
Some(BlockKind::Scarecrow)
} else {
None
}
})
.map(|kind| Block::new(kind, Rgb::white()));
}
Some(if in_furrow { dirt } else { mound })
} else if roll(0, 20) == 0 {
surface_block =
Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
} else if roll(1, 30) == 0 {
surface_block =
Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
}
Some(if in_furrow { dirt } else { mound })
},
_ => None,
};
if let Some(color) = color {
if col_sample.water_dist.map(|dist| dist > 2.0).unwrap_or(true) {
let is_path = col_sample
.path
.map(|(dist, _, path, _)| dist < path.width)
.unwrap_or(false);
if col_sample.water_dist.map(|dist| dist > 2.0).unwrap_or(true) && !is_path
{
let diff = (surface_z - land_surface_z).abs();
for z in -8 - diff..3 + diff {
let pos = Vec3::new(offs.x, offs.y, surface_z + z);