Merge branch 'floppy_coastal_town' into 'master'

coastal_town

See merge request veloren/veloren!3887
This commit is contained in:
Marcel 2023-07-12 23:02:40 +00:00
commit e48ef26952
17 changed files with 1161 additions and 243 deletions

View File

@ -22,4 +22,5 @@ pub enum SettlementKindMeta {
CliffTown,
DesertCity,
SavannahPit,
CoastalTown,
}

View File

@ -85,6 +85,7 @@ impl Data {
SiteKind::Refactor(site2)
| SiteKind::CliffTown(site2)
| SiteKind::SavannahPit(site2)
| SiteKind::CoastalTown(site2)
| SiteKind::DesertCity(site2) => Some(site2),
_ => None,
})?)))
@ -122,6 +123,8 @@ impl Data {
| PlotKind::CliffTower(_)
| PlotKind::DesertCityMultiPlot(_)
| PlotKind::DesertCityTemple(_)
| PlotKind::CoastalHouse(_)
| PlotKind::CoastalWorkshop(_)
)
}) as _;
let matches_plazas = (|kind: &PlotKind| matches!(kind, PlotKind::Plaza)) as _;

View File

@ -25,7 +25,8 @@ impl Site {
SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::DesertCity(_)
| SiteKind::SavannahPit(_) => Some(true),
| SiteKind::SavannahPit(_)
| SiteKind::CoastalTown(_) => Some(true),
// Evil
SiteKind::Dungeon(_)
| SiteKind::ChapelSite(_)

View File

@ -74,6 +74,7 @@ impl Rule for Migrate {
site.world_site.map_or(false, |ws| matches!(&ctx.index.sites.get(ws).kind, SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::SavannahPit(_)
| SiteKind::CoastalTown(_)
| SiteKind::DesertCity(_)))
})
.min_by_key(|(_, site)| site.wpos.as_().distance_squared(npc.wpos.xy()) as i32)

View File

@ -673,6 +673,7 @@ fn adventure() -> impl Action<DefaultState> {
SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::SavannahPit(_)
| SiteKind::CoastalTown(_)
| SiteKind::DesertCity(_)
),
) && ctx.npc.current_site.map_or(true, |cs| *site_id != cs)
@ -776,6 +777,7 @@ fn villager(visiting_site: SiteId) -> impl Action<DefaultState> {
Some(SiteKind::Refactor(site2)
| SiteKind::CliffTown(site2)
| SiteKind::SavannahPit(site2)
| SiteKind::CoastalTown(site2)
| SiteKind::DesertCity(site2)) => site2,
_ => return None,
};

View File

@ -79,6 +79,7 @@ fn on_death(ctx: EventCtx<SimulateNpcs, OnDeath>) {
SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::SavannahPit(_)
| SiteKind::CoastalTown(_)
| SiteKind::DesertCity(_)
)
})

View File

@ -119,6 +119,9 @@ impl ActivityUpdate {
Settlement(CliffTown) => format!("Climbing the towers of {chunk_name}"),
Settlement(DesertCity) => format!("Hiding from the sun in {chunk_name}"),
Settlement(SavannahPit) => format!("Shop at the market down in {chunk_name}"),
Settlement(CoastalTown) => {
format!("Dip your feet in the water in {chunk_name}")
},
_ => format!("In {chunk_name}"),
};

View File

@ -373,6 +373,7 @@ impl Civs {
SiteKind::Refactor => (32i32, 10.0),
SiteKind::CliffTown => (64i32, 25.0),
SiteKind::SavannahPit => (48i32, 25.0),
SiteKind::CoastalTown => (64i32, 35.0),
SiteKind::DesertCity => (64i32, 25.0),
SiteKind::ChapelSite => (36i32, 10.0),
SiteKind::Tree => (12i32, 8.0),
@ -482,6 +483,13 @@ impl Civs {
wpos,
))
},
SiteKind::CoastalTown => {
WorldSite::coastal_town(site2::Site::generate_coastal_town(
&Land::from_sim(ctx.sim),
&mut rng,
wpos,
))
},
SiteKind::DesertCity => WorldSite::desert_city(
site2::Site::generate_desert_city(&Land::from_sim(ctx.sim), &mut rng, wpos),
),
@ -809,6 +817,7 @@ impl Civs {
0..=10 => SiteKind::CliffTown,
11..=12 => SiteKind::DesertCity,
13..=18 => SiteKind::SavannahPit,
19..=36 => SiteKind::CoastalTown,
_ => SiteKind::Refactor,
};
let world_dims = ctx.sim.get_aabr();
@ -1256,6 +1265,7 @@ impl Civs {
| SiteKind::Settlement
| SiteKind::CliffTown
| SiteKind::SavannahPit
| SiteKind::CoastalTown
| SiteKind::DesertCity
| SiteKind::Castle
)
@ -1269,6 +1279,7 @@ impl Civs {
| SiteKind::Settlement
| SiteKind::CliffTown
| SiteKind::SavannahPit
| SiteKind::CoastalTown
| SiteKind::DesertCity
| SiteKind::Castle = self.sites[site].kind
{
@ -1755,6 +1766,7 @@ pub enum SiteKind {
Refactor,
CliffTown,
SavannahPit,
CoastalTown,
DesertCity,
ChapelSite,
Tree,
@ -1829,6 +1841,10 @@ impl SiteKind {
&& !chunk.river.near_water()
&& suitable_for_town()
},
SiteKind::CoastalTown => {
(2.0..3.5).contains(&(chunk.water_alt - CONFIG.sea_level))
&& suitable_for_town()
},
SiteKind::DesertCity => {
(0.9..1.0).contains(&chunk.temp) && !chunk.near_cliffs() && suitable_for_town()
},
@ -1903,6 +1919,7 @@ impl Site {
| SiteKind::CliffTown
| SiteKind::DesertCity
| SiteKind::SavannahPit
| SiteKind::CoastalTown
)
}

View File

@ -163,7 +163,7 @@ impl World {
name: site.site_tmp.map(|id| index.sites[id].name().to_string()),
// TODO: Probably unify these, at some point
kind: match &site.kind {
civ::SiteKind::Settlement | civ::SiteKind::Refactor | civ::SiteKind::CliffTown | civ::SiteKind::SavannahPit | civ::SiteKind::DesertCity => world_msg::SiteKind::Town,
civ::SiteKind::Settlement | civ::SiteKind::Refactor | civ::SiteKind::CliffTown | civ::SiteKind::SavannahPit | civ::SiteKind::CoastalTown | civ::SiteKind::DesertCity => world_msg::SiteKind::Town,
civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon {
difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) {
Some(SiteKind::Dungeon(d)) => d.dungeon_difficulty().unwrap_or(0),

View File

@ -123,6 +123,7 @@ impl Environment {
| SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::SavannahPit(_)
| SiteKind::CoastalTown(_)
| SiteKind::DesertCity(_) => towns += site.economy.pop,
SiteKind::Dungeon(_) => dungeons += site.economy.pop,
SiteKind::Castle(_) => castles += site.economy.pop,

View File

@ -72,6 +72,7 @@ pub enum SiteKind {
DesertCity(site2::Site),
ChapelSite(site2::Site),
DwarvenMine(site2::Site),
CoastalTown(site2::Site),
GiantTree(site2::Site),
Gnarling(site2::Site),
Bridge(site2::Site),
@ -135,6 +136,13 @@ impl Site {
}
}
pub fn coastal_town(ct: site2::Site) -> Self {
Self {
kind: SiteKind::CoastalTown(ct),
economy: Economy::default(),
}
}
pub fn desert_city(dc: site2::Site) -> Self {
Self {
kind: SiteKind::DesertCity(dc),
@ -185,6 +193,7 @@ impl Site {
SiteKind::Refactor(s) => s.radius(),
SiteKind::CliffTown(ct) => ct.radius(),
SiteKind::SavannahPit(sp) => sp.radius(),
SiteKind::CoastalTown(ct) => ct.radius(),
SiteKind::DesertCity(dc) => dc.radius(),
SiteKind::ChapelSite(p) => p.radius(),
SiteKind::DwarvenMine(p) => p.radius(),
@ -204,6 +213,7 @@ impl Site {
SiteKind::Refactor(s) => s.origin,
SiteKind::CliffTown(ct) => ct.origin,
SiteKind::SavannahPit(sp) => sp.origin,
SiteKind::CoastalTown(ct) => ct.origin,
SiteKind::DesertCity(dc) => dc.origin,
SiteKind::ChapelSite(p) => p.origin,
SiteKind::DwarvenMine(p) => p.origin,
@ -223,6 +233,7 @@ impl Site {
SiteKind::Refactor(s) => s.spawn_rules(wpos),
SiteKind::CliffTown(ct) => ct.spawn_rules(wpos),
SiteKind::SavannahPit(sp) => sp.spawn_rules(wpos),
SiteKind::CoastalTown(ct) => ct.spawn_rules(wpos),
SiteKind::DesertCity(dc) => dc.spawn_rules(wpos),
SiteKind::ChapelSite(p) => p.spawn_rules(wpos),
SiteKind::DwarvenMine(p) => p.spawn_rules(wpos),
@ -242,6 +253,7 @@ impl Site {
SiteKind::Refactor(s) => s.name(),
SiteKind::CliffTown(ct) => ct.name(),
SiteKind::SavannahPit(sp) => sp.name(),
SiteKind::CoastalTown(ct) => ct.name(),
SiteKind::DesertCity(dc) => dc.name(),
SiteKind::ChapelSite(p) => p.name(),
SiteKind::DwarvenMine(p) => p.name(),
@ -262,6 +274,7 @@ impl Site {
| SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::SavannahPit(_)
| SiteKind::CoastalTown(_)
| SiteKind::DesertCity(_) => Some(common::trade::SiteInformation {
id: site_id,
unconsumed_stock: self.economy.get_available_stock(),
@ -280,6 +293,7 @@ impl Site {
SiteKind::Refactor(s) => s.render(canvas, dynamic_rng),
SiteKind::CliffTown(ct) => ct.render(canvas, dynamic_rng),
SiteKind::SavannahPit(sp) => sp.render(canvas, dynamic_rng),
SiteKind::CoastalTown(ct) => ct.render(canvas, dynamic_rng),
SiteKind::DesertCity(dc) => dc.render(canvas, dynamic_rng),
SiteKind::ChapelSite(p) => p.render(canvas, dynamic_rng),
SiteKind::DwarvenMine(p) => p.render(canvas, dynamic_rng),
@ -312,6 +326,7 @@ impl Site {
SiteKind::Refactor(_) => {},
SiteKind::CliffTown(_) => {},
SiteKind::SavannahPit(_) => {},
SiteKind::CoastalTown(_) => {},
SiteKind::DesertCity(_) => {},
SiteKind::ChapelSite(p) => p.apply_supplement(dynamic_rng, wpos2d, supplement),
SiteKind::DwarvenMine(p) => p.apply_supplement(dynamic_rng, wpos2d, supplement),
@ -329,6 +344,7 @@ impl Site {
SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::SavannahPit(_)
| SiteKind::CoastalTown(_)
| SiteKind::DesertCity(_)
| SiteKind::Settlement(_)
)
@ -344,6 +360,7 @@ impl Site {
SiteKind::Refactor(site2) => Some(site2),
SiteKind::CliffTown(site2) => Some(site2),
SiteKind::SavannahPit(site2) => Some(site2),
SiteKind::CoastalTown(site2) => Some(site2),
SiteKind::Tree(_) => None,
SiteKind::DesertCity(site2) => Some(site2),
SiteKind::ChapelSite(site2) => Some(site2),
@ -366,6 +383,9 @@ impl SiteKind {
SiteKind::SavannahPit(_) => {
Some(SiteKindMeta::Settlement(SettlementKindMeta::SavannahPit))
},
SiteKind::CoastalTown(_) => {
Some(SiteKindMeta::Settlement(SettlementKindMeta::CoastalTown))
},
SiteKind::DesertCity(_) => {
Some(SiteKindMeta::Settlement(SettlementKindMeta::DesertCity))
},

View File

@ -1039,6 +1039,98 @@ impl Site {
site
}
pub fn generate_coastal_town(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
let mut rng = reseed(rng);
let mut site = Site {
origin,
name: NameGen::location(&mut rng).generate_danari(),
..Site::default()
};
site.demarcate_obstacles(land);
site.make_plaza(land, &mut rng);
let build_chance = Lottery::from(vec![(38.0, 1), (7.0, 2)]);
for _ in 0..45 {
match *build_chance.choose_seeded(rng.gen()) {
1 => {
// CoastalHouse
let size = (7.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,
7..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let coastal_house = plot::CoastalHouse::generate(
land,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let coastal_house_alt = coastal_house.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::CoastalHouse(coastal_house),
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(coastal_house_alt),
})
} else {
site.make_plaza(land, &mut rng);
}
},
2 => {
// CoastalWorkshop
let size = (7.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,
7..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let coastal_workshop = plot::CoastalWorkshop::generate(
land,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let coastal_workshop_alt = coastal_workshop.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::CoastalWorkshop(coastal_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(coastal_workshop_alt),
})
} else {
site.make_plaza(land, &mut rng);
}
},
_ => {},
}
}
site
}
pub fn generate_desert_city(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
let mut rng = reseed(rng);
@ -1494,6 +1586,10 @@ impl Site {
for plot in plots_to_render {
let (prim_tree, fills, mut entities) = match &self.plots[plot].kind {
PlotKind::House(house) => house.render_collect(self, canvas),
PlotKind::CoastalHouse(coastal_house) => coastal_house.render_collect(self, canvas),
PlotKind::CoastalWorkshop(coastal_workshop) => {
coastal_workshop.render_collect(self, canvas)
},
PlotKind::Workshop(workshop) => workshop.render_collect(self, canvas),
PlotKind::Castle(castle) => castle.render_collect(self, canvas),
PlotKind::SeaChapel(sea_chapel) => sea_chapel.render_collect(self, canvas),

View File

@ -3,6 +3,8 @@ mod bridge;
mod castle;
mod citadel;
mod cliff_tower;
mod coastal_house;
mod coastal_workshop;
mod desert_city_multiplot;
mod desert_city_temple;
pub mod dungeon;
@ -18,11 +20,12 @@ mod workshop;
pub use self::{
adlet::AdletStronghold, bridge::Bridge, castle::Castle, citadel::Citadel,
cliff_tower::CliffTower, desert_city_multiplot::DesertCityMultiPlot,
desert_city_temple::DesertCityTemple, dungeon::Dungeon, dwarven_mine::DwarvenMine,
giant_tree::GiantTree, gnarling::GnarlingFortification, house::House,
savannah_hut::SavannahHut, savannah_pit::SavannahPit, savannah_workshop::SavannahWorkshop,
sea_chapel::SeaChapel, workshop::Workshop,
cliff_tower::CliffTower, coastal_house::CoastalHouse, coastal_workshop::CoastalWorkshop,
desert_city_multiplot::DesertCityMultiPlot, desert_city_temple::DesertCityTemple,
dungeon::Dungeon, dwarven_mine::DwarvenMine, giant_tree::GiantTree,
gnarling::GnarlingFortification, house::House, savannah_hut::SavannahHut,
savannah_pit::SavannahPit, savannah_workshop::SavannahWorkshop, sea_chapel::SeaChapel,
workshop::Workshop,
};
use super::*;
@ -64,6 +67,8 @@ impl Plot {
pub enum PlotKind {
House(House),
CoastalHouse(CoastalHouse),
CoastalWorkshop(CoastalWorkshop),
Workshop(Workshop),
DesertCityMultiPlot(DesertCityMultiPlot),
DesertCityTemple(DesertCityTemple),

View File

@ -0,0 +1,465 @@
use super::*;
use crate::{
site2::plot::dungeon::wall_staircase,
util::{RandomField, Sampler, CARDINALS, DIAGONALS, NEIGHBORS},
Land,
};
use common::terrain::{BlockKind, SpriteKind};
use rand::prelude::*;
use std::sync::Arc;
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct CoastalHouse {
/// 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 CoastalHouse {
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 {
door_tile: door_tile_pos,
bounds,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2,
}
}
}
impl Structure for CoastalHouse {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_coastalhouse\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_coastalhouse")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.alt + 1;
let center = self.bounds.center();
let white = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 37 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
_ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
})
}));
let blue_broken = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 20 {
0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
_ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
})
}));
let length = (14 + RandomField::new(0).get(center.with_z(base)) % 3) as i32;
let width = (12 + RandomField::new(0).get((center - 1).with_z(base)) % 3) as i32;
let height = (12 + RandomField::new(0).get((center + 1).with_z(base)) % 4) as i32;
let storeys = (1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32;
// fence, blue gates
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 1),
max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base),
})
.fill(blue_broken.clone());
for dir in CARDINALS {
let frame_pos = Vec2::new(
center.x + dir.x * (length + 5),
center.y + dir.y * (width + 5),
);
painter
.line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
.fill(blue_broken.clone());
}
// foundation
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
})
.fill(white.clone());
for f in 0..8 {
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
.with_z(base - 3 - f),
max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
.with_z(base - 2 - f),
})
.fill(white.clone());
}
// clear yard
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
})
.clear();
// clear entries
for dir in CARDINALS {
let clear_pos = Vec2::new(
center.x + dir.x * (length + 7),
center.y + dir.y * (width + 7),
);
painter
.line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
.clear();
}
for s in 0..storeys {
// roof terrace
painter
.aabb(Aabb {
min: Vec2::new(
center.x - length - 3 + (2 * s),
center.y - width - 3 + (2 * s),
)
.with_z(base - 3 + height + (s * height)),
max: Vec2::new(
center.x + length + 2 - (2 * s),
center.y + width + 2 - (2 * s),
)
.with_z(base - 2 + height + (s * height)),
})
.fill(white.clone());
painter
.aabb(Aabb {
min: Vec2::new(
center.x - length - 3 + (2 * s),
center.y - width - 3 + (2 * s),
)
.with_z(base - 2 + height + (s * height)),
max: Vec2::new(
center.x + length + 2 - (2 * s),
center.y + width + 2 - (2 * s),
)
.with_z(base - 1 + height + (s * height)),
})
.fill(blue_broken.clone());
painter
.aabb(Aabb {
min: Vec2::new(
center.x - length - 2 + (2 * s),
center.y - width - 2 + (2 * s),
)
.with_z(base - 2 + height + (s * height)),
max: Vec2::new(
center.x + length + 1 - (2 * s),
center.y + width + 1 - (2 * s),
)
.with_z(base - 1 + height + (s * height)),
})
.clear();
// room
painter
.aabb(Aabb {
min: Vec2::new(center.x - length + (2 * s), center.y - width + (2 * s))
.with_z(base - 2 + (s * height)),
max: Vec2::new(center.x + length - (2 * s), center.y + width - (2 * s))
.with_z(base - 1 + (s * height)),
})
.fill(blue_broken.clone());
painter
.aabb(Aabb {
min: Vec2::new(
center.x - length + 1 + (2 * s),
center.y - width + 1 + (2 * s),
)
.with_z(base - 2 + (s * height)),
max: Vec2::new(
center.x + length - 1 - (2 * s),
center.y + width - 1 - (2 * s),
)
.with_z(base - 1 + height - 1 + (s * height)),
})
.fill(white.clone());
// entries
painter
.line(
Vec2::new(center.x, center.y + 1 - width + (2 * s))
.with_z(base - 1 + (s * height)),
Vec2::new(center.x, center.y - 2 + width - (2 * s))
.with_z(base - 1 + (s * height)),
3.0,
)
.fill(blue_broken.clone());
painter
.line(
Vec2::new(center.x, center.y - width + (2 * s)).with_z(base - 1 + (s * height)),
Vec2::new(center.x, center.y + width - (2 * s)).with_z(base - 1 + (s * height)),
2.0,
)
.clear();
painter
.line(
Vec2::new(center.x + 1 - length + (2 * s), center.y)
.with_z(base - 1 + (s * height)),
Vec2::new(center.x - 2 + length - (2 * s), center.y)
.with_z(base - 1 + (s * height)),
3.0,
)
.fill(blue_broken.clone());
painter
.line(
Vec2::new(center.x - length + (2 * s), center.y)
.with_z(base - 1 + (s * height)),
Vec2::new(center.x + length - (2 * s), center.y)
.with_z(base - 1 + (s * height)),
2.0,
)
.clear();
// windows length
painter
.line(
Vec2::new(center.x - (length / 3), center.y + 1 - width + (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
Vec2::new(center.x - (length / 3), center.y - 2 + width - (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
3.0,
)
.fill(blue_broken.clone());
painter
.line(
Vec2::new(center.x - (length / 3), center.y - width - 2 + (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
Vec2::new(center.x - (length / 3), center.y + width + 2 - (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
2.0,
)
.clear();
painter
.line(
Vec2::new(center.x + (length / 3), center.y + 1 - width + (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
Vec2::new(center.x + (length / 3), center.y - 2 + width - (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
3.0,
)
.fill(blue_broken.clone());
painter
.line(
Vec2::new(center.x + (length / 3), center.y - width - 2 + (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
Vec2::new(center.x + (length / 3), center.y + width + 2 - (2 * s))
.with_z(base - 1 + (s * height) + (height / 2)),
2.0,
)
.clear();
// windows width
painter
.line(
Vec2::new(center.x + 1 - length + (2 * s), center.y)
.with_z(base - 1 + (s * height) + (height / 2)),
Vec2::new(center.x - 2 + length - (2 * s), center.y)
.with_z(base - 1 + (s * height) + (height / 2)),
3.0,
)
.fill(blue_broken.clone());
painter
.line(
Vec2::new(center.x - length - 2 + (2 * s), center.y)
.with_z(base - 1 + (s * height) + (height / 2)),
Vec2::new(center.x + length + 2 - (2 * s), center.y)
.with_z(base - 1 + (s * height) + (height / 2)),
2.0,
)
.clear();
// clear room
painter
.aabb(Aabb {
min: Vec2::new(
center.x - length + 2 + (2 * s),
center.y - width + 2 + (2 * s),
)
.with_z(base - 2 + (s * height)),
max: Vec2::new(
center.x + length - 2 - (2 * s),
center.y + width - 2 - (2 * s),
)
.with_z(base - 2 + height - 1 + (s * height)),
})
.clear();
// room floors
painter
.aabb(Aabb {
min: Vec2::new(
center.x - length + 5 + (2 * s),
center.y - width + 5 + (2 * s),
)
.with_z(base - 3 + (s * height)),
max: Vec2::new(
center.x + length - 5 - (2 * s),
center.y + width - 5 - (2 * s),
)
.with_z(base - 2 + (s * height)),
})
.fill(blue_broken.clone());
painter
.aabb(Aabb {
min: Vec2::new(
center.x - length + 6 + (2 * s),
center.y - width + 6 + (2 * s),
)
.with_z(base - 3 + (s * height)),
max: Vec2::new(
center.x + length - 6 - (2 * s),
center.y + width - 6 - (2 * s),
)
.with_z(base - 2 + (s * height)),
})
.fill(white.clone());
// furniture
let mut sprites = vec![
SpriteKind::DrawerSmall,
SpriteKind::DrawerMedium,
SpriteKind::DrawerMedium,
SpriteKind::ChairSingle,
SpriteKind::ChairDouble,
SpriteKind::CoatRack,
SpriteKind::Bed,
SpriteKind::WardrobeDouble,
SpriteKind::WardrobeSingle,
SpriteKind::TableSide,
SpriteKind::Bowl,
SpriteKind::VialEmpty,
SpriteKind::SepareArabic,
SpriteKind::FountainArabic,
SpriteKind::JugAndBowlArabic,
SpriteKind::Crate,
SpriteKind::Pot,
SpriteKind::Lantern,
SpriteKind::TableArabicSmall,
SpriteKind::CushionArabic,
SpriteKind::JugArabic,
SpriteKind::DecorSetArabic,
];
'outer: for dir in DIAGONALS {
let furniture_pos = Vec2::new(
center.x + dir.x * ((length / 2) - 2),
center.y + dir.y * ((width / 2) - 2),
);
for dir in NEIGHBORS {
if sprites.is_empty() {
break 'outer;
}
let position = furniture_pos + dir * 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 + (s * height)), sprite);
}
}
// clear floor center if stairs
if storeys > 1 {
painter
.cylinder(Aabb {
min: (center - 6).with_z(base - 2 + (s * height)),
max: (center + 6).with_z(base + (s * height)),
})
.clear();
};
// wall lamps
for d in 0..2 {
let door_lamp_pos = Vec2::new(
center.x - length + 2 + (2 * s) + (d * ((2 * (length - (2 * s))) - 5)),
center.y,
)
.with_z(base + 1 + (s * height));
painter.rotated_sprite(
door_lamp_pos,
SpriteKind::WallLampSmall,
2 + ((d * 4) as u8),
);
let lamp_pos = Vec2::new(
center.x,
center.y - width + 2 + (2 * s) + (d * ((2 * (width - (2 * s))) - 5)),
)
.with_z(base + 1 + (s * height));
painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 4 - ((d * 4) as u8));
}
for d in 0..2 {
let door_lamp_pos = Vec2::new(
center.x - length - 1 + (2 * s) + (d * ((2 * (length - (2 * s))) + 1)),
center.y,
)
.with_z(base + 1 + (s * height));
painter.rotated_sprite(
door_lamp_pos,
SpriteKind::WallLampSmall,
6 + ((d * 4) as u8),
);
let lamp_pos = Vec2::new(
center.x,
center.y - width - 1 + (2 * s) + (d * ((2 * (width - (2 * s))) + 1)),
)
.with_z(base + 1 + (s * height));
painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 8 - ((d * 4) as u8));
}
}
let top_limit = painter.aabb(Aabb {
min: Vec2::new(center.x - length, center.y - width)
.with_z(base + (storeys * height) - 2),
max: Vec2::new(center.x + length, center.y + width)
.with_z(base - 2 + (storeys * height) + (height / 2)),
});
painter
.superquadric(
Aabb {
min: Vec2::new(center.x - length - 1, center.y - width - 1)
.with_z(base + (storeys * height) - (height / 2)),
max: Vec2::new(center.x + length, center.y + width)
.with_z(base - 2 + (storeys * height) + (height / 2)),
},
1.5,
)
.intersect(top_limit)
.fill(white.clone());
// stairway1 stairs
let stair_radius1 = 3.0;
let stairs_clear1 = painter.cylinder(Aabb {
min: (center - 1 - stair_radius1 as i32).with_z(base - 2),
max: (center + 2 + stair_radius1 as i32).with_z(base + ((storeys - 1) * height) - 2),
});
let stairs_clear2 = painter.cylinder(Aabb {
min: (center - 2 - stair_radius1 as i32).with_z(base - 2),
max: (center + 3 + stair_radius1 as i32).with_z(base + ((storeys - 1) * height) - 2),
});
stairs_clear1.clear();
painter
.cylinder(Aabb {
min: (center - 1).with_z(base - 2),
max: (center + 2).with_z(base + ((storeys - 1) * height) - 2),
})
.fill(white.clone());
stairs_clear2
.sample(wall_staircase(
center.with_z(base + ((storeys - 1) * height) - 2),
stair_radius1,
(height / 2) as f32,
))
.fill(white);
}
}

View File

@ -0,0 +1,332 @@
use super::*;
use crate::{
util::{RandomField, Sampler, CARDINALS},
Land,
};
use common::terrain::{BlockKind, SpriteKind};
use rand::prelude::*;
use std::sync::Arc;
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct CoastalWorkshop {
/// 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 CoastalWorkshop {
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 {
door_tile: door_tile_pos,
bounds,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2,
}
}
}
impl Structure for CoastalWorkshop {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_coastalworkshop\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_coastalworkshop")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.alt + 1;
let center = self.bounds.center();
let white = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 37 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
_ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
})
}));
let blue_broken = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 20 {
0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
_ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
})
}));
let length = (14 + RandomField::new(0).get(center.with_z(base)) % 3) as i32;
let width = (12 + RandomField::new(0).get((center - 1).with_z(base)) % 3) as i32;
let height = (12 + RandomField::new(0).get((center + 1).with_z(base)) % 4) as i32;
// fence, blue gates
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 1),
max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base),
})
.fill(blue_broken.clone());
for dir in CARDINALS {
let frame_pos = Vec2::new(
center.x + dir.x * (length + 5),
center.y + dir.y * (width + 5),
);
painter
.line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
.fill(blue_broken.clone());
}
// foundation
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
})
.fill(white.clone());
for f in 0..8 {
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
.with_z(base - 3 - f),
max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
.with_z(base - 2 - f),
})
.fill(white.clone());
}
// clear yard
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
})
.clear();
// clear entries
for dir in CARDINALS {
let clear_pos = Vec2::new(
center.x + dir.x * (length + 7),
center.y + dir.y * (width + 7),
);
painter
.line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
.clear();
}
// roof terrace
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 3, center.y - width - 3)
.with_z(base - 3 + height),
max: Vec2::new(center.x + length + 2, center.y + width + 2)
.with_z(base - 2 + height),
})
.fill(white.clone());
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 3, center.y - width - 3)
.with_z(base - 2 + height),
max: Vec2::new(center.x + length + 2, center.y + width + 2)
.with_z(base - 1 + height),
})
.fill(blue_broken.clone());
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 2, center.y - width - 2)
.with_z(base - 2 + height),
max: Vec2::new(center.x + length + 1, center.y + width + 1)
.with_z(base - 1 + height),
})
.clear();
// room
painter
.aabb(Aabb {
min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
max: Vec2::new(center.x + length, center.y + width).with_z(base - 1),
})
.fill(blue_broken.clone());
painter
.aabb(Aabb {
min: Vec2::new(center.x - length + 1, center.y - width + 1).with_z(base - 2),
max: Vec2::new(center.x + length - 1, center.y + width - 1)
.with_z(base - 1 + height - 1),
})
.fill(white.clone());
// entries
let entry_limit = painter.aabb(Aabb {
min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
max: Vec2::new(center.x + length, center.y + width).with_z(base - 1 + height - 1),
});
painter
.line(
Vec2::new(center.x, center.y + 1 - width).with_z(base - 1),
Vec2::new(center.x, center.y - 2 + width).with_z(base - 1),
8.0,
)
.intersect(entry_limit)
.fill(blue_broken.clone());
painter
.line(
Vec2::new(center.x, center.y - width).with_z(base - 1),
Vec2::new(center.x, center.y + width).with_z(base - 1),
7.0,
)
.intersect(entry_limit)
.clear();
painter
.line(
Vec2::new(center.x + 1 - length, center.y).with_z(base - 1),
Vec2::new(center.x - 2 + length, center.y).with_z(base - 1),
8.0,
)
.intersect(entry_limit)
.fill(blue_broken.clone());
painter
.line(
Vec2::new(center.x - length, center.y).with_z(base - 1),
Vec2::new(center.x + length, center.y).with_z(base - 1),
7.0,
)
.intersect(entry_limit)
.clear();
// clear room
painter
.aabb(Aabb {
min: Vec2::new(center.x - length + 2, center.y - width + 2).with_z(base - 2),
max: Vec2::new(center.x + length - 2, center.y + width - 2)
.with_z(base - 2 + height - 1),
})
.clear();
// room floors
painter
.aabb(Aabb {
min: Vec2::new(center.x - length + 5, center.y - width + 5).with_z(base - 3),
max: Vec2::new(center.x + length - 5, center.y + width - 5).with_z(base - 2),
})
.fill(blue_broken.clone());
painter
.aabb(Aabb {
min: Vec2::new(center.x - length + 6, center.y - width + 6).with_z(base - 3),
max: Vec2::new(center.x + length - 6, center.y + width - 6).with_z(base - 2),
})
.fill(white.clone());
// wall lamps
for d in 0..2 {
let door_lamp_pos =
Vec2::new(center.x - length + 2 + (d * ((2 * (length)) - 5)), center.y)
.with_z(base + 6);
painter.rotated_sprite(
door_lamp_pos,
SpriteKind::WallLampSmall,
2 + ((d * 4) as u8),
);
let lamp_pos = Vec2::new(center.x, center.y - width + 2 + (d * ((2 * (width)) - 5)))
.with_z(base + 6);
painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 4 - ((d * 4) as u8));
}
for d in 0..2 {
let door_lamp_pos =
Vec2::new(center.x - length - 1 + (d * ((2 * (length)) + 1)), center.y)
.with_z(base + 6);
painter.rotated_sprite(
door_lamp_pos,
SpriteKind::WallLampSmall,
6 + ((d * 4) as u8),
);
let lamp_pos = Vec2::new(center.x, center.y - width - 1 + (d * ((2 * (width)) + 1)))
.with_z(base + 6);
painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 8 - ((d * 4) as u8));
}
// chimney
painter
.cylinder(Aabb {
min: (center - 4).with_z(base + height - 4),
max: (center + 2).with_z(base - 2 + height + (height / 2)),
})
.fill(blue_broken);
let top_limit = painter.aabb(Aabb {
min: Vec2::new(center.x - length, center.y - width).with_z(base + height - 2),
max: Vec2::new(center.x + length, center.y + width)
.with_z(base - 2 + height + (height / 2)),
});
painter
.superquadric(
Aabb {
min: Vec2::new(center.x - length - 1, center.y - width - 1)
.with_z(base + height - (height / 2)),
max: Vec2::new(center.x + length, center.y + width)
.with_z(base - 2 + height + (height / 2)),
},
1.5,
)
.intersect(top_limit)
.fill(white.clone());
// clear chimney
painter
.cylinder(Aabb {
min: (center - 3).with_z(base + height - 4),
max: (center + 1).with_z(base - 2 + height + (height / 2)),
})
.clear();
painter
.cylinder(Aabb {
min: (center - 3).with_z(base - 2),
max: (center + 1).with_z(base - 1),
})
.fill(white);
painter
.aabb(Aabb {
min: (center - 2).with_z(base - 2),
max: (center).with_z(base - 1),
})
.clear();
painter
.aabb(Aabb {
min: (center - 2).with_z(base - 3),
max: (center).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,
];
'outer: for d in 0..3 {
for dir in CARDINALS {
if stations.is_empty() {
break 'outer;
}
let position = center + dir * (4 + d * 2);
let cr_station = stations.swap_remove(
RandomField::new(0).get(position.with_z(base)) as usize % stations.len(),
);
painter.sprite(position.with_z(base - 2), cr_station);
}
}
painter.spawn(
EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5)).into_waypoint(),
);
}
}

View File

@ -39,6 +39,8 @@ struct WatchTower {
/// Represents house data generated by the `generate()` method
pub struct DesertCityMultiPlot {
/// 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
@ -58,6 +60,7 @@ impl DesertCityMultiPlot {
tile_aabr: Aabr<i32>,
campfire: bool,
) -> 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),
@ -120,6 +123,7 @@ impl DesertCityMultiPlot {
};
Self {
bounds,
door_tile: door_tile_pos,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32,
diameter,
plot_kind,
@ -159,14 +163,23 @@ impl Structure for DesertCityMultiPlot {
// Fence
painter
.aabb(Aabb {
min: (self.bounds.min + 1).with_z(base - 20),
max: (self.bounds.max).with_z(base + 2),
min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
max: Vec2::new(self.bounds.min.x + 2, self.bounds.max.y).with_z(base + 2),
})
.without(painter.aabb(Aabb {
min: (self.bounds.min + 2).with_z(base - 20),
max: (self.bounds.max - 1).with_z(base + 2),
.union(painter.aabb(Aabb {
min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
}))
.union(painter.aabb(Aabb {
min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
}))
.union(painter.aabb(Aabb {
min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
}))
.fill(sandstone_broken.clone());
// Gate1
painter
.aabb(Aabb {
@ -429,17 +442,20 @@ impl Structure for DesertCityMultiPlot {
.aabb(Aabb {
min: Vec2::new(center.x - 1, center.y - (2 * room_length / 3) + 5)
.with_z(floor_level + 4),
max: Vec2::new(center.x + 1, center.y - (2 * room_length / 3) + 6)
.with_z(floor_level + 5),
})
.fill(Fill::Block(
Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(),
));
painter
.aabb(Aabb {
min: Vec2::new(center.x - 1, center.y + (2 * room_length / 3) - 2)
.with_z(floor_level + 4),
max: Vec2::new(center.x + 1, center.y + (2 * room_length / 3) - 1)
.with_z(floor_level + 5),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(center.x - 1, center.y - (2 * room_length / 3) + 6)
.with_z(floor_level + 4),
max: Vec2::new(center.x + 1, center.y + (2 * room_length / 3) - 2)
.with_z(floor_level + 5),
}),
)
.fill(Fill::Block(
Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(),
));
@ -449,17 +465,20 @@ impl Structure for DesertCityMultiPlot {
.aabb(Aabb {
min: Vec2::new(center.x - 1, center.y - room_length + 4)
.with_z(floor_level + 4),
max: Vec2::new(center.x + 1, center.y - room_length + 5)
.with_z(floor_level + 5),
})
.fill(Fill::Block(
Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(),
));
painter
.aabb(Aabb {
min: Vec2::new(center.x - 1, center.y + room_length)
.with_z(floor_level + 4),
max: Vec2::new(center.x + 1, center.y + room_length + 1)
.with_z(floor_level + 5),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(center.x - 1, center.y - room_length + 5)
.with_z(floor_level + 4),
max: Vec2::new(center.x + 1, center.y + room_length)
.with_z(floor_level + 5),
}),
)
.fill(Fill::Block(
Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(),
));
@ -757,6 +776,13 @@ impl Structure for DesertCityMultiPlot {
.with_z(base + 4),
})
.clear();
// clear Tower base
painter
.aabb(Aabb {
min: (tower_center - tower.length).with_z(base),
max: (tower_center + tower.length).with_z(base + 5),
})
.clear();
// Tower Entry Lamps
for d in 0..2 {
painter
@ -832,28 +858,6 @@ impl Structure for DesertCityMultiPlot {
))
.intersect(tower_clear)
.fill(sandstone.clone());
//make some room for base entries
painter
.aabb(Aabb {
min: (tower_center - tower.length).with_z(base),
max: (tower_center + tower.length).with_z(base + 5),
})
.without(painter.aabb(Aabb {
min: (tower_center - tower.length + 1).with_z(base),
max: (tower_center + tower.length - 1).with_z(base + 5),
}))
.clear();
// clear some steps at the bottom for tower entries
painter
.aabb(Aabb {
min: (tower_center - tower.length + 1).with_z(base + 1),
max: (tower_center + tower.length - 1).with_z(base + 2),
})
.without(painter.aabb(Aabb {
min: (tower_center - tower.length + 2).with_z(base + 1),
max: (tower_center + tower.length - 2).with_z(base + 2),
}))
.clear();
}
},
PlotKind::Multiple { subplots } => {
@ -1150,7 +1154,6 @@ impl Structure for DesertCityMultiPlot {
SpriteKind::Loom,
SpriteKind::Anvil,
SpriteKind::DismantlingBench,
SpriteKind::RepairBench,
];
'outer: for d in 0..2 {
for dir in NEIGHBORS {
@ -1328,26 +1331,28 @@ impl Structure for DesertCityMultiPlot {
subplot_center.y - room_length + 4,
)
.with_z(floor_level + 4),
max: Vec2::new(
subplot_center.x + 1,
subplot_center.y - room_length + 5,
)
.with_z(floor_level + 5),
})
.fill(Fill::Block(
Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(),
));
painter
.aabb(Aabb {
min: Vec2::new(
subplot_center.x - 1,
subplot_center.y + room_length,
)
.with_z(floor_level + 4),
max: Vec2::new(
subplot_center.x + 1,
subplot_center.y + room_length + 1,
)
.with_z(floor_level + 5),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(
subplot_center.x - 1,
subplot_center.y - room_length + 5,
)
.with_z(floor_level + 4),
max: Vec2::new(
subplot_center.x + 1,
subplot_center.y + room_length,
)
.with_z(floor_level + 5),
}),
)
.fill(Fill::Block(
Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(),
));
@ -1462,30 +1467,30 @@ impl Structure for DesertCityMultiPlot {
bldg_b_center.y - 4,
)
.with_z(base + 2 + (n * (tower_height / 2)) + (h * 4)),
max: Vec2::new(
bldg_b_center.x + (2 * tower_length),
bldg_b_center.y - 2,
)
.with_z(base + 5 + (n * (tower_height / 2)) + (h * 4)),
})
.fill(Fill::Block(
Block::air(SpriteKind::WindowArabic)
.with_ori(4)
.unwrap(),
));
painter
.aabb(Aabb {
min: Vec2::new(
bldg_b_center.x + (2 * tower_length) - 1,
bldg_b_center.y + 2,
)
.with_z(base + 2 + (n * (tower_height / 2)) + (h * 4)),
max: Vec2::new(
bldg_b_center.x + (2 * tower_length),
bldg_b_center.y + 4,
)
.with_z(base + 5 + (n * (tower_height / 2)) + (h * 4)),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(
bldg_b_center.x + (2 * tower_length) - 1,
bldg_b_center.y - 2,
)
.with_z(
base + 2 + (n * (tower_height / 2)) + (h * 4),
),
max: Vec2::new(
bldg_b_center.x + (2 * tower_length),
bldg_b_center.y + 2,
)
.with_z(
base + 5 + (n * (tower_height / 2)) + (h * 4),
),
}),
)
.fill(Fill::Block(
Block::air(SpriteKind::WindowArabic)
.with_ori(4)
@ -1864,6 +1869,13 @@ impl Structure for DesertCityMultiPlot {
.with_z(base + 4),
})
.clear();
// clear Stairway Tower base
painter
.aabb(Aabb {
min: (bldg_a_center - tower_length).with_z(base),
max: (bldg_a_center + tower_length).with_z(base + 6),
})
.clear();
// Stairway Tower Entry Lamps
for d in 0..2 {
painter
@ -1959,17 +1971,6 @@ impl Structure for DesertCityMultiPlot {
))
.intersect(tower_clear)
.fill(sandstone.clone());
// make some room for base entries
painter
.aabb(Aabb {
min: (bldg_a_center - tower_length).with_z(base),
max: (bldg_a_center + tower_length).with_z(base + 5),
})
.without(painter.aabb(Aabb {
min: (bldg_a_center - tower_length + 1).with_z(base),
max: (bldg_a_center + tower_length - 1).with_z(base + 5),
}))
.clear();
},
SubPlotKind::WatchTower(tower) => {
// WatchTower Windowsills
@ -2086,6 +2087,13 @@ impl Structure for DesertCityMultiPlot {
.with_z(base + 4),
})
.clear();
// clear Watch Tower base
painter
.aabb(Aabb {
min: (subplot_center - tower.length).with_z(base),
max: (subplot_center + tower.length).with_z(base + 5),
})
.clear();
// WatchTower Entry Lamps
for d in 0..2 {
painter
@ -2180,28 +2188,6 @@ impl Structure for DesertCityMultiPlot {
))
.intersect(tower_clear)
.fill(sandstone.clone());
//make some room for base entries
painter
.aabb(Aabb {
min: (subplot_center - tower.length).with_z(base),
max: (subplot_center + tower.length).with_z(base + 5),
})
.without(painter.aabb(Aabb {
min: (subplot_center - tower.length + 1).with_z(base),
max: (subplot_center + tower.length - 1).with_z(base + 5),
}))
.clear();
// clear some steps at the bottom for tower entries
painter
.aabb(Aabb {
min: (subplot_center - tower.length + 1).with_z(base + 1),
max: (subplot_center + tower.length - 1).with_z(base + 2),
})
.without(painter.aabb(Aabb {
min: (subplot_center - tower.length + 2).with_z(base + 1),
max: (subplot_center + tower.length - 2).with_z(base + 2),
}))
.clear();
},
SubPlotKind::AnimalShed => {
// fenced animal shed
@ -2215,22 +2201,50 @@ impl Structure for DesertCityMultiPlot {
)
.with_z(base),
max: Vec2::new(
subplot_center.x + (2 * shed_length) + 2,
subplot_center.x - (2 * shed_length) + 1,
subplot_center.y + (2 * shed_length) + 1,
)
.with_z(base + 2),
})
.without(
.union(
painter.aabb(Aabb {
min: Vec2::new(
subplot_center.x - (2 * shed_length) + 1,
subplot_center.y - (2 * shed_length),
subplot_center.x + (2 * shed_length) + 1,
subplot_center.y - (2 * shed_length) - 1,
)
.with_z(base),
max: Vec2::new(
subplot_center.x + (2 * shed_length) + 1,
subplot_center.x + (2 * shed_length) + 2,
subplot_center.y + (2 * shed_length) + 1,
)
.with_z(base + 2),
}),
)
.union(
painter.aabb(Aabb {
min: Vec2::new(
subplot_center.x - (2 * shed_length),
subplot_center.y - (2 * shed_length) - 1,
)
.with_z(base),
max: Vec2::new(
subplot_center.x + (2 * shed_length) + 2,
subplot_center.y - (2 * shed_length),
)
.with_z(base + 2),
}),
)
.union(
painter.aabb(Aabb {
min: Vec2::new(
subplot_center.x - (2 * shed_length),
subplot_center.y + (2 * shed_length),
)
.with_z(base),
max: Vec2::new(
subplot_center.x + (2 * shed_length) + 2,
subplot_center.y + (2 * shed_length) + 1,
)
.with_z(base + 2),
}),
)

View File

@ -10,6 +10,8 @@ use vek::*;
/// Represents house data generated by the `generate()` method
pub struct DesertCityTemple {
/// 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
@ -25,6 +27,7 @@ impl DesertCityTemple {
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),
@ -32,6 +35,7 @@ impl DesertCityTemple {
Self {
bounds,
door_tile: door_tile_pos,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32,
}
}
@ -69,12 +73,20 @@ impl Structure for DesertCityTemple {
// Fence
painter
.aabb(Aabb {
min: (self.bounds.min + 1).with_z(base - 20),
max: (self.bounds.max).with_z(base + 2),
min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
max: Vec2::new(self.bounds.min.x + 2, self.bounds.max.y).with_z(base + 2),
})
.without(painter.aabb(Aabb {
min: (self.bounds.min + 2).with_z(base - 20),
max: (self.bounds.max - 1).with_z(base + 2),
.union(painter.aabb(Aabb {
min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
}))
.union(painter.aabb(Aabb {
min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
}))
.union(painter.aabb(Aabb {
min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
}))
.fill(sandstone_broken);
painter
@ -273,105 +285,86 @@ impl Structure for DesertCityTemple {
.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 2)
.with_z(base + 2),
max: Vec2::new(center.x + temple_size - 3, center.y - temple_size + 3)
.with_z(base + temple_size - 1),
})
.fill(temple_color.clone());
painter
.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 2, center.y + temple_size - 4)
.with_z(base + 2),
max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 3)
.with_z(base + temple_size - 1),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 3)
.with_z(base + 2),
max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 4)
.with_z(base + temple_size - 1),
}),
)
.fill(temple_color.clone());
// color inlays2
painter
.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 2)
.with_z(base + 2),
max: Vec2::new(center.x - temple_size + 3, center.y + temple_size - 3)
.with_z(base + temple_size - 1),
})
.fill(temple_color.clone());
painter
.aabb(Aabb {
min: Vec2::new(center.x + temple_size - 4, center.y - temple_size + 2)
.with_z(base + 2),
max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 3)
.with_z(base + temple_size - 1),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 3, center.y - temple_size + 2)
.with_z(base + 2),
max: Vec2::new(center.x + temple_size - 4, center.y + temple_size - 3)
.with_z(base + temple_size - 1),
}),
)
.fill(temple_color.clone());
// carve outside plane1
painter
.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 6, center.y - temple_size).with_z(base + 3),
max: Vec2::new(center.x + temple_size - 7, center.y - temple_size + 1)
.with_z(base + temple_size - 2),
})
.clear();
painter
.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 6, center.y + temple_size - 2)
.with_z(base + 3),
max: Vec2::new(center.x + temple_size - 7, center.y + temple_size - 1)
.with_z(base + temple_size - 2),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 6, center.y - temple_size + 1)
.with_z(base + 3),
max: Vec2::new(center.x + temple_size - 7, center.y + temple_size - 2)
.with_z(base + temple_size - 2),
}),
)
.clear();
// carve outside plane2
painter
.aabb(Aabb {
min: Vec2::new(center.x - temple_size, center.y - temple_size + 6).with_z(base + 3),
max: Vec2::new(center.x - temple_size + 1, center.y + temple_size - 7)
.with_z(base + temple_size - 2),
})
.clear();
painter
.aabb(Aabb {
min: Vec2::new(center.x + temple_size - 2, center.y - temple_size + 6)
.with_z(base + 3),
max: Vec2::new(center.x + temple_size - 1, center.y + temple_size - 7)
.with_z(base + temple_size - 2),
})
.without(
painter.aabb(Aabb {
min: Vec2::new(center.x - temple_size + 1, center.y - temple_size + 6)
.with_z(base + 3),
max: Vec2::new(center.x + temple_size - 2, center.y + temple_size - 7)
.with_z(base + temple_size - 2),
}),
)
.clear();
// Temple top
// Temple Top Socket
painter
.sphere(Aabb {
min: (center - temple_size + 3).with_z(base),
max: (center + temple_size - 4).with_z(base + (2 * temple_size) - 6),
})
.without(painter.aabb(Aabb {
min: (center - temple_size + 2).with_z(base + 1),
max: (center + temple_size - 3).with_z(base + temple_size - 3),
}))
.without(painter.aabb(Aabb {
min: (center - temple_size + 1).with_z(base),
max: (center + temple_size - 1).with_z(base + temple_size),
}))
// carve temple top
.without(painter.sphere(Aabb {
min: (center - temple_size + 5).with_z(base + 2),
max: (center + temple_size - 6).with_z(base + (2 * temple_size) - 8),
}))
.fill(sandstone.clone());
.cylinder(Aabb {
min: (center - temple_size + 3).with_z(base + temple_size),
max: (center + temple_size - 4).with_z(base + temple_size + 1),
})
.fill(sandstone.clone());
// Temple Top
let top = painter.sphere(Aabb {
min: (center - temple_size + 3).with_z(base),
max: (center + temple_size - 4).with_z(base + (2 * temple_size) - 7),
});
top.fill(sandstone.clone());
// temple top decoration
// apply two decoration options (horizontal, vertical, none) with two spread
// options for hor/ver options
for s in 0..2 {
let top = painter
.sphere(Aabb {
min: (center - temple_size + 3).with_z(base),
max: (center + temple_size - 4).with_z(base + (2 * temple_size) - 6),
})
.without(painter.sphere(Aabb {
min: (center - temple_size + 5).with_z(base + 2),
max: (center + temple_size - 6).with_z(base + (2 * temple_size) - 8),
}))
.without(painter.aabb(Aabb {
min: (center - temple_size + 1).with_z(base),
max: (center + temple_size - 2).with_z(base + temple_size + 1),
}));
// decor carve out style
let spread_select =
((RandomField::new(0).get((center - 1 - s).with_z(base))) % 2) as i32;
@ -421,6 +414,20 @@ impl Structure for DesertCityTemple {
_ => {},
}
}
// Temple Top Socket
painter
.cylinder(Aabb {
min: (center - temple_size + 3).with_z(base + temple_size),
max: (center + temple_size - 4).with_z(base + temple_size + 1),
})
.fill(sandstone.clone());
// clear top
painter
.sphere(Aabb {
min: (center - temple_size + 4).with_z(base + 1),
max: (center + temple_size - 5).with_z(base + (2 * temple_size) - 8),
})
.clear();
// round or rectangle entries
let entry_select = ((RandomField::new(0).get((center + 1).with_z(base))) % 2) as i32;
@ -550,17 +557,6 @@ impl Structure for DesertCityTemple {
.clear();
},
}
// Temple Top Socket
painter
.cylinder(Aabb {
min: (center - temple_size + 3).with_z(base + temple_size),
max: (center + temple_size - 4).with_z(base + temple_size + 1),
})
.without(painter.cylinder(Aabb {
min: (center - temple_size + 5).with_z(base + temple_size),
max: (center + temple_size - 6).with_z(base + temple_size + 1),
}))
.fill(sandstone.clone());
// carve temple
painter
@ -569,13 +565,6 @@ impl Structure for DesertCityTemple {
max: (center + temple_size - 5).with_z(base + temple_size - 1),
})
.clear();
// carve round opening to dome
painter
.cylinder(Aabb {
min: (center - temple_size + 6).with_z(base + temple_size - 1),
max: (center + temple_size - 7).with_z(base + temple_size),
})
.clear();
// floating sculpture, placeholder for sun god
@ -593,8 +582,8 @@ impl Structure for DesertCityTemple {
.fill(sandstone.clone());
painter
.sphere(Aabb {
min: (center - 4).with_z(base + 8),
max: (center + 3).with_z(base + 13),
min: (center - 3).with_z(base + 8),
max: (center + 2).with_z(base + 13),
})
.fill(sandstone.clone());
painter
@ -646,38 +635,5 @@ impl Structure for DesertCityTemple {
})
.fill(floaty_block.clone());
}
// floating sculpture, placeholder for sun god
painter
.sphere(Aabb {
min: (center - 3).with_z(base),
max: (center + 2).with_z(base + 5),
})
.fill(sandstone.clone());
painter
.sphere(Aabb {
min: (center - 3).with_z(base + 4),
max: (center + 2).with_z(base + 9),
})
.fill(sandstone.clone());
painter
.sphere(Aabb {
min: (center - 4).with_z(base + 8),
max: (center + 3).with_z(base + 13),
})
.fill(sandstone.clone());
painter
.sphere(Aabb {
min: (center - 4).with_z(base + 14),
max: (center + 3).with_z(base + 15),
})
.fill(sandstone);
painter
.sphere(Aabb {
min: (center - 3).with_z(base + 16),
max: (center + 2).with_z(base + 21),
})
.fill(floaty_block);
}
}