more_airship_docks

This commit is contained in:
flo
2024-11-13 13:52:52 +00:00
parent 642748833e
commit 9b45f394a4
31 changed files with 1585 additions and 208 deletions

View File

@ -45,7 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Npcs can catch you stealing.
- Button to show unknown recipes.
- Water splashes and river particles.
- Custom furniture for Coastal Houses
- Custom furniture for Coastal Houses.
- Airship docks for desert cities, coastal towns and mesa towns.
### Changed

View File

@ -1 +1,2 @@
common-signs-keep_out = Keep Out!
common-signs-airship_dock = Service interrupted!

View File

@ -1,18 +0,0 @@
/// Distribution of different dungeon levels.
///
/// first number is dungeon level, integer
/// second number is weight, any normal positive float (not a NaN, for example)
///
/// Values are relative to each other,
/// lesser weight means there will be less dungeons of that tier.
///
/// General rules:
/// 1) Weight should not be less then zero
/// 2) At least some of weights shouldn't be a zero
/// 3) Keep it synced with number of dungeon levels
/// 4) Keep these pairs sorted from lowest to highest tier
///
/// Tips:
/// 1) Set every probability to 0.0 and left one with any other number
/// and you will have map full of dungeons of same level
([])

View File

@ -1,16 +0,0 @@
#![enable(unwrap_newtypes)]
[
(
specifier: "world.structure.dungeon.desert_entrance.1",
center: (35, 50, 21)
),
(
specifier: "world.structure.dungeon.desert_entrance.2",
center: (21, 21, 41)
),
(
specifier: "world.structure.dungeon.desert_entrance.3",
center: (32, 31, 28)
),
]

View File

@ -1,44 +0,0 @@
#![enable(unwrap_newtypes)]
[
(
specifier: "world.structure.dungeon.pillar_entrance.round.1",
center: (21, 17, 28)
),
(
specifier: "world.structure.dungeon.pillar_entrance.round.2",
center: (20, 28, 15)
),
(
specifier: "world.structure.dungeon.pillar_entrance.1",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.2",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.3",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.4",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.5",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.6",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.temperate_entrance.ruins_4",
center: (13, 11, 14)
),
(
specifier: "world.structure.dungeon.misc_entrance.tower-ruin",
center: (13, 16, 9)
),
]

View File

@ -1,16 +0,0 @@
#![enable(unwrap_newtypes)]
[
(
specifier: "world.structure.dungeon.jungle_entrance.1",
center: (50, 40, 10)
),
(
specifier: "world.structure.dungeon.jungle_entrance.2",
center: (60, 36, 36)
),
(
specifier: "world.structure.dungeon.jungle_entrance.3",
center: (24, 22, 44)
),
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1231,7 +1231,11 @@ fn pilot<S: State>(ship: common::comp::ship::Body) -> impl Action<S> {
.filter(|plot| {
matches!(
plot.kind(),
PlotKind::AirshipDock(_) | PlotKind::SavannahAirshipDock(_)
PlotKind::AirshipDock(_)
| PlotKind::SavannahAirshipDock(_)
| PlotKind::DesertCityAirshipDock(_)
| PlotKind::CoastalAirshipDock(_)
| PlotKind::CliffTownAirshipDock(_)
)
})
.map(|plot| site.tile_center_wpos(plot.root_tile()))

View File

@ -2162,6 +2162,8 @@ impl SiteKind {
},
SiteKind::DesertCity => {
(0.9..1.0).contains(&chunk.temp) && !chunk.near_cliffs() && suitable_for_town()
&& on_land()
&& !chunk.river.near_water()
},
SiteKind::ChapelSite => {
matches!(chunk.get_biome(), BiomeKind::Ocean)

View File

@ -1358,39 +1358,87 @@ impl Site {
};
let mut campfires = 0;
site.make_plaza(land, &mut rng);
for _ in 0..30 {
// CliffTower
let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
let campfire = campfires < 4;
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
site.find_roadside_aabr(&mut rng, 8..(size + 1).pow(2), Extent2::broadcast(size))
}) {
let cliff_tower = plot::CliffTower::generate(
land,
index,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
campfire,
);
let cliff_tower_alt = cliff_tower.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::CliffTower(cliff_tower),
root_tile: aabr.center(),
tiles: aabr_tiles(aabr).collect(),
seed: rng.gen(),
});
let build_chance = Lottery::from(vec![(30.0, 1), (50.0, 2)]);
let mut airship_docks = 0;
for _ in 0..80 {
match *build_chance.choose_seeded(rng.gen()) {
1 => {
// CliffTower
let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
let campfire = campfires < 4;
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
site.find_roadside_aabr(
&mut rng,
8..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let cliff_tower = plot::CliffTower::generate(
land,
index,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
campfire,
);
let cliff_tower_alt = cliff_tower.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::CliffTower(cliff_tower),
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(cliff_tower_alt),
});
campfires += 1;
} else {
site.make_plaza(land, &mut rng);
site.blit_aabr(aabr, Tile {
kind: TileKind::Building,
plot: Some(plot),
hard_alt: Some(cliff_tower_alt),
});
campfires += 1;
} else {
site.make_plaza(land, &mut rng);
}
},
2 if airship_docks < 1 => {
// CliffTownAirshipDock
let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
if let Some((aabr, door_tile, door_dir)) = attempt(32, || {
site.find_roadside_aabr(
&mut rng,
8..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let cliff_town_airship_dock = plot::CliffTownAirshipDock::generate(
land,
index,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let cliff_town_airship_dock_alt = cliff_town_airship_dock.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::CliffTownAirshipDock(cliff_town_airship_dock),
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(cliff_town_airship_dock_alt),
});
airship_docks += 1;
} else {
site.make_plaza(land, &mut rng);
}
},
_ => {},
}
}
@ -1410,7 +1458,7 @@ impl Site {
site.make_plaza(land, &mut rng);
let mut airship_dock = 0;
let build_chance = Lottery::from(vec![(25.0, 1), (7.0, 2), (3.0, 3), (15.0, 4)]);
let build_chance = Lottery::from(vec![(25.0, 1), (5.0, 2), (5.0, 3), (15.0, 4)]);
for _ in 0..50 {
match *build_chance.choose_seeded(rng.gen()) {
@ -1489,7 +1537,7 @@ impl Site {
3 if airship_dock < 1 => {
// SavannahAirshipDock
let size = (4.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
let size = (6.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,
@ -1518,10 +1566,10 @@ impl Site {
plot: Some(plot),
hard_alt: Some(savannah_airship_dock_alt),
});
airship_dock += 1;
} else {
site.make_plaza(land, &mut rng);
}
airship_dock += 1;
},
// Field
4 => {
@ -1543,9 +1591,9 @@ impl Site {
site.demarcate_obstacles(land);
site.make_plaza(land, &mut rng);
let build_chance = Lottery::from(vec![(38.0, 1), (7.0, 2), (15.0, 3)]);
for _ in 0..45 {
let build_chance = Lottery::from(vec![(38.0, 1), (7.0, 2), (15.0, 3), (15.0, 4)]);
let mut airship_docks = 0;
for _ in 0..55 {
match *build_chance.choose_seeded(rng.gen()) {
1 => {
// CoastalHouse
@ -1619,8 +1667,44 @@ impl Site {
site.make_plaza(land, &mut rng);
}
},
3 if airship_docks < 1 => {
// CoastalAirshipDock
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_airship_dock = plot::CoastalAirshipDock::generate(
land,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let coastal_airship_dock_alt = coastal_airship_dock.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::CoastalAirshipDock(coastal_airship_dock),
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_airship_dock_alt),
});
airship_docks += 1;
} else {
site.make_plaza(land, &mut rng);
}
},
// Field
3 => {
4 => {
Self::generate_farm(false, &mut rng, &mut site, land);
},
_ => {},
@ -1665,12 +1749,13 @@ impl Site {
hard_alt: Some(desert_city_arena_alt),
});
let build_chance = Lottery::from(vec![(20.0, 1), (10.0, 2), (10.0, 3)]);
let build_chance = Lottery::from(vec![(20.0, 1), (10.0, 2), (5.0, 3), (10.0, 4)]);
let mut temples = 0;
let mut airship_docks = 0;
let mut campfires = 0;
for _ in 0..30 {
for _ in 0..35 {
match *build_chance.choose_seeded(rng.gen()) {
// DesertCityMultiplot
1 => {
@ -1744,8 +1829,42 @@ impl Site {
temples += 1;
}
},
// DesertCityAirshipDock
3 if airship_docks < 1 => {
let size = (6.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,
8..(size + 1).pow(2),
Extent2::broadcast(size),
)
}) {
let desert_city_airship_dock = plot::DesertCityAirshipDock::generate(
land,
&mut reseed(&mut rng),
&site,
door_tile,
door_dir,
aabr,
);
let desert_city_airship_dock_alt = desert_city_airship_dock.alt;
let plot = site.create_plot(Plot {
kind: PlotKind::DesertCityAirshipDock(desert_city_airship_dock),
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(desert_city_airship_dock_alt),
});
airship_docks += 1;
}
},
// cactus farm
3 => {
4 => {
Self::generate_farm(true, &mut rng, &mut site, land);
},
_ => {},
@ -2491,6 +2610,9 @@ impl Site {
},
PlotKind::GliderFinish(glider_finish) => glider_finish.render_collect(self, canvas),
PlotKind::Tavern(tavern) => tavern.render_collect(self, canvas),
PlotKind::CoastalAirshipDock(coastal_airship_dock) => {
coastal_airship_dock.render_collect(self, canvas)
},
PlotKind::CoastalHouse(coastal_house) => coastal_house.render_collect(self, canvas),
PlotKind::CoastalWorkshop(coastal_workshop) => {
coastal_workshop.render_collect(self, canvas)
@ -2504,6 +2626,9 @@ impl Site {
PlotKind::Haniwa(haniwa) => haniwa.render_collect(self, canvas),
PlotKind::GiantTree(giant_tree) => giant_tree.render_collect(self, canvas),
PlotKind::CliffTower(cliff_tower) => cliff_tower.render_collect(self, canvas),
PlotKind::CliffTownAirshipDock(cliff_town_airship_dock) => {
cliff_town_airship_dock.render_collect(self, canvas)
},
PlotKind::Sahagin(sahagin) => sahagin.render_collect(self, canvas),
PlotKind::SavannahAirshipDock(savannah_airship_dock) => {
savannah_airship_dock.render_collect(self, canvas)
@ -2541,6 +2666,9 @@ impl Site {
PlotKind::DesertCityArena(desert_city_arena) => {
desert_city_arena.render_collect(self, canvas)
},
PlotKind::DesertCityAirshipDock(desert_city_airship_dock) => {
desert_city_airship_dock.render_collect(self, canvas)
},
PlotKind::Citadel(citadel) => citadel.render_collect(self, canvas),
PlotKind::Bridge(bridge) => bridge.render_collect(self, canvas),
PlotKind::PirateHideout(pirate_hideout) => {

View File

@ -4,18 +4,25 @@ use crate::{
util::{RandomField, Sampler},
Land,
};
use common::terrain::{Block, BlockKind, SpriteKind};
use common::{
comp::Content,
generation::SpecialEntity,
terrain::{BlockKind, SpriteCfg, SpriteKind},
};
use rand::prelude::*;
use std::f32::consts::PI;
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct AirshipDock {
/// Axis aligned bounding region for the house
bounds: Aabr<i32>,
/// Approximate altitude of the door tile
pub(crate) alt: i32,
rotation: f32,
pub door_tile: Vec2<i32>,
center: Vec2<i32>,
base: i32,
height: i32,
pub docking_positions: Vec<Vec3<i32>>,
campfire_pos: Vec3<i32>,
}
impl AirshipDock {
@ -32,6 +39,10 @@ impl AirshipDock {
min: site.tile_wpos(tile_aabr.min),
max: site.tile_wpos(tile_aabr.max),
};
let center = bounds.center();
let alt = land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32;
let base = alt + 3;
let height = base + 28;
let rotation = if door_dir.y < 0 {
PI
} else if door_dir.x < 0 {
@ -41,12 +52,20 @@ impl AirshipDock {
} else {
0.0
};
let docking_positions = vec![
(center + (door_dir * 16)).with_z(height + 9),
(center - (door_dir * 21)).with_z(height + 9),
];
let campfire_pos = (center - (door_dir * 10)).with_z(height + 9);
Self {
door_tile: door_tile_pos,
bounds,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32,
alt,
rotation,
center,
base,
height,
docking_positions,
campfire_pos,
}
}
}
@ -61,11 +80,9 @@ impl Structure for AirshipDock {
let wood = Fill::Brick(BlockKind::Rock, Rgb::new(45, 28, 21), 24);
let woodalt = Fill::Brick(BlockKind::Rock, Rgb::new(30, 22, 15), 24);
let base = self.alt + 3;
let center = self.bounds.center();
let height = base + 28;
//rotation
let base = self.base;
let center = self.center;
let height = self.height;
//lower doorway
painter
@ -724,5 +741,17 @@ impl Structure for AirshipDock {
)));
}
}
// campfire
painter.spawn(
EntityInfo::at(self.campfire_pos.map(|e| e as f32 + 0.5))
.into_special(SpecialEntity::Waypoint),
);
// dock
for dock_pos in &self.docking_positions {
painter.rotated_sprite_with_cfg(*dock_pos, SpriteKind::Sign, 2, SpriteCfg {
unlock: None,
content: Some(Content::localized("common-signs-airship_dock")),
});
}
}
}

View File

@ -0,0 +1,590 @@
use super::*;
use crate::{
site2::util::gradient::WrapMode,
util::{RandomField, Sampler, DIAGONALS, LOCALITY, NEIGHBORS},
Land,
};
use common::{
comp::Content,
generation::SpecialEntity,
terrain::{BlockKind, SpriteCfg, SpriteKind},
};
use rand::prelude::*;
use std::{f32::consts::TAU, mem};
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct CliffTownAirshipDock {
/// Tile position of the door tile
pub door_tile: Vec2<i32>,
/// Approximate altitude of the door tile
pub(crate) alt: i32,
door_dir: Vec2<i32>,
surface_color: Rgb<f32>,
sub_surface_color: Rgb<f32>,
center: Vec2<i32>,
variant: i32,
storeys: i32,
platform_length: i32,
pub docking_positions: Vec<Vec3<i32>>,
}
impl CliffTownAirshipDock {
pub fn generate(
land: &Land,
index: IndexRef,
_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),
};
let center = bounds.center();
let alt = land.get_alt_approx(door_tile_pos) as i32;
let variant = 15;
let storeys = 5 + (variant / 2);
let platform_length = 2 * variant;
let mut docking_positions = vec![];
let mut platform_level = alt - 40;
let mut platform_height = 18 + variant / 2;
for s in 0..storeys {
if s == (storeys - 1) {
for dir in CARDINALS {
let docking_pos = center + dir * (platform_length + 7);
docking_positions.push(docking_pos.with_z(platform_level + 1));
}
}
platform_height += -1;
platform_level += platform_height;
}
let (surface_color, sub_surface_color) =
if let Some(sample) = land.column_sample(bounds.center(), index) {
(sample.surface_color, sample.sub_surface_color)
} else {
(Rgb::new(161.0, 116.0, 86.0), Rgb::new(88.0, 64.0, 64.0))
};
Self {
door_tile: door_tile_pos,
alt,
door_dir,
surface_color,
sub_surface_color,
center,
variant,
storeys,
platform_length,
docking_positions,
}
}
}
impl Structure for CliffTownAirshipDock {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_cliff_town_airship_dock\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_cliff_town_airship_dock")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.alt;
let plot_center = self.center;
let door_dir = self.door_dir;
let surface_color = self.surface_color.map(|e| (e * 255.0) as u8);
let sub_surface_color = self.sub_surface_color.map(|e| (e * 255.0) as u8);
let gradient_center = Vec3::new(
plot_center.x as f32,
plot_center.y as f32,
(base + 1) as f32,
);
let gradient_var_1 = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 8;
let gradient_var_2 = RandomField::new(0).get(plot_center.with_z(base + 1)) as i32 % 10;
let brick = Fill::Gradient(
util::gradient::Gradient::new(
gradient_center,
8.0 + gradient_var_1 as f32,
util::gradient::Shape::Point,
(surface_color, sub_surface_color),
)
.with_repeat(if gradient_var_2 > 5 {
WrapMode::Repeat
} else {
WrapMode::PingPong
}),
BlockKind::Rock,
);
let wood = Fill::Brick(BlockKind::Wood, Rgb::new(106, 83, 51), 12);
let color = Fill::Block(Block::air(SpriteKind::CliffDecorBlock));
let window = Fill::Block(Block::air(SpriteKind::WindowArabic));
let window2 = Fill::Block(Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap());
let rope = Fill::Block(Block::air(SpriteKind::Rope));
let tube_var = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 6;
let radius = 10.0 + tube_var as f32;
let tubes = 3.0 + tube_var as f32;
let phi = TAU / tubes;
for n in 1..=tubes as i32 {
let center = Vec2::new(
plot_center.x + (radius * ((n as f32 * phi).cos())) as i32,
plot_center.y + (radius * ((n as f32 * phi).sin())) as i32,
);
// common superquadric degree for rooms
let sq_type = 3.5;
let storeys = self.storeys;
let variant = self.variant;
let mut length = 16 + (variant / 2);
let mut width = 7 * length / 8;
let mut height = 18 + variant / 2;
let mut floor_level = self.alt - 40;
let platform_length = self.platform_length;
let mut ground_entries = 0;
for s in 0..storeys {
let x_offset = RandomField::new(0).get((center - length).with_z(base)) as i32 % 10;
let y_offset = RandomField::new(0).get((center + length).with_z(base)) as i32 % 10;
let super_center =
Vec2::new(center.x - 3 + x_offset / 2, center.y - 3 + y_offset / 2);
// CliffTower Hoodoo Overlay
painter
.cubic_bezier(
super_center.with_z(floor_level + (height / 2)),
(super_center - x_offset).with_z(floor_level + height),
(super_center - y_offset).with_z(floor_level + (height) + (height / 2)),
super_center.with_z(floor_level + (2 * height)),
(length - 1) as f32,
)
.fill(brick.clone());
if s == (storeys - 1) {
for dir in LOCALITY {
let cone_pos = super_center + (dir * 2);
let cone_var =
4 + RandomField::new(0).get(cone_pos.with_z(base)) as i32 % 4;
painter
.cone_with_radius(
cone_pos.with_z(floor_level + (2 * height) + 5),
(length / 2) as f32,
(length + cone_var) as f32,
)
.fill(brick.clone());
}
}
// center tube with rooms
if n == tubes as i32 {
// ground_entries
if ground_entries < 1 && floor_level > (base - 6) {
for dir in CARDINALS {
let entry_pos_inner = plot_center + (dir * (2 * length) - 4);
let entry_pos_outer = plot_center + (dir * (3 * length) + 4);
painter
.line(
entry_pos_inner.with_z(floor_level + 6),
entry_pos_outer.with_z(base + 35),
6.0,
)
.clear();
}
let door_start = plot_center + door_dir * ((3 * (length / 2)) + 1);
painter
.line(
door_start.with_z(floor_level + 2),
self.door_tile.with_z(base),
4.0,
)
.fill(wood.clone());
painter
.line(
door_start.with_z(floor_level + 7),
self.door_tile.with_z(base + 6),
7.0,
)
.clear();
ground_entries += 1;
}
painter
.cubic_bezier(
plot_center.with_z(floor_level + (height / 2)),
(plot_center - x_offset).with_z(floor_level + height),
(plot_center - y_offset).with_z(floor_level + (height) + (height / 2)),
plot_center.with_z(floor_level + (2 * height)),
(length + 2) as f32,
)
.fill(brick.clone());
// platform
if s == (storeys - 1) {
let limit_up = painter.aabb(Aabb {
min: (plot_center - platform_length - 2).with_z(floor_level - 4),
max: (plot_center + platform_length + 2).with_z(floor_level + 1),
});
painter
.superquadric(
Aabb {
min: (plot_center - platform_length - 2)
.with_z(floor_level - 4),
max: (plot_center + platform_length + 2)
.with_z(floor_level + 6),
},
4.0,
)
.intersect(limit_up)
.fill(wood.clone());
// lanterns & cargo
for dir in NEIGHBORS {
let lantern_pos = plot_center + (dir * (platform_length - 6));
painter.sprite(
lantern_pos.with_z(floor_level + 1),
SpriteKind::StreetLamp,
);
}
for dir in DIAGONALS {
let cargo_pos = plot_center + (dir * (2 * length));
for dir in CARDINALS {
let sprite_pos = cargo_pos + dir;
let rows =
(RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
for r in 0..rows {
painter
.aabb(Aabb {
min: (sprite_pos).with_z(floor_level + 1 + r),
max: (sprite_pos + 1).with_z(floor_level + 2 + r),
})
.fill(Fill::Block(Block::air(
match (RandomField::new(0)
.get(sprite_pos.with_z(base + r))
% 2)
as i32
{
0 => SpriteKind::Barrel,
_ => SpriteKind::CrateBlock,
},
)));
if r > 0 {
painter.owned_resource_sprite(
sprite_pos.with_z(floor_level + 2 + r),
SpriteKind::Crate,
0,
);
}
}
}
}
for dir in CARDINALS {
// docks
let dock_pos = plot_center + (dir * platform_length);
painter
.cylinder(Aabb {
min: (dock_pos - 8).with_z(floor_level),
max: (dock_pos + 8).with_z(floor_level + 1),
})
.fill(wood.clone());
painter
.cylinder(Aabb {
min: (dock_pos - 7).with_z(floor_level - 1),
max: (dock_pos + 7).with_z(floor_level),
})
.fill(wood.clone());
}
// campfire
let campfire_pos =
Vec2::new(plot_center.x - platform_length - 2, plot_center.y)
.with_z(floor_level);
painter.spawn(
EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
.into_special(SpecialEntity::Waypoint),
);
}
// clear rooms and entries & decor
if floor_level > (base - 6) {
// decor
painter
.line(
Vec2::new(plot_center.x, plot_center.y - length)
.with_z(floor_level + 5),
Vec2::new(plot_center.x, plot_center.y + length)
.with_z(floor_level + 5),
4.0,
)
.fill(color.clone());
painter
.line(
Vec2::new(plot_center.x - length, plot_center.y)
.with_z(floor_level + 5),
Vec2::new(plot_center.x + length, plot_center.y)
.with_z(floor_level + 5),
4.0,
)
.fill(color.clone());
// entries
painter
.line(
Vec2::new(plot_center.x, plot_center.y - (2 * length) - 4)
.with_z(floor_level + 4),
Vec2::new(plot_center.x, plot_center.y + (2 * length) + 4)
.with_z(floor_level + 4),
4.0,
)
.clear();
painter
.line(
Vec2::new(plot_center.x - (2 * length) - 4, plot_center.y)
.with_z(floor_level + 4),
Vec2::new(plot_center.x + (2 * length) + 4, plot_center.y)
.with_z(floor_level + 4),
4.0,
)
.clear();
painter
.superquadric(
Aabb {
min: (plot_center - length - 1).with_z(floor_level),
max: (plot_center + length + 1)
.with_z(floor_level + height - 4),
},
sq_type,
)
.clear();
// room floor
painter
.cylinder(Aabb {
min: (plot_center - length - 3).with_z(floor_level),
max: (plot_center + length + 3).with_z(floor_level + 1),
})
.fill(brick.clone());
painter
.cylinder(Aabb {
min: (plot_center - length + 1).with_z(floor_level),
max: (plot_center + length - 1).with_z(floor_level + 1),
})
.fill(color.clone());
painter
.cylinder(Aabb {
min: (plot_center - length + 2).with_z(floor_level),
max: (plot_center + length - 2).with_z(floor_level + 1),
})
.fill(brick.clone());
// entry sprites
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x - 3, plot_center.y + length)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x + 4, plot_center.y + length + 1)
.with_z(floor_level + 7),
})
.fill(window2.clone());
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x - 2, plot_center.y + length)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x + 3, plot_center.y + length + 1)
.with_z(floor_level + 7),
})
.clear();
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x - 3, plot_center.y - length - 1)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x + 4, plot_center.y - length)
.with_z(floor_level + 7),
})
.fill(window2.clone());
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x - 2, plot_center.y - length - 1)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x + 3, plot_center.y - length)
.with_z(floor_level + 7),
})
.clear();
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x + length, plot_center.y - 3)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x + length + 1, plot_center.y + 4)
.with_z(floor_level + 7),
})
.fill(window.clone());
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x + length, plot_center.y - 2)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x + length + 1, plot_center.y + 3)
.with_z(floor_level + 7),
})
.clear();
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x - length - 1, plot_center.y - 3)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x - length, plot_center.y + 4)
.with_z(floor_level + 7),
})
.fill(window.clone());
painter
.aabb(Aabb {
min: Vec2::new(plot_center.x - length - 1, plot_center.y - 2)
.with_z(floor_level + 2),
max: Vec2::new(plot_center.x - length, plot_center.y + 3)
.with_z(floor_level + 7),
})
.clear();
// cargo in rooms
for dir in DIAGONALS {
let cargo_pos = plot_center + (dir * (length / 2));
for dir in CARDINALS {
let sprite_pos = cargo_pos + dir;
let rows =
(RandomField::new(0).get(sprite_pos.with_z(base)) % 4) as i32;
for r in 0..rows {
painter
.aabb(Aabb {
min: (sprite_pos).with_z(floor_level + 1 + r),
max: (sprite_pos + 1).with_z(floor_level + 2 + r),
})
.fill(Fill::Block(Block::air(
match (RandomField::new(0)
.get(sprite_pos.with_z(base + r))
% 2)
as i32
{
0 => SpriteKind::Barrel,
_ => SpriteKind::CrateBlock,
},
)));
}
}
}
// wall lamps
let corner_pos_1 = Vec2::new(plot_center.x - length, plot_center.y - 5);
let corner_pos_2 = Vec2::new(plot_center.x - 5, plot_center.y - length);
for dir in SQUARE_4 {
let lamp_pos_1 = Vec2::new(
corner_pos_1.x + (dir.x * ((2 * length) - 1)),
corner_pos_1.y + (dir.y * 10),
)
.with_z(floor_level + 7);
painter.rotated_sprite(
lamp_pos_1,
SpriteKind::WallLampMesa,
(2 + (4 * dir.x)) as u8,
);
let lamp_pos_2 = Vec2::new(
corner_pos_2.x + (dir.x * 10),
corner_pos_2.y + (dir.y * ((2 * length) - 1)),
)
.with_z(floor_level + 7);
painter.rotated_sprite(
lamp_pos_2,
SpriteKind::WallLampMesa,
(4 - (4 * dir.y)) as u8,
);
}
}
// stairs
if floor_level > (base + 8) {
let stairs_level = floor_level + 1;
let stairs_start = plot_center + door_dir * ((2 * length) - 7);
let mid_dir = if door_dir.x != 0 {
door_dir.x
} else {
door_dir.y
};
let stairs_mid = Vec2::new(
plot_center.x + mid_dir * (3 * (length / 2)),
plot_center.y + mid_dir * (3 * (length / 2)),
);
let stairs_end = Vec2::new(
plot_center.x + door_dir.y * ((2 * length) - 7),
plot_center.y + door_dir.x * ((2 * length) - 7),
);
let rope_pos = Vec2::new(
plot_center.x + mid_dir * ((3 * (length / 2)) + 2),
plot_center.y + mid_dir * ((3 * (length / 2)) + 2),
);
painter
.cylinder(Aabb {
min: (stairs_start - 6).with_z(stairs_level - 1),
max: (stairs_start + 6).with_z(stairs_level),
})
.fill(wood.clone());
painter
.cylinder(Aabb {
min: (stairs_mid - 6).with_z(stairs_level - (height / 2) - 1),
max: (stairs_mid + 6).with_z(stairs_level - (height / 2)),
})
.fill(wood.clone());
painter
.cylinder(Aabb {
min: (stairs_end - 6).with_z(stairs_level - height - 1),
max: (stairs_end + 6).with_z(stairs_level - height),
})
.fill(wood.clone());
for n in 0..2 {
let stairs = painter
.line(
stairs_start.with_z(stairs_level + (n * 2)),
stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
4.0 + (n as f32 / 2.0),
)
.union(painter.line(
stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
stairs_end.with_z(stairs_level - height + (n * 2)),
4.0 + (n as f32 / 2.0),
));
match n {
0 => stairs.fill(wood.clone()),
_ => stairs.clear(),
};
}
painter
.line(
rope_pos.with_z(stairs_level + (height / 2) - 3),
(plot_center - (length / 2))
.with_z(stairs_level + (height / 2) + 2),
1.5,
)
.fill(wood.clone());
painter
.aabb(Aabb {
min: rope_pos.with_z(stairs_level - (height / 2) - 1),
max: (rope_pos + 1).with_z(stairs_level + (height / 2) - 3),
})
.fill(rope.clone());
}
}
// vary next storey
length += -1;
width += -1;
height += -1;
floor_level += height;
mem::swap(&mut length, &mut width);
}
}
for dock_pos in &self.docking_positions {
painter.rotated_sprite_with_cfg(
*dock_pos,
SpriteKind::Sign,
Dir::from_vec2(dock_pos.xy() - self.center).sprite_ori(),
SpriteCfg {
unlock: None,
content: Some(Content::localized("common-signs-airship_dock")),
},
);
}
}
}

View File

@ -0,0 +1,294 @@
use super::*;
use crate::{
site2::gen::place_circular,
util::{RandomField, Sampler, CARDINALS},
Land,
};
use common::{
comp::Content,
generation::SpecialEntity,
terrain::{BlockKind, SpriteCfg, SpriteKind},
};
use rand::prelude::*;
use std::sync::Arc;
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct CoastalAirshipDock {
/// Tile position of the door tile
pub door_tile: Vec2<i32>,
/// Approximate altitude of the door tile
pub(crate) alt: i32,
base: i32,
center: Vec2<i32>,
size: i32,
bldg_height: i32,
diameter: i32,
pub docking_positions: Vec<Vec3<i32>>,
}
impl CoastalAirshipDock {
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),
};
let diameter = (bounds.max.x - bounds.min.x).min(bounds.max.y - bounds.min.y);
let alt = land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2;
let size = (diameter / 3) + 2;
let bldg_height = 12;
let base = alt + 1;
let center = bounds.center();
let mut docking_positions = vec![];
let top_floor = base + (bldg_height * 6) - 3;
for dir in CARDINALS {
let docking_pos = center + dir * size;
docking_positions.push(docking_pos.with_z(top_floor));
}
Self {
door_tile: door_tile_pos,
alt,
base,
center,
size,
bldg_height,
diameter,
docking_positions,
}
}
}
impl Structure for CoastalAirshipDock {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_coastal_airship_dock\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_coastal_airship_dock")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.base;
let center = self.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 = self.diameter / 2;
let width = (self.diameter / 2) - 1;
let height = 15;
// fence, blue gates
painter
.aabb(Aabb {
min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
})
.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 - 2),
})
.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();
}
// rooms
let size = self.size;
let room_offset = size / 6;
let bldg_height = self.bldg_height;
for r in 0..=4 {
let bldg_size = size - (room_offset * r);
let bldg_base = base + ((bldg_height + 2) * r);
if r == 4 {
painter
.cylinder(Aabb {
min: (center - bldg_size - 2).with_z(bldg_base + bldg_height - 1),
max: (center + bldg_size + 2).with_z(bldg_base + bldg_height),
})
.fill(white.clone());
painter
.cylinder(Aabb {
min: (center - bldg_size - 2).with_z(bldg_base + bldg_height),
max: (center + bldg_size + 2).with_z(bldg_base + bldg_height + 1),
})
.fill(blue_broken.clone());
painter
.cylinder(Aabb {
min: (center - bldg_size - 1).with_z(bldg_base + bldg_height),
max: (center + bldg_size + 1).with_z(bldg_base + bldg_height + 1),
})
.clear();
let cargo_pos = Vec2::new(center.x, center.y + 5);
for dir in CARDINALS {
let sprite_pos = cargo_pos + dir;
let rows = 1 + (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
for r in 0..rows {
painter
.aabb(Aabb {
min: (sprite_pos).with_z(bldg_base + bldg_height + r),
max: (sprite_pos + 1).with_z(bldg_base + bldg_height + 1 + r),
})
.fill(Fill::Block(Block::air(
match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
as i32
{
0 => SpriteKind::Barrel,
_ => SpriteKind::CrateBlock,
},
)));
if r > 1 {
painter.owned_resource_sprite(
sprite_pos.with_z(bldg_base + bldg_height + 1 + r),
SpriteKind::Crate,
0,
);
}
}
// docks
let gangway_pos = center + dir * (size / 2);
let dock_pos = center + dir * (size - 3);
painter
.aabb(Aabb {
min: (gangway_pos - 3).with_z(bldg_base + bldg_height - 1),
max: (gangway_pos + 3).with_z(bldg_base + bldg_height),
})
.fill(white.clone());
painter
.cylinder(Aabb {
min: (dock_pos - 4).with_z(bldg_base + bldg_height),
max: (dock_pos + 4).with_z(bldg_base + bldg_height + 1),
})
.fill(blue_broken.clone());
painter
.cylinder(Aabb {
min: (dock_pos - 3).with_z(bldg_base + bldg_height - 1),
max: (dock_pos + 3).with_z(bldg_base + bldg_height + 1),
})
.fill(white.clone());
}
// campfire
let campfire_pos = center.with_z(bldg_base + bldg_height);
painter.spawn(
EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
.into_special(SpecialEntity::Waypoint),
);
}
painter
.cylinder(Aabb {
min: (center - bldg_size).with_z(bldg_base - 2),
max: (center + bldg_size).with_z(bldg_base + bldg_height),
})
.fill(white.clone());
}
for r in 0..=4 {
let bldg_size = size - (room_offset * r);
let bldg_base = base + ((bldg_height + 2) * r);
let step_positions = place_circular(center, (bldg_size - 1) as f32, 14);
for (s, step_pos) in step_positions.enumerate() {
let step_size = (size / 3) - r;
painter
.cylinder(Aabb {
min: (step_pos - step_size).with_z(bldg_base - 2 + s as i32),
max: (step_pos + step_size).with_z(bldg_base + 4 + s as i32),
})
.clear();
painter
.cylinder(Aabb {
min: (step_pos - step_size).with_z(bldg_base - 3 + s as i32),
max: (step_pos + step_size).with_z(bldg_base - 2 + s as i32),
})
.fill(blue_broken.clone());
painter
.cylinder(Aabb {
min: (step_pos - step_size + 1).with_z(bldg_base - 4 + s as i32),
max: (step_pos + step_size - 1).with_z(bldg_base - 2 + s as i32),
})
.fill(white.clone());
}
let lamp_positions = place_circular(center, (bldg_size + 1) as f32, 14);
for (l, lamp_pos) in lamp_positions.enumerate() {
if (RandomField::new(0).get(lamp_pos.with_z(base)) % 4) < 1 {
painter
.aabb(Aabb {
min: (lamp_pos - 1).with_z(bldg_base - 3 + l as i32),
max: (lamp_pos + 1).with_z(bldg_base - 2 + l as i32),
})
.fill(blue_broken.clone());
painter.sprite(
lamp_pos.with_z(bldg_base - 2 + l as i32),
SpriteKind::FireBowlGround,
);
}
}
}
for dock_pos in &self.docking_positions {
painter.rotated_sprite_with_cfg(
*dock_pos,
SpriteKind::Sign,
Dir::from_vec2(dock_pos.xy() - self.center).sprite_ori(),
SpriteCfg {
unlock: None,
content: Some(Content::localized("common-signs-airship_dock")),
},
);
}
}
}

View File

@ -0,0 +1,404 @@
use super::*;
use crate::{
util::{RandomField, Sampler, DIAGONALS},
Land,
};
use common::{
comp::Content,
generation::SpecialEntity,
terrain::{BlockKind, SpriteCfg, SpriteKind},
};
use rand::prelude::*;
use std::sync::Arc;
use vek::*;
/// Represents house data generated by the `generate()` method
pub struct DesertCityAirshipDock {
/// 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,
pub docking_positions: Vec<Vec3<i32>>,
center: Vec2<i32>,
base: i32,
length: i32,
height: i32,
floors: i32,
}
impl DesertCityAirshipDock {
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),
};
let alt = land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32;
let center = bounds.center();
let length = 14;
let height = 2 * (length / 3);
let floors = 4;
let mut docking_positions = vec![];
let base = alt + 1;
let top_floor = base + 5 + (height * (floors + 1));
for dir in CARDINALS {
let docking_pos = center + dir * (length * 2);
docking_positions.push(docking_pos.with_z(top_floor));
}
Self {
bounds,
door_tile: door_tile_pos,
alt,
docking_positions,
center,
base,
length,
height,
floors,
}
}
}
impl Structure for DesertCityAirshipDock {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_desertcityairshipdock\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_desertcityairshipdock")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let sandstone = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 37 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
_ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
})
}));
let sandstone_broken = Fill::Sampling(Arc::new(|center| {
Some(match (RandomField::new(0).get(center)) % 42 {
0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
_ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
})
}));
let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12);
let base = self.base;
let center = self.center;
// Fence
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.min.x + 2, self.bounds.max.y).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
.aabb(Aabb {
min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
})
.clear();
painter
.aabb(Aabb {
min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
})
.clear();
// Foundation
painter
.aabb(Aabb {
min: (self.bounds.min + 1).with_z(base - 20),
max: (self.bounds.max).with_z(base),
})
.fill(sandstone.clone());
// rooms
let length = self.length;
let height = self.height;
let floors = self.floors;
let carve = length / 4;
for f in 0..=floors {
let bldg_base = base + f * (height + 1);
let bldg_length = length;
// room
painter
.aabb(Aabb {
min: (center - bldg_length).with_z(bldg_base),
max: (center + bldg_length).with_z(bldg_base + height),
})
.fill(sandstone.clone());
// roof floor
painter
.aabb(Aabb {
min: (center - bldg_length - 1 - f).with_z(bldg_base + height),
max: (center + bldg_length + 1 + f).with_z(bldg_base + height + 1),
})
.fill(wood.clone());
painter
.aabb(Aabb {
min: (center - bldg_length - f).with_z(bldg_base + height),
max: (center + bldg_length + f).with_z(bldg_base + height + 1),
})
.fill(sandstone.clone());
// clear room
painter
.aabb(Aabb {
min: (center - bldg_length + 1).with_z(bldg_base),
max: (center + bldg_length - 1).with_z(bldg_base + height - 1),
})
.clear();
let clear_limit_1 = painter.aabb(Aabb {
min: Vec2::new(center.x - bldg_length, center.y - bldg_length + 1)
.with_z(bldg_base),
max: Vec2::new(center.x + bldg_length, center.y + bldg_length - 1)
.with_z(bldg_base + height),
});
let clear_limit_2 = painter.aabb(Aabb {
min: Vec2::new(center.x - bldg_length + 1, center.y - bldg_length)
.with_z(bldg_base),
max: Vec2::new(center.x + bldg_length - 1, center.y + bldg_length)
.with_z(bldg_base + height),
});
for c in 0..=4 {
let space = c * ((2 * carve) + 1);
painter
.vault(
Aabb {
min: Vec2::new(
center.x - bldg_length,
center.y + space - bldg_length - carve + 1,
)
.with_z(bldg_base + (height / 2)),
max: Vec2::new(
center.x + bldg_length,
center.y + space - bldg_length + carve - 1,
)
.with_z(bldg_base + height - 1),
},
Dir::X,
)
.intersect(clear_limit_1)
.clear();
painter
.vault(
Aabb {
min: Vec2::new(
center.x + space - bldg_length - carve + 1,
center.y - bldg_length,
)
.with_z(bldg_base + (height / 2)),
max: Vec2::new(
center.x + space - bldg_length + carve - 1,
center.y + bldg_length,
)
.with_z(bldg_base + height - 1),
},
Dir::Y,
)
.intersect(clear_limit_2)
.clear();
painter
.aabb(Aabb {
min: Vec2::new(
center.x - bldg_length,
center.y + space - bldg_length - carve,
)
.with_z(bldg_base),
max: Vec2::new(
center.x + bldg_length,
center.y + space - bldg_length + carve,
)
.with_z(bldg_base + (height / 2)),
})
.intersect(clear_limit_1)
.clear();
painter
.aabb(Aabb {
min: Vec2::new(
center.x + space - bldg_length - carve,
center.y - bldg_length,
)
.with_z(bldg_base),
max: Vec2::new(
center.x + space - bldg_length + carve,
center.y + bldg_length,
)
.with_z(bldg_base + (height / 2)),
})
.intersect(clear_limit_2)
.clear();
}
for dir in DIAGONALS {
let clear_pos = center + dir * bldg_length;
painter
.aabb(Aabb {
min: (clear_pos - 1).with_z(bldg_base),
max: (clear_pos + 1).with_z(bldg_base + height - 1),
})
.clear();
}
for dir in DIAGONALS {
let lamp_pos = center + dir * (bldg_length - 1);
painter.sprite(lamp_pos.with_z(bldg_base), SpriteKind::StreetLamp);
if f == 4 {
let lamp_pos = center + dir * bldg_length;
painter.sprite(
lamp_pos.with_z(bldg_base + height + 1),
SpriteKind::StreetLamp,
);
let cargo_pos = center + (dir * ((bldg_length / 2) + 1));
for dir in CARDINALS {
let sprite_pos = cargo_pos + dir;
let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
for r in 0..rows {
painter
.aabb(Aabb {
min: (sprite_pos).with_z(bldg_base + height + 1 + r),
max: (sprite_pos + 1).with_z(bldg_base + height + 2 + r),
})
.fill(Fill::Block(Block::air(
match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
as i32
{
0 => SpriteKind::Barrel,
_ => SpriteKind::CrateBlock,
},
)));
if r > 0 {
painter.owned_resource_sprite(
sprite_pos.with_z(bldg_base + height + 2 + r),
SpriteKind::Crate,
0,
);
}
}
}
}
}
// docks
if f == 4 {
for dir in CARDINALS {
let gangway_pos_1 = center + dir * (3 * (bldg_length / 2));
let gangway_pos_2 = center + dir * ((3 * (bldg_length / 2)) - 4);
let dock_pos = center + dir * ((bldg_length * 2) - 3);
painter
.aabb(Aabb {
min: (gangway_pos_2 - 3).with_z(bldg_base + height - 1),
max: (gangway_pos_2 + 3).with_z(bldg_base + height),
})
.fill(wood.clone());
painter
.aabb(Aabb {
min: (gangway_pos_1 - 3).with_z(bldg_base + height),
max: (gangway_pos_1 + 3).with_z(bldg_base + height + 1),
})
.fill(wood.clone());
painter
.cylinder(Aabb {
min: (dock_pos - 4).with_z(bldg_base + height),
max: (dock_pos + 4).with_z(bldg_base + height + 1),
})
.fill(wood.clone());
painter
.cylinder(Aabb {
min: (dock_pos - 3).with_z(bldg_base + height - 1),
max: (dock_pos + 3).with_z(bldg_base + height),
})
.fill(wood.clone());
}
// campfire
painter.spawn(
EntityInfo::at(
Vec2::new(center.x + bldg_length - 2, center.y)
.with_z(bldg_base + height + 1)
.map(|e| e as f32 + 0.5),
)
.into_special(SpecialEntity::Waypoint),
);
}
for dock_pos in &self.docking_positions {
painter.rotated_sprite_with_cfg(
*dock_pos,
SpriteKind::Sign,
Dir::from_vec2(dock_pos.xy() - self.center).sprite_ori(),
SpriteCfg {
unlock: None,
content: Some(Content::localized("common-signs-airship_dock")),
},
);
}
// stairs
painter
.aabb(Aabb {
min: (center - (bldg_length / 2) - 1).with_z(bldg_base + height - 1),
max: (center + (bldg_length / 2) + 1).with_z(bldg_base + height + 1),
})
.fill(wood.clone());
painter
.aabb(Aabb {
min: (center - (bldg_length / 2)).with_z(bldg_base + height - 1),
max: (center + (bldg_length / 2)).with_z(bldg_base + height + 1),
})
.clear();
for w in 0..((bldg_length / 2) + 2) {
painter
.aabb(Aabb {
min: Vec2::new(
center.x - (bldg_length / 2) - 2 + (2 * w),
center.y - (bldg_length / 2),
)
.with_z(bldg_base + w),
max: Vec2::new(
center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
center.y + (bldg_length / 2),
)
.with_z(bldg_base + 1 + w),
})
.fill(wood.clone());
painter
.aabb(Aabb {
min: Vec2::new(
center.x - (bldg_length / 2) - 2 + (2 * w),
center.y - (bldg_length / 2),
)
.with_z(bldg_base - 1 + w),
max: Vec2::new(
center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
center.y + (bldg_length / 2),
)
.with_z(bldg_base + w),
})
.fill(sandstone.clone());
}
}
}
}

View File

@ -5,9 +5,12 @@ mod camp;
mod castle;
mod citadel;
mod cliff_tower;
mod cliff_town_airship_dock;
mod coastal_airship_dock;
mod coastal_house;
mod coastal_workshop;
mod cultist;
mod desert_city_airship_dock;
mod desert_city_arena;
mod desert_city_multiplot;
mod desert_city_temple;
@ -40,8 +43,10 @@ mod workshop;
pub use self::{
adlet::AdletStronghold, airship_dock::AirshipDock, bridge::Bridge, camp::Camp, castle::Castle,
citadel::Citadel, cliff_tower::CliffTower, coastal_house::CoastalHouse,
coastal_workshop::CoastalWorkshop, cultist::Cultist, desert_city_arena::DesertCityArena,
citadel::Citadel, cliff_tower::CliffTower, cliff_town_airship_dock::CliffTownAirshipDock,
coastal_airship_dock::CoastalAirshipDock, coastal_house::CoastalHouse,
coastal_workshop::CoastalWorkshop, cultist::Cultist,
desert_city_airship_dock::DesertCityAirshipDock, desert_city_arena::DesertCityArena,
desert_city_multiplot::DesertCityMultiPlot, desert_city_temple::DesertCityTemple,
dwarven_mine::DwarvenMine, farm_field::FarmField, giant_tree::GiantTree,
glider_finish::GliderFinish, glider_platform::GliderPlatform, glider_ring::GliderRing,
@ -98,12 +103,14 @@ pub enum PlotKind {
GliderPlatform(GliderPlatform),
GliderFinish(GliderFinish),
Tavern(Tavern),
CoastalAirshipDock(CoastalAirshipDock),
CoastalHouse(CoastalHouse),
CoastalWorkshop(CoastalWorkshop),
Workshop(Workshop),
DesertCityMultiPlot(DesertCityMultiPlot),
DesertCityTemple(DesertCityTemple),
DesertCityArena(DesertCityArena),
DesertCityAirshipDock(DesertCityAirshipDock),
SeaChapel(SeaChapel),
JungleRuin(JungleRuin),
Plaza,
@ -115,6 +122,7 @@ pub enum PlotKind {
Haniwa(Haniwa),
GiantTree(GiantTree),
CliffTower(CliffTower),
CliffTownAirshipDock(CliffTownAirshipDock),
Sahagin(Sahagin),
Citadel(Citadel),
SavannahAirshipDock(SavannahAirshipDock),
@ -141,9 +149,11 @@ macro_rules! foreach_plot {
match $p {
PlotKind::House($x) => $y,
PlotKind::AirshipDock($x) => $y,
PlotKind::CoastalAirshipDock($x) => $y,
PlotKind::CoastalHouse($x) => $y,
PlotKind::CoastalWorkshop($x) => $y,
PlotKind::Workshop($x) => $y,
PlotKind::DesertCityAirshipDock($x) => $y,
PlotKind::DesertCityMultiPlot($x) => $y,
PlotKind::DesertCityTemple($x) => $y,
PlotKind::DesertCityArena($x) => $y,
@ -156,6 +166,7 @@ macro_rules! foreach_plot {
PlotKind::Adlet($x) => $y,
PlotKind::GiantTree($x) => $y,
PlotKind::CliffTower($x) => $y,
PlotKind::CliffTownAirshipDock($x) => $y,
PlotKind::Citadel($x) => $y,
PlotKind::SavannahAirshipDock($x) => $y,
PlotKind::SavannahHut($x) => $y,

View File

@ -4,7 +4,11 @@ use crate::{
util::{RandomField, Sampler, CARDINALS, DIAGONALS},
Land,
};
use common::terrain::{BlockKind, SpriteKind};
use common::{
comp::Content,
generation::SpecialEntity,
terrain::{BlockKind, SpriteCfg, SpriteKind},
};
use rand::prelude::*;
use std::{f32::consts::TAU, sync::Arc};
use vek::*;
@ -13,10 +17,12 @@ use vek::*;
pub struct SavannahAirshipDock {
/// 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,
center: Vec2<i32>,
length: i32,
platform_height: i32,
pub docking_positions: Vec<Vec3<i32>>,
}
impl SavannahAirshipDock {
@ -33,10 +39,25 @@ impl SavannahAirshipDock {
min: site.tile_wpos(tile_aabr.min),
max: site.tile_wpos(tile_aabr.max),
};
let center = bounds.center();
let alt = land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2;
let base = alt + 1;
let length = 18;
let platform_height = 40;
let mut docking_positions = vec![];
let top_floor = base + platform_height - 2;
for dir in CARDINALS {
let docking_pos = center + dir * (length + 5);
docking_positions.push(docking_pos.with_z(top_floor));
}
Self {
bounds,
door_tile: door_tile_pos,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2,
alt,
center,
length,
platform_height,
docking_positions,
}
}
}
@ -48,8 +69,7 @@ impl Structure for SavannahAirshipDock {
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_savannah_airship_dock")]
fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
let base = self.alt + 1;
let center = self.bounds.center();
let center = self.center;
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);
@ -64,10 +84,10 @@ impl Structure for SavannahAirshipDock {
_ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
})
}));
let length = 18;
let length = self.length;
let height = length / 2;
let platform_height = self.platform_height;
let storeys = 1;
let platform_height = 40;
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;
@ -123,6 +143,35 @@ impl Structure for SavannahAirshipDock {
max: (center + length + 1).with_z(base + platform_height - 2),
})
.fill(clay.clone());
// docks
for dir in CARDINALS {
let dock_pos = center + dir * (length + 2);
painter
.cylinder(Aabb {
min: (dock_pos - 5).with_z(base + platform_height - 3),
max: (dock_pos + 5).with_z(base + platform_height - 2),
})
.fill(color.clone());
painter
.cylinder(Aabb {
min: (dock_pos - 4).with_z(base + platform_height - 3),
max: (dock_pos + 4).with_z(base + platform_height - 2),
})
.fill(wood_dark.clone());
}
for dock_pos in &self.docking_positions {
painter.rotated_sprite_with_cfg(
*dock_pos,
SpriteKind::Sign,
Dir::from_vec2(dock_pos.xy() - self.center).sprite_ori(),
SpriteCfg {
unlock: None,
content: Some(Content::localized("common-signs-airship_dock")),
},
);
}
// lanterns, crates & barrels
for dir in CARDINALS {
let lantern_pos = center + (dir * length);
@ -160,6 +209,12 @@ impl Structure for SavannahAirshipDock {
}
}
}
// campfire
let campfire_pos = (center - (2 * (length / 3)) - 1).with_z(base + platform_height);
painter.spawn(
EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
.into_special(SpecialEntity::Waypoint),
);
for b in 0..2 {
let base = base + (b * platform_height);
let radius = radius - (b * (radius / 3));