diff --git a/assets/world/manifests/site_structures/pirate_hideout/pirate_hideout.ron b/assets/world/manifests/site_structures/pirate_hideout/pirate_hideout.ron new file mode 100644 index 0000000000..337df3ff9e --- /dev/null +++ b/assets/world/manifests/site_structures/pirate_hideout/pirate_hideout.ron @@ -0,0 +1,9 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.spots.pirate_hideout", + center: (32, 27, 5), + + ), +] diff --git a/assets/world/manifests/spots_general/pirate_hideout.ron b/assets/world/manifests/spots_general/pirate_hideout.ron deleted file mode 100644 index 90c3f8e05d..0000000000 --- a/assets/world/manifests/spots_general/pirate_hideout.ron +++ /dev/null @@ -1,16 +0,0 @@ -#![enable(unwrap_newtypes)] - -[ - ( - specifier: "world.structure.spots.pirate_hideout", - center: (32, 27, 5), - custom_indices: { - 12: Sprite(Cauldron), - 10: Sprite(WitchWindow), - 44: Filled(GlowingRock, (r: 54, g: 180, b: 64)), - 8: Filled(Air, (r: 255, g: 255, b: 255)), - 249: Sprite(PotionMinor), - 251: Sprite(Ember), - }, - ), -] diff --git a/common/src/terrain/site.rs b/common/src/terrain/site.rs index e857268f77..ac576be988 100644 --- a/common/src/terrain/site.rs +++ b/common/src/terrain/site.rs @@ -23,4 +23,5 @@ pub enum SettlementKindMeta { DesertCity, SavannahPit, CoastalTown, + PirateHideout, } diff --git a/rtsim/src/gen/site.rs b/rtsim/src/gen/site.rs index 1b1bf819a5..9433bbb8c6 100644 --- a/rtsim/src/gen/site.rs +++ b/rtsim/src/gen/site.rs @@ -31,6 +31,7 @@ impl Site { SiteKind::Dungeon(_) | SiteKind::ChapelSite(_) | SiteKind::Gnarling(_) + | SiteKind::PirateHideout(_) | SiteKind::Adlet(_) => Some(false), SiteKind::DwarvenMine(_) => Some(false), // Neutral diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index c8935d6b17..c77d4701c7 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -259,7 +259,7 @@ impl Civs { let world_dims = ctx.sim.get_aabr(); for _ in 0..initial_civ_count * 3 { attempt(5, || { - let (loc, kind) = match ctx.rng.gen_range(0..64) { + let (loc, kind) = match ctx.rng.gen_range(0..78) { 0..=5 => ( find_site_loc( &mut ctx, @@ -337,6 +337,16 @@ impl Civs { )?, SiteKind::DwarvenMine, ),*/ + 56..=68 => ( + find_site_loc( + &mut ctx, + &ProximityRequirementsBuilder::new() + .avoid_all_of(this.pirate_hideout_enemies(), 40) + .finalize(&world_dims), + &SiteKind::PirateHideout, + )?, + SiteKind::PirateHideout, + ), _ => ( find_site_loc( &mut ctx, @@ -382,6 +392,7 @@ impl Civs { SiteKind::Citadel => (16i32, 0.0), SiteKind::Bridge(_, _) => (0, 0.0), SiteKind::Adlet => (16i32, 0.0), + SiteKind::PirateHideout => (8i32, 3.0), //SiteKind::DwarvenMine => (8i32, 3.0), }; @@ -490,6 +501,13 @@ impl Civs { wpos, )) }, + SiteKind::PirateHideout => { + WorldSite::pirate_hideout(site2::Site::generate_pirate_hideout( + &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), ), @@ -1446,6 +1464,13 @@ impl Civs { } }) } + + fn pirate_hideout_enemies(&self) -> impl Iterator> + '_ { + self.sites().filter_map(|s| match s.kind { + SiteKind::Tree | SiteKind::GiantTree => None, + _ => Some(s.center), + }) + } } /// Attempt to find a path between two locations @@ -1775,6 +1800,7 @@ pub enum SiteKind { Citadel, Bridge(Vec2, Vec2), Adlet, + PirateHideout, //DwarvenMine, } @@ -1845,6 +1871,9 @@ impl SiteKind { (2.0..3.5).contains(&(chunk.water_alt - CONFIG.sea_level)) && suitable_for_town() }, + SiteKind::PirateHideout => { + (0.5..3.5).contains(&(chunk.water_alt - CONFIG.sea_level)) + }, SiteKind::DesertCity => { (0.9..1.0).contains(&chunk.temp) && !chunk.near_cliffs() && suitable_for_town() }, diff --git a/world/src/layer/spot.rs b/world/src/layer/spot.rs index 3dd9c683e3..017f1aaf53 100644 --- a/world/src/layer/spot.rs +++ b/world/src/layer/spot.rs @@ -42,7 +42,6 @@ pub enum Spot { MyrmidonTemple, GnarlingTotem, WitchHouse, - PirateHideout, GnomeSpring, WolfBurrow, Igloo, @@ -120,20 +119,6 @@ impl Spot { }, false, ); - Self::generate_spots( - Spot::PirateHideout, - world, - 1.3, - |g, c| { - g < 0.25 - && !c.near_cliffs() - && !c.river.near_water() - && !c.path.0.is_way() - && c.sites.is_empty() - && matches!(c.get_biome(), Forest | Jungle) - }, - false, - ); Self::generate_spots( Spot::SaurokAltar, world, @@ -624,15 +609,6 @@ pub fn apply_spots_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) { (2..3, "common.entity.dungeon.adlet.tracker"), ], }, - Spot::PirateHideout => SpotConfig { - base_structures: Some("spots_general.pirate_hideout"), - entity_radius: 70.0, - entities: &[ - (12..16, "common.entity.spot.pirate"), - (2..4, "common.entity.wild.peaceful.parrot"), - (4..6, "common.entity.wild.peaceful.rat"), - ], - }, Spot::GnarlingTotem => SpotConfig { base_structures: Some("site_structures.gnarling.totem"), entity_radius: 30.0, diff --git a/world/src/lib.rs b/world/src/lib.rs index 4a2ebbfb28..568825839c 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -157,13 +157,14 @@ impl World { .civs() .sites .iter() + .filter(|(_, site)| !matches!(&site.kind, civ::SiteKind::PirateHideout)) .map(|(_, site)| { world_msg::SiteInfo { id: site.site_tmp.map(|i| i.id()).unwrap_or_default(), 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::CoastalTown | 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 | civ::SiteKind::PirateHideout => 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), diff --git a/world/src/site/economy/context.rs b/world/src/site/economy/context.rs index a4027d937c..3d6d30772a 100644 --- a/world/src/site/economy/context.rs +++ b/world/src/site/economy/context.rs @@ -134,6 +134,7 @@ impl Environment { SiteKind::ChapelSite(_) => {}, SiteKind::DwarvenMine(_) => {}, SiteKind::Bridge(_) => {}, + SiteKind::PirateHideout(_) => {}, } } if towns.valid() { diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 4ba576f6bf..3ddee2f0dc 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -77,6 +77,7 @@ pub enum SiteKind { Gnarling(site2::Site), Bridge(site2::Site), Adlet(site2::Site), + PirateHideout(site2::Site), } impl Site { @@ -143,6 +144,13 @@ impl Site { } } + pub fn pirate_hideout(ph: site2::Site) -> Self { + Self { + kind: SiteKind::PirateHideout(ph), + economy: Economy::default(), + } + } + pub fn desert_city(dc: site2::Site) -> Self { Self { kind: SiteKind::DesertCity(dc), @@ -194,6 +202,7 @@ impl Site { SiteKind::CliffTown(ct) => ct.radius(), SiteKind::SavannahPit(sp) => sp.radius(), SiteKind::CoastalTown(ct) => ct.radius(), + SiteKind::PirateHideout(ph) => ph.radius(), SiteKind::DesertCity(dc) => dc.radius(), SiteKind::ChapelSite(p) => p.radius(), SiteKind::DwarvenMine(p) => p.radius(), @@ -214,6 +223,7 @@ impl Site { SiteKind::CliffTown(ct) => ct.origin, SiteKind::SavannahPit(sp) => sp.origin, SiteKind::CoastalTown(ct) => ct.origin, + SiteKind::PirateHideout(ph) => ph.origin, SiteKind::DesertCity(dc) => dc.origin, SiteKind::ChapelSite(p) => p.origin, SiteKind::DwarvenMine(p) => p.origin, @@ -234,6 +244,7 @@ impl Site { SiteKind::CliffTown(ct) => ct.spawn_rules(wpos), SiteKind::SavannahPit(sp) => sp.spawn_rules(wpos), SiteKind::CoastalTown(ct) => ct.spawn_rules(wpos), + SiteKind::PirateHideout(ph) => ph.spawn_rules(wpos), SiteKind::DesertCity(dc) => dc.spawn_rules(wpos), SiteKind::ChapelSite(p) => p.spawn_rules(wpos), SiteKind::DwarvenMine(p) => p.spawn_rules(wpos), @@ -254,6 +265,7 @@ impl Site { SiteKind::CliffTown(ct) => ct.name(), SiteKind::SavannahPit(sp) => sp.name(), SiteKind::CoastalTown(ct) => ct.name(), + SiteKind::PirateHideout(ph) => ph.name(), SiteKind::DesertCity(dc) => dc.name(), SiteKind::ChapelSite(p) => p.name(), SiteKind::DwarvenMine(p) => p.name(), @@ -275,6 +287,7 @@ impl Site { | SiteKind::CliffTown(_) | SiteKind::SavannahPit(_) | SiteKind::CoastalTown(_) + | SiteKind::PirateHideout(_) | SiteKind::DesertCity(_) => Some(common::trade::SiteInformation { id: site_id, unconsumed_stock: self.economy.get_available_stock(), @@ -294,6 +307,7 @@ impl Site { 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::PirateHideout(ph) => ph.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), @@ -327,6 +341,7 @@ impl Site { SiteKind::CliffTown(_) => {}, SiteKind::SavannahPit(_) => {}, SiteKind::CoastalTown(_) => {}, + SiteKind::PirateHideout(_) => {}, SiteKind::DesertCity(_) => {}, SiteKind::ChapelSite(p) => p.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::DwarvenMine(p) => p.apply_supplement(dynamic_rng, wpos2d, supplement), @@ -345,6 +360,7 @@ impl Site { | SiteKind::CliffTown(_) | SiteKind::SavannahPit(_) | SiteKind::CoastalTown(_) + | SiteKind::PirateHideout(_) | SiteKind::DesertCity(_) | SiteKind::Settlement(_) ) @@ -361,6 +377,7 @@ impl Site { SiteKind::CliffTown(site2) => Some(site2), SiteKind::SavannahPit(site2) => Some(site2), SiteKind::CoastalTown(site2) => Some(site2), + SiteKind::PirateHideout(site2) => Some(site2), SiteKind::Tree(_) => None, SiteKind::DesertCity(site2) => Some(site2), SiteKind::ChapelSite(site2) => Some(site2), @@ -386,6 +403,9 @@ impl SiteKind { SiteKind::CoastalTown(_) => { Some(SiteKindMeta::Settlement(SettlementKindMeta::CoastalTown)) }, + SiteKind::PirateHideout(_) => { + Some(SiteKindMeta::Settlement(SettlementKindMeta::PirateHideout)) + }, SiteKind::DesertCity(_) => { Some(SiteKindMeta::Settlement(SettlementKindMeta::DesertCity)) }, diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index fd7cf5c2d1..6b615b46f4 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -1261,6 +1261,38 @@ impl Site { site } + pub fn generate_pirate_hideout(land: &Land, rng: &mut impl Rng, origin: Vec2) -> Self { + let mut rng = reseed(rng); + let mut site = Site { + origin, + name: "".to_string(), + ..Site::default() + }; + let size = 8.0 as i32; + let aabr = Aabr { + min: Vec2::broadcast(-size), + max: Vec2::broadcast(size), + }; + { + let pirate_hideout = + plot::PirateHideout::generate(land, &mut reseed(&mut rng), &site, aabr); + let pirate_hideout_alt = pirate_hideout.alt; + let plot = site.create_plot(Plot { + kind: PlotKind::PirateHideout(pirate_hideout), + 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(pirate_hideout_alt), + }); + } + site + } + pub fn generate_bridge( land: &Land, index: IndexRef, @@ -1613,6 +1645,9 @@ impl Site { }, PlotKind::Citadel(citadel) => citadel.render_collect(self, canvas), PlotKind::Bridge(bridge) => bridge.render_collect(self, canvas), + PlotKind::PirateHideout(pirate_hideout) => { + pirate_hideout.render_collect(self, canvas) + }, PlotKind::Plaza | PlotKind::Road(_) => continue, // _ => continue, Avoid using a wildcard here!! }; diff --git a/world/src/site2/plot.rs b/world/src/site2/plot.rs index fa23283540..a1dd961c51 100644 --- a/world/src/site2/plot.rs +++ b/world/src/site2/plot.rs @@ -12,6 +12,7 @@ mod dwarven_mine; mod giant_tree; mod gnarling; mod house; +mod pirate_hideout; mod savannah_hut; mod savannah_pit; mod savannah_workshop; @@ -23,9 +24,9 @@ pub use self::{ 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, + gnarling::GnarlingFortification, house::House, pirate_hideout::PirateHideout, + savannah_hut::SavannahHut, savannah_pit::SavannahPit, savannah_workshop::SavannahWorkshop, + sea_chapel::SeaChapel, workshop::Workshop, }; use super::*; @@ -86,5 +87,6 @@ pub enum PlotKind { SavannahHut(SavannahHut), SavannahWorkshop(SavannahWorkshop), Bridge(Bridge), + PirateHideout(PirateHideout), //DwarvenMine(DwarvenMine), } diff --git a/world/src/site2/plot/pirate_hideout.rs b/world/src/site2/plot/pirate_hideout.rs new file mode 100644 index 0000000000..f361707962 --- /dev/null +++ b/world/src/site2/plot/pirate_hideout.rs @@ -0,0 +1,87 @@ +use super::*; +use crate::{ + assets::AssetHandle, + site2::gen::PrimitiveTransform, + util::{sampler::Sampler, RandomField}, + Land, +}; +use common::{ + generation::EntityInfo, + terrain::{Structure as PrefabStructure, StructuresGroup}, +}; +use lazy_static::lazy_static; +use rand::prelude::*; +use std::f32::consts::TAU; +use vek::*; + +pub struct PirateHideout { + bounds: Aabr, + pub(crate) alt: i32, +} +impl PirateHideout { + pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr) -> Self { + let bounds = Aabr { + min: site.tile_wpos(tile_aabr.min), + max: site.tile_wpos(tile_aabr.max), + }; + Self { + bounds, + alt: land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2)) + as i32 + + 2, + } + } +} + +impl Structure for PirateHideout { + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"render_pirate_hideout\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "render_pirate_hideout")] + fn render_inner(&self, _site: &Site, land: &Land, painter: &Painter) { + let center = self.bounds.center(); + let base = land.get_alt_approx(center) as i32; + let mut thread_rng = thread_rng(); + let model_pos = center.with_z(base - 2); + // model + lazy_static! { + pub static ref MODEL: AssetHandle = + PrefabStructure::load_group("site_structures.pirate_hideout.pirate_hideout"); + } + let rng = RandomField::new(0).get(model_pos) % 10; + let model = MODEL.read(); + let model = model[rng as usize % model.len()].clone(); + painter + .prim(Primitive::Prefab(Box::new(model.clone()))) + .translate(model_pos) + .fill(Fill::Prefab(Box::new(model), model_pos, rng)); + + // npcs + let npc_radius = 15; + let phi = TAU / 5.0; + for n in 1..=5 { + let npc_pos = Vec2::new( + center.x + (npc_radius as f32 * ((n as f32 * phi).cos())) as i32, + center.y + (npc_radius as f32 * ((n as f32 * phi).sin())) as i32, + ); + + match RandomField::new(0).get(npc_pos.with_z(base)) % 10 { + // rat + 0 => painter.spawn( + EntityInfo::at(npc_pos.with_z(base).as_()) + .with_asset_expect("common.entity.wild.peaceful.rat", &mut thread_rng), + ), + // parrot + 1 => painter.spawn( + EntityInfo::at(npc_pos.with_z(base).as_()) + .with_asset_expect("common.entity.wild.peaceful.parrot", &mut thread_rng), + ), + // pirates + _ => painter.spawn( + EntityInfo::at(npc_pos.with_z(base).as_()) + .with_asset_expect("common.entity.spot.pirate", &mut thread_rng), + ), + } + } + } +}