savannah_hut

This commit is contained in:
flo 2023-06-03 11:54:58 +00:00 committed by Joshua Barretto
parent e71eee0cd2
commit 22854cbea2
8 changed files with 811 additions and 200 deletions

View File

@ -113,7 +113,15 @@ impl Data {
let matches_buildings = (|kind: &PlotKind| {
matches!(
kind,
PlotKind::House(_) | PlotKind::Workshop(_) | PlotKind::Plaza
PlotKind::House(_)
| PlotKind::Workshop(_)
| PlotKind::Plaza
| PlotKind::SavannahPit(_)
| PlotKind::SavannahHut(_)
| PlotKind::SavannahWorkshop(_)
| PlotKind::CliffTower(_)
| PlotKind::DesertCityMultiPlot(_)
| PlotKind::DesertCityTemple(_)
)
}) as _;
let matches_plazas = (|kind: &PlotKind| matches!(kind, PlotKind::Plaza)) as _;

View File

@ -330,7 +330,7 @@ impl Civs {
SiteKind::Castle => (16i32, 5.0),
SiteKind::Refactor => (32i32, 10.0),
SiteKind::CliffTown => (32i32, 10.0),
SiteKind::SavannahPit => (36i32, 20.0),
SiteKind::SavannahPit => (48i32, 25.0),
SiteKind::DesertCity => (64i32, 25.0),
SiteKind::ChapelSite => (36i32, 10.0),
SiteKind::Tree => (12i32, 8.0),

View File

@ -893,6 +893,11 @@ impl Site {
name: NameGen::location(&mut rng).generate_savannah_custom(),
..Site::default()
};
site.demarcate_obstacles(land);
site.make_plaza(land, &mut rng);
let size = 11.0 as i32;
let aabr = Aabr {
min: Vec2::broadcast(-size),
@ -915,6 +920,86 @@ impl Site {
hard_alt: Some(savannah_pit_alt),
});
}
let build_chance = Lottery::from(vec![(38.0, 1), (7.0, 2)]);
for _ in 0..45 {
match *build_chance.choose_seeded(rng.gen()) {
1 => {
// SavannahHut
let size = (4.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
site.find_roadside_aabr(
&mut rng,
4..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let savannah_hut = plot::SavannahHut::generate(
land,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let savannah_hut_alt = savannah_hut.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::SavannahHut(savannah_hut),
root_tile: aabr.center(),
tiles: aabr_tiles(aabr).collect(),
seed: rng.gen(),
});
site.blit_aabr(aabr, Tile {
kind: TileKind::Building,
plot: Some(plot),
hard_alt: Some(savannah_hut_alt),
});
} else {
site.make_plaza(land, &mut rng);
}
},
2 => {
// SavannahWorkshop
let size = (4.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
site.find_roadside_aabr(
&mut rng,
4..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let savannah_workshop = plot::SavannahWorkshop::generate(
land,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let savannah_workshop_alt = savannah_workshop.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::SavannahWorkshop(savannah_workshop),
root_tile: aabr.center(),
tiles: aabr_tiles(aabr).collect(),
seed: rng.gen(),
});
site.blit_aabr(aabr, Tile {
kind: TileKind::Building,
plot: Some(plot),
hard_alt: Some(savannah_workshop_alt),
});
} else {
site.make_plaza(land, &mut rng);
}
},
_ => {},
}
}
site
}
@ -1382,6 +1467,10 @@ impl Site {
PlotKind::GiantTree(giant_tree) => giant_tree.render_collect(self, canvas),
PlotKind::CliffTower(cliff_tower) => cliff_tower.render_collect(self, canvas),
PlotKind::SavannahPit(savannah_pit) => savannah_pit.render_collect(self, canvas),
PlotKind::SavannahHut(savannah_hut) => savannah_hut.render_collect(self, canvas),
PlotKind::SavannahWorkshop(savannah_workshop) => {
savannah_workshop.render_collect(self, canvas)
},
PlotKind::DesertCityMultiPlot(desert_city_multi_plot) => {
desert_city_multi_plot.render_collect(self, canvas)
},

View File

@ -9,7 +9,9 @@ pub mod dungeon;
mod giant_tree;
mod gnarling;
mod house;
mod savannah_hut;
mod savannah_pit;
mod savannah_workshop;
mod sea_chapel;
mod workshop;
@ -17,8 +19,9 @@ pub use self::{
adlet::AdletStronghold, bridge::Bridge, castle::Castle, citadel::Citadel,
cliff_tower::CliffTower, desert_city_multiplot::DesertCityMultiPlot,
desert_city_temple::DesertCityTemple, dungeon::Dungeon, giant_tree::GiantTree,
gnarling::GnarlingFortification, house::House, savannah_pit::SavannahPit,
sea_chapel::SeaChapel, workshop::Workshop,
gnarling::GnarlingFortification, house::House, savannah_hut::SavannahHut,
savannah_pit::SavannahPit, savannah_workshop::SavannahWorkshop, sea_chapel::SeaChapel,
workshop::Workshop,
};
use super::*;
@ -74,5 +77,7 @@ pub enum PlotKind {
CliffTower(CliffTower),
Citadel(Citadel),
SavannahPit(SavannahPit),
SavannahHut(SavannahHut),
SavannahWorkshop(SavannahWorkshop),
Bridge(Bridge),
}

View File

@ -883,16 +883,6 @@ impl Structure for CliffTower {
))
.fill(brick.clone());
}
// spawn mountaineers in each room
let spawn_pos = super_center.with_z(floor_level + 4);
let npc_amount = RandomField::new(0).get(spawn_pos) % 4;
for _ in 0..npc_amount {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.mountaineer", &mut rng),
);
}
}
// vary next storey
length += -1;

View File

@ -0,0 +1,266 @@
use super::*;
use crate::{
util::{RandomField, Sampler, CARDINALS, DIAGONALS},
Land,
};
use common::terrain::{BlockKind, SpriteKind};
use rand::prelude::*;
use std::{f32::consts::TAU, sync::Arc};
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct SavannahHut {
/// Tile position of the door tile
pub door_tile: Vec2<i32>,
/// Axis aligned bounding region for the house
bounds: Aabr<i32>,
/// Approximate altitude of the door tile
pub(crate) alt: i32,
}
impl SavannahHut {
pub fn generate(
land: &Land,
_rng: &mut impl Rng,
site: &Site,
door_tile: Vec2<i32>,
door_dir: Vec2<i32>,
tile_aabr: Aabr<i32>,
) -> Self {
let door_tile_pos = site.tile_center_wpos(door_tile);
let bounds = Aabr {
min: site.tile_wpos(tile_aabr.min),
max: site.tile_wpos(tile_aabr.max),
};
Self {
bounds,
door_tile: door_tile_pos,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2,
}
}
}
impl Structure for SavannahHut {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_savannahhut\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_savannahhut")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.alt + 1;
let center = self.bounds.center();
let sprite_fill = Fill::Sampling(Arc::new(|wpos| {
Some(match (RandomField::new(0).get(wpos)) % 25 {
0 => Block::air(SpriteKind::Bowl),
1 => Block::air(SpriteKind::VialEmpty),
2 => Block::air(SpriteKind::Lantern),
3 => Block::air(SpriteKind::JugArabic),
4 => Block::air(SpriteKind::Crate),
_ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
})
}));
let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
let color = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 7 {
0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
_ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
})
}));
let length = (10 + RandomField::new(0).get(center.with_z(base)) % 6) as i32;
let height = 2 * length / 3;
let storeys = (1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32;
let radius = length + (length / 3);
let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
let reed_parts = 36_f32 + reed_var;
let phi = TAU / reed_parts;
// roof cone
painter
.cone(Aabb {
min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
max: (center + radius)
.with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
})
.fill(reed.clone());
painter
.cone(Aabb {
min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
max: (center + radius)
.with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
})
.clear();
// foundation
painter
.cylinder(Aabb {
min: (center - length).with_z(base - 3),
max: (center + length + 1).with_z(base - 2),
})
.fill(clay.clone());
painter
.cylinder(Aabb {
min: (center - length - 1).with_z(base - 4),
max: (center + length + 2).with_z(base - 3),
})
.fill(clay.clone());
painter
.cylinder(Aabb {
min: (center - length - 2).with_z(base - 5),
max: (center + length + 3).with_z(base - 4),
})
.fill(clay.clone());
painter
.cylinder(Aabb {
min: (center - length - 3).with_z(base - height),
max: (center + length + 4).with_z(base - 5),
})
.fill(clay.clone());
// room
for s in 0..storeys {
let room = painter.cylinder(Aabb {
min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
});
room.fill(clay.clone());
// decor inlays
for dir in DIAGONALS {
let decor_pos = center + dir * (length - 2 - s);
let decor = painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
decor_pos.with_z(base - 1 + (s * (height + 2))),
5.0,
)
.intersect(room);
decor.fill(color.clone());
painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
decor_pos.with_z(base - 1 + (s * (height + 2))),
4.0,
)
.intersect(decor)
.fill(clay.clone());
}
}
// clear rooms
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base - 2),
max: (center + 1 + length - 4).with_z(base + (storeys * height)),
})
.clear();
// wood decor
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base - 1),
max: (center + 1 + length - 4).with_z(base),
})
.fill(wood_dark.clone());
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base),
max: (center + 1 + length - 4).with_z(base + 1),
})
.fill(sprite_fill);
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base + (storeys * height) - 1),
max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
})
.fill(wood_dark);
for s in 0..storeys {
// entries, windows
for dir in CARDINALS {
let frame_pos = center + dir * (length - 2 - s);
let clear_pos = center + dir * (length + 2 - s);
painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
frame_pos.with_z(base - 1 + (s * (height + 2))),
3.0,
)
.fill(color.clone());
painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
clear_pos.with_z(base - 1 + (s * (height + 2))),
2.0,
)
.clear();
}
}
// re clear room
painter
.cylinder(Aabb {
min: (center - length + 5).with_z(base - 2),
max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
})
.clear();
// floor
painter
.cylinder(Aabb {
min: (center - (length / 2) - 1).with_z(base - 3),
max: (center + (length / 2) + 1).with_z(base - 2),
})
.fill(color);
painter
.cylinder(Aabb {
min: (center - (length / 2) + 1).with_z(base - 3),
max: (center + (length / 2) - 1).with_z(base - 2),
})
.fill(clay);
// furniture
let mut sprites = vec![
SpriteKind::DrawerSmall,
SpriteKind::ChairSingle,
SpriteKind::CoatRack,
SpriteKind::Bed,
SpriteKind::WardrobeSingle,
SpriteKind::CushionArabic,
SpriteKind::TableArabicSmall,
SpriteKind::DecorSetArabic,
SpriteKind::Bowl,
SpriteKind::VialEmpty,
SpriteKind::JugArabic,
SpriteKind::JugAndBowlArabic,
SpriteKind::SepareArabic,
];
let rows = if length > 12 { 2 } else { 1 };
'outer: for d in 0..rows {
for dir in DIAGONALS {
if sprites.is_empty() {
break 'outer;
}
let position = center + dir * (length - (9 + (d * 3)));
let sprite = sprites.swap_remove(
RandomField::new(0).get(position.with_z(base)) as usize % sprites.len(),
);
painter.sprite(position.with_z(base - 2), sprite);
}
}
// reed roof lines
for n in 1..=reed_parts as i32 {
let pos = Vec2::new(
center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
);
painter
.line(
pos.with_z(base + (storeys * height) - (height / 2)),
center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
1.0,
)
.fill(reed.clone());
}
}
}

View File

@ -284,6 +284,135 @@ impl Structure for SavannahPit {
max: (center + length).with_z(base - (2 * length)),
})
.clear();
// floor -1 donut room & floor -2 donut room
let rooms_clear = painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (2 * length)),
max: (center + (4 * length)).with_z(base - length - 1),
})
.union(painter.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (3 * length)),
max: (center + (4 * length)).with_z(base - (2 * length) - 1),
}));
let rooms = painter
.cylinder(Aabb {
min: (center - (2 * length) - 2).with_z(base - (2 * length)),
max: (center + (2 * length) + 2).with_z(base - length - 1),
})
.union(painter.cylinder(Aabb {
min: (center - length - 2).with_z(base - (3 * length)),
max: (center + length + 2).with_z(base - (2 * length) - 1),
}));
rooms_clear.without(rooms).clear();
// floor 0 wood ring
painter
.cylinder(Aabb {
min: (center - (3 * length)).with_z(base - length + (length / 2) - 3),
max: (center + (3 * length)).with_z(base - length + (length / 2) - 2),
})
.fill(wood_light.clone());
painter
.cylinder(Aabb {
min: (center - (3 * length) + 1).with_z(base - length + (length / 2) - 3),
max: (center + (3 * length) - 1).with_z(base - length + (length / 2) - 2),
})
.clear();
// floor -1 wood ring
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (2 * length) + (length / 2) - 3),
max: (center + (4 * length)).with_z(base - (2 * length) + (length / 2) - 2),
})
.fill(wood_light.clone());
painter
.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (2 * length) + (length / 2) - 3),
max: (center + (4 * length) - 1).with_z(base - (2 * length) + (length / 2) - 2),
})
.clear();
// floor -1 wood ring sprites
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (2 * length) + (length / 2) - 2),
max: (center + (4 * length)).with_z(base - (2 * length) + (length / 2) - 1),
})
.fill(sprite_fill.clone());
painter
.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (2 * length) + (length / 2) - 2),
max: (center + (4 * length) - 1).with_z(base - (2 * length) + (length / 2) - 1),
})
.clear();
// floor -1 center decor ring
painter
.cylinder(Aabb {
min: (center - (2 * length) - 2).with_z(base - (2 * length) + (length / 2) - 3),
max: (center + (2 * length) + 2).with_z(base - (2 * length) + (length / 2) - 1),
})
.fill(color.clone());
painter
.cylinder(Aabb {
min: (center - (2 * length)).with_z(base - (2 * length) + (length / 2) - 3),
max: (center + (2 * length)).with_z(base - (2 * length) + (length / 2) - 1),
})
.clear();
// floor -1 room entry
let room1_entry_pos = Vec2::new(center.x, center.y - (2 * length));
painter
.sphere(Aabb {
min: (room1_entry_pos - 5).with_z(base - (2 * length)),
max: (room1_entry_pos + 5).with_z(base - (2 * length) + 5),
})
.clear();
// floor -2 wood ring
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (3 * length) + (length / 2) - 3),
max: (center + (4 * length)).with_z(base - (3 * length) + (length / 2) - 2),
})
.fill(wood_light.clone());
painter
.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (3 * length) + (length / 2) - 3),
max: (center + (4 * length) - 1).with_z(base - (3 * length) + (length / 2) - 2),
})
.clear();
// floor -2 wood ring sprites
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (3 * length) + (length / 2) - 2),
max: (center + (4 * length)).with_z(base - (3 * length) + (length / 2) - 1),
})
.fill(sprite_fill.clone());
painter
.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (3 * length) + (length / 2) - 2),
max: (center + (4 * length) - 1).with_z(base - (3 * length) + (length / 2) - 1),
})
.clear();
// floor -2 center decor ring
painter
.cylinder(Aabb {
min: (center - length - 2).with_z(base - (3 * length) + (length / 2) - 3),
max: (center + length + 2).with_z(base - (3 * length) + (length / 2) - 1),
})
.fill(color);
painter
.cylinder(Aabb {
min: (center - length).with_z(base - (3 * length) + (length / 2) - 3),
max: (center + length).with_z(base - (3 * length) + (length / 2) - 1),
})
.clear();
// floor -2 room entry
let room2_entry_pos = Vec2::new(center.x, center.y - length);
painter
.sphere(Aabb {
min: (room2_entry_pos - 5).with_z(base - (3 * length)),
max: (room2_entry_pos + 5).with_z(base - (3 * length) + 5),
})
.clear();
// floor stairs lamps
for dir in SQUARE_4 {
@ -391,8 +520,8 @@ impl Structure for SavannahPit {
.fill(wood_dark.clone());
let stair_radius2 = ((2 * length) + 1) as f32;
let stairs_clear2 = painter.prim(Primitive::Cylinder(Aabb {
min: (center - (3 * length)).with_z(base - (3 * length)),
max: (center + (3 * length)).with_z(base - (2 * length)),
min: (center - length).with_z(base - (3 * length)),
max: (center + length).with_z(base - (2 * length)),
}));
painter
.prim(Primitive::sampling(
@ -405,101 +534,6 @@ impl Structure for SavannahPit {
),
))
.fill(wood_dark.clone());
// floor -1 donut room
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (2 * length)),
max: (center + (4 * length)).with_z(base - length - 1),
})
.without(painter.cylinder(Aabb {
min: (center - (2 * length) - 2).with_z(base - (2 * length)),
max: (center + (2 * length) + 2).with_z(base - length - 1),
}))
.clear();
// floor -1 room entry
let room1_entry_pos = Vec2::new(center.x, center.y - (2 * length));
painter
.sphere(Aabb {
min: (room1_entry_pos - 5).with_z(base - (2 * length)),
max: (room1_entry_pos + 5).with_z(base - (2 * length) + 5),
})
.clear();
// floor -2 donut room
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (3 * length)),
max: (center + (4 * length)).with_z(base - (2 * length) - 1),
})
.without(painter.cylinder(Aabb {
min: (center - length - 2).with_z(base - (3 * length)),
max: (center + length + 2).with_z(base - (2 * length) - 1),
}))
.clear();
// floor -2 room entry
let room2_entry_pos = Vec2::new(center.x, center.y - length);
painter
.sphere(Aabb {
min: (room2_entry_pos - 5).with_z(base - (3 * length)),
max: (room2_entry_pos + 5).with_z(base - (3 * length) + 5),
})
.clear();
// floor 0 wood ring
painter
.cylinder(Aabb {
min: (center - (3 * length)).with_z(base - length + (length / 2) - 3),
max: (center + (3 * length)).with_z(base - length + (length / 2) - 2),
})
.without(painter.cylinder(Aabb {
min: (center - (3 * length) + 1).with_z(base - length + (length / 2) - 3),
max: (center + (3 * length) - 1).with_z(base - length + (length / 2) - 2),
}))
.fill(wood_light.clone());
// floor -1 wood ring
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (2 * length) + (length / 2) - 3),
max: (center + (4 * length)).with_z(base - (2 * length) + (length / 2) - 2),
})
.without(painter.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (2 * length) + (length / 2) - 3),
max: (center + (4 * length) - 1).with_z(base - (2 * length) + (length / 2) - 2),
}))
.fill(wood_light.clone());
// floor -2 wood ring
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (3 * length) + (length / 2) - 3),
max: (center + (4 * length)).with_z(base - (3 * length) + (length / 2) - 2),
})
.without(painter.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (3 * length) + (length / 2) - 3),
max: (center + (4 * length) - 1).with_z(base - (3 * length) + (length / 2) - 2),
}))
.fill(wood_light.clone());
// floor -1 wood ring sprites
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (2 * length) + (length / 2) - 2),
max: (center + (4 * length)).with_z(base - (2 * length) + (length / 2) - 1),
})
.without(painter.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (2 * length) + (length / 2) - 2),
max: (center + (4 * length) - 1).with_z(base - (2 * length) + (length / 2) - 1),
}))
.fill(sprite_fill.clone());
// floor -2 wood ring sprites
painter
.cylinder(Aabb {
min: (center - (4 * length)).with_z(base - (3 * length) + (length / 2) - 2),
max: (center + (4 * length)).with_z(base - (3 * length) + (length / 2) - 1),
})
.without(painter.cylinder(Aabb {
min: (center - (4 * length) + 1).with_z(base - (3 * length) + (length / 2) - 2),
max: (center + (4 * length) - 1).with_z(base - (3 * length) + (length / 2) - 1),
}))
.fill(sprite_fill.clone());
// top wood cone
painter
.cone(Aabb {
@ -597,16 +631,6 @@ impl Structure for SavannahPit {
max: (entries_clear + 2).with_z(base - ((1 + f) * length) + 4),
})
.clear();
// villagers in each room
let spawn_pos = (room_center - 3).with_z(base - ((1 + f) * length));
let npc_amount = RandomField::new(0).get(spawn_pos) % 3;
for _ in 0..npc_amount {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.villager", &mut rng),
);
}
}
// outside platforms
painter
@ -642,16 +666,6 @@ impl Structure for SavannahPit {
})
.fill(wood_dark.clone());
}
// villagers on platforms
let spawn_pos = (room_center - 2).with_z(base + length + 2);
let npc_amount = RandomField::new(0).get(spawn_pos) % 3;
for _ in 0..npc_amount {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.villager", &mut rng),
);
}
let lantern_pos = center + dir * ((4 * length) - (length / 4) + 4);
painter.sprite(lantern_pos.with_z(base + length + 1), SpriteKind::Lantern);
}
@ -698,16 +712,6 @@ impl Structure for SavannahPit {
max: (entries_clear + 3).with_z(base - ((1 + f) * length) + 4),
})
.clear();
// villagers in each room
let spawn_pos = (room_center - 3).with_z(base - ((1 + f) * length));
let npc_amount = RandomField::new(0).get(spawn_pos) % 3;
for _ in 0..npc_amount {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.villager", &mut rng),
);
}
// furniture
match RandomField::new(0).get(room_center.with_z(base)) % 2 {
0 => {
@ -815,16 +819,6 @@ impl Structure for SavannahPit {
})
.fill(wood_dark.clone());
}
// villagers on platforms
let spawn_pos = (room_center - 2).with_z(base + length + 2);
let npc_amount = RandomField::new(0).get(spawn_pos) % 3;
for _ in 0..npc_amount {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.villager", &mut rng),
);
}
let lantern_pos = center + dir * ((3 * length) - (length / 3) + 4);
painter.sprite(lantern_pos.with_z(base + length + 1), SpriteKind::Lantern);
}
@ -944,18 +938,6 @@ impl Structure for SavannahPit {
}
}
}
// villagers outside on every floor
for a in 0..5 {
let spawn_pos = (center - 5).with_z(base + length - (length * a));
let npc_amount = RandomField::new(0).get(spawn_pos) % 4;
for _ in 0..npc_amount {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.villager", &mut rng),
);
}
}
// campfire
let campfire_pos = (center - 10).with_z(base);
@ -1059,13 +1041,6 @@ impl Structure for SavannahPit {
max: (crate_pos + 2).with_z(base - (5 * length) + 5),
})
.clear();
// villager and lantern in each stand
let spawn_pos = (crate_pos - 1).with_z(base - (5 * length) + 3);
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.villager", &mut rng),
);
painter.sprite(
(crate_pos + 1).with_z(base - (5 * length) + 3),
SpriteKind::Lantern,
@ -1098,16 +1073,6 @@ impl Structure for SavannahPit {
// placeholder for market items
.fill(Fill::Block(Block::air(SpriteKind::Crate)));
}
// villagers in market hall
let spawn_pos = (center + 10).with_z(base - (5 * length) + 4);
let npc_amount = RandomField::new(0).get(spawn_pos) % 12;
for _ in 0..npc_amount {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.villager", &mut rng),
);
}
// supports
for dir in CARDINALS {
let outer_support = center + dir * ((4 * length) - 2);
@ -1574,16 +1539,6 @@ impl Structure for SavannahPit {
})
.fill(wood_dark.clone());
}
// guards and lanterns on towers
let spawn_pos = (tower_center - 2).with_z(base + (length / 2) + 2);
let npc_amount = 1 + RandomField::new(0).get(spawn_pos) % 3;
for _ in 1..(npc_amount + 1) {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.guard", &mut rng),
);
}
let lantern_pos = tower_center - 3;
painter.sprite(lantern_pos.with_z(base + (length / 2)), SpriteKind::Lantern);
// tunnel lanterns
@ -1643,16 +1598,6 @@ impl Structure for SavannahPit {
})
.fill(wood_dark.clone());
}
// guards and lanterns on towers
let spawn_pos = (tower_center - 2).with_z(base + (length / 2) + 2);
let npc_amount = 1 + RandomField::new(0).get(spawn_pos) % 3;
for _ in 1..(npc_amount + 1) {
let mut rng = thread_rng();
painter.spawn(
EntityInfo::at(spawn_pos.map(|e| e as f32))
.with_asset_expect("common.entity.village.guard", &mut rng),
);
}
let lantern_pos = center + dir * (7 * wall_length);
painter.sprite(lantern_pos.with_z(base + (length / 2)), SpriteKind::Lantern);
// tunnel lanterns

View File

@ -0,0 +1,308 @@
use super::*;
use crate::{
util::{RandomField, Sampler, CARDINALS, DIAGONALS},
Land,
};
use common::terrain::{BlockKind, SpriteKind};
use rand::prelude::*;
use std::{f32::consts::TAU, sync::Arc};
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct SavannahWorkshop {
/// Tile position of the door tile
pub door_tile: Vec2<i32>,
/// Axis aligned bounding region for the house
bounds: Aabr<i32>,
/// Approximate altitude of the door tile
pub(crate) alt: i32,
}
impl SavannahWorkshop {
pub fn generate(
land: &Land,
_rng: &mut impl Rng,
site: &Site,
door_tile: Vec2<i32>,
door_dir: Vec2<i32>,
tile_aabr: Aabr<i32>,
) -> Self {
let door_tile_pos = site.tile_center_wpos(door_tile);
let bounds = Aabr {
min: site.tile_wpos(tile_aabr.min),
max: site.tile_wpos(tile_aabr.max),
};
Self {
bounds,
door_tile: door_tile_pos,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2,
}
}
}
impl Structure for SavannahWorkshop {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_savannahworkshop\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_savannahworkshop")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.alt + 1;
let center = self.bounds.center();
let sprite_fill = Fill::Sampling(Arc::new(|wpos| {
Some(match (RandomField::new(0).get(wpos)) % 25 {
0 => Block::air(SpriteKind::Bowl),
1 => Block::air(SpriteKind::VialEmpty),
2 => Block::air(SpriteKind::Lantern),
3 => Block::air(SpriteKind::JugArabic),
4 => Block::air(SpriteKind::Crate),
_ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
})
}));
let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
let color = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 7 {
0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
_ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
})
}));
let length = (10 + RandomField::new(0).get(center.with_z(base)) % 6) as i32;
let height = 2 * length / 3;
let storeys = (1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32;
let radius = length + (length / 3);
let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
let reed_parts = 36_f32 + reed_var;
let phi = TAU / reed_parts;
// roof cone
painter
.cone(Aabb {
min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
max: (center + radius)
.with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
})
.fill(reed.clone());
painter
.cone(Aabb {
min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
max: (center + radius)
.with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
})
.clear();
// foundation
painter
.cylinder(Aabb {
min: (center - length).with_z(base - 3),
max: (center + length + 1).with_z(base - 2),
})
.fill(clay.clone());
painter
.cylinder(Aabb {
min: (center - length - 1).with_z(base - 4),
max: (center + length + 2).with_z(base - 3),
})
.fill(clay.clone());
painter
.cylinder(Aabb {
min: (center - length - 2).with_z(base - 5),
max: (center + length + 3).with_z(base - 4),
})
.fill(clay.clone());
painter
.cylinder(Aabb {
min: (center - length - 3).with_z(base - height),
max: (center + length + 4).with_z(base - 5),
})
.fill(clay.clone());
// room
for s in 0..storeys {
let room = painter.cylinder(Aabb {
min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
});
room.fill(clay.clone());
// decor inlays
for dir in DIAGONALS {
let decor_pos = center + dir * (length - 2 - s);
let decor = painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
decor_pos.with_z(base - 1 + (s * (height + 2))),
5.0,
)
.intersect(room);
decor.fill(color.clone());
painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
decor_pos.with_z(base - 1 + (s * (height + 2))),
4.0,
)
.intersect(decor)
.fill(clay.clone());
}
}
// clear rooms
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base - 2),
max: (center + 1 + length - 4).with_z(base + (storeys * height)),
})
.clear();
// wood decor
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base - 1),
max: (center + 1 + length - 4).with_z(base),
})
.fill(wood_dark.clone());
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base),
max: (center + 1 + length - 4).with_z(base + 1),
})
.fill(sprite_fill);
painter
.cylinder(Aabb {
min: (center - length + 4).with_z(base + (storeys * height) - 1),
max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
})
.fill(wood_dark.clone());
for s in 0..storeys {
// entries, windows
for dir in CARDINALS {
let frame_pos = center + dir * (length - 2 - s);
let clear_pos = center + dir * (length + 2 - s);
painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
frame_pos.with_z(base - 1 + (s * (height + 2))),
3.0,
)
.fill(color.clone());
painter
.line(
center.with_z(base - 1 + (s * (height + 2))),
clear_pos.with_z(base - 1 + (s * (height + 2))),
2.0,
)
.clear();
}
}
// re clear room
painter
.cylinder(Aabb {
min: (center - length + 5).with_z(base - 2),
max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
})
.clear();
// floor
painter
.cylinder(Aabb {
min: (center - (length / 2) - 1).with_z(base - 3),
max: (center + (length / 2) + 1).with_z(base - 2),
})
.fill(color);
painter
.cylinder(Aabb {
min: (center - (length / 2) + 1).with_z(base - 3),
max: (center + (length / 2) - 1).with_z(base - 2),
})
.fill(clay.clone());
// reed roof lines
for n in 1..=reed_parts as i32 {
let pos = Vec2::new(
center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
);
painter
.line(
pos.with_z(base + (storeys * height) - (height / 2)),
center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
1.0,
)
.fill(reed.clone());
}
// chimney
painter
.cylinder(Aabb {
min: (center - 3)
.with_z(base - 1 + (storeys * height) + (height / 2) + reed_var as i32),
max: (center + 4)
.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
})
.fill(wood_dark);
// clear chimney
painter
.cylinder(Aabb {
min: (center - 2).with_z(base + (storeys * height) - 4),
max: (center + 3)
.with_z(base + 5 * (storeys * height) + (height / 2) + reed_var as i32),
})
.clear();
painter
.cylinder(Aabb {
min: (center - 1).with_z(base - 2),
max: (center + 2).with_z(base - 1),
})
.fill(clay);
painter
.aabb(Aabb {
min: (center).with_z(base - 2),
max: (center + 1).with_z(base - 1),
})
.clear();
painter
.aabb(Aabb {
min: (center).with_z(base - 3),
max: (center + 1).with_z(base - 2),
})
.fill(Fill::Block(Block::air(SpriteKind::Ember)));
let mut stations = vec![
SpriteKind::CraftingBench,
SpriteKind::Forge,
SpriteKind::SpinningWheel,
SpriteKind::TanningRack,
SpriteKind::CookingPot,
SpriteKind::Cauldron,
SpriteKind::Loom,
SpriteKind::Anvil,
SpriteKind::DismantlingBench,
SpriteKind::RepairBench,
];
let cr_pos = stations.len() as f32;
let phi = TAU / cr_pos;
'outer: for d in 0..2 {
let dist = 4 + d;
for n in 1..=cr_pos as i32 {
let pos = Vec2::new(
center.x + ((dist as f32) * ((n as f32 * phi).cos())) as i32,
center.y + ((dist as f32) * ((n as f32 * phi).sin())) as i32,
);
if stations.is_empty() {
break 'outer;
}
let cr_station = stations.swap_remove(
RandomField::new(0).get(pos.with_z(base)) as usize % stations.len(),
);
painter.sprite(pos.with_z(base - 2), cr_station);
}
}
painter.spawn(
EntityInfo::at((center).with_z(base - 2).map(|e| e as f32 + 0.5)).into_waypoint(),
);
}
}