diff --git a/assets/world/manifests/site_structures/rock_circle/rock_circle.ron b/assets/world/manifests/site_structures/rock_circle/rock_circle.ron new file mode 100644 index 0000000000..939b7ba4d6 --- /dev/null +++ b/assets/world/manifests/site_structures/rock_circle/rock_circle.ron @@ -0,0 +1,8 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.natural.rock-circle", + center: (30, 30, 4), + ), +] diff --git a/rtsim/src/gen/site.rs b/rtsim/src/gen/site.rs index 4baaaf4d0d..b89cf1177d 100644 --- a/rtsim/src/gen/site.rs +++ b/rtsim/src/gen/site.rs @@ -33,6 +33,7 @@ impl Site { | SiteKind::Gnarling(_) | SiteKind::PirateHideout(_) | SiteKind::JungleRuin(_) + | SiteKind::RockCircle(_) | SiteKind::Adlet(_) => Some(false), SiteKind::DwarvenMine(_) => Some(false), // Neutral diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 5e871ac378..a8e8a6fae6 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -276,7 +276,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..79) { + let (loc, kind) = match ctx.rng.gen_range(0..85) { 0..=4 => { if index.features().site2_giant_trees { ( @@ -352,7 +352,17 @@ impl Civs { )?, SiteKind::JungleRuin, ), - /*43..=48 => ( + 43..=49 => ( + find_site_loc( + &mut ctx, + &ProximityRequirementsBuilder::new() + .avoid_all_of(this.rock_circle_enemies(), 40) + .finalize(&world_dims), + &SiteKind::RockCircle, + )?, + SiteKind::RockCircle, + ), + /*50..=55 => ( find_site_loc( &mut ctx, &ProximityRequirementsBuilder::new() @@ -362,7 +372,7 @@ impl Civs { )?, SiteKind::DwarvenMine, ), - 49..=54 => ( + 56..=61 => ( find_site_loc( &mut ctx, &ProximityRequirementsBuilder::new() @@ -373,7 +383,7 @@ impl Civs { )?, SiteKind::Castle, ), - 55..=60 => (SiteKind::Citadel, (&castle_enemies, 20)), + 62..=67 => (SiteKind::Citadel, (&castle_enemies, 20)), */ _ => ( find_site_loc( @@ -422,6 +432,7 @@ impl Civs { SiteKind::Bridge(_, _) => (0, 0.0), SiteKind::Adlet => (16i32, 0.0), SiteKind::PirateHideout => (8i32, 3.0), + SiteKind::RockCircle => (8i32, 3.0), //SiteKind::DwarvenMine => (8i32, 3.0), }; @@ -540,6 +551,9 @@ impl Civs { SiteKind::JungleRuin => WorldSite::jungle_ruin( site2::Site::generate_jungle_ruin(&Land::from_sim(ctx.sim), &mut rng, wpos), ), + SiteKind::RockCircle => WorldSite::rock_circle( + site2::Site::generate_rock_circle(&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), ), @@ -1510,6 +1524,13 @@ impl Civs { _ => Some(s.center), }) } + + fn rock_circle_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 @@ -1842,6 +1863,7 @@ pub enum SiteKind { Bridge(Vec2, Vec2), Adlet, PirateHideout, + RockCircle, //DwarvenMine, JungleRuin, } @@ -1922,6 +1944,7 @@ impl SiteKind { SiteKind::JungleRuin => { matches!(chunk.get_biome(), BiomeKind::Jungle) }, + SiteKind::RockCircle => !chunk.near_cliffs() && !chunk.river.near_water(), 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 21e251484a..47eced614c 100644 --- a/world/src/layer/spot.rs +++ b/world/src/layer/spot.rs @@ -38,7 +38,6 @@ use vek::*; pub enum Spot { DwarvenGrave, SaurokAltar, - RockCircle, MyrmidonTemple, GnarlingTotem, WitchHouse, @@ -175,19 +174,6 @@ impl Spot { }, false, ); - Self::generate_spots( - Spot::RockCircle, - world, - 0.5, - |g, c| { - g < 0.1 - && !c.near_cliffs() - && !c.river.near_water() - && !c.path.0.is_way() - && c.sites.is_empty() - }, - false, - ); Self::generate_spots( Spot::MyrmidonTemple, world, @@ -573,11 +559,6 @@ pub fn apply_spots_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) { (2..8, "common.entity.wild.aggressive.mighty_saurok"), ], }, - Spot::RockCircle => SpotConfig { - base_structures: Some("spots.rock-circle"), - entity_radius: 20.0, - entities: &[(-8..2, "common.entity.wild.aggressive.dullahan")], - }, Spot::MyrmidonTemple => SpotConfig { base_structures: Some("spots.myrmidon-temple"), entity_radius: 10.0, diff --git a/world/src/lib.rs b/world/src/lib.rs index 56341923a6..af2d97d1d7 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -176,14 +176,14 @@ impl World { .civs() .sites .iter() - .filter(|(_, site)| !matches!(&site.kind, civ::SiteKind::PirateHideout | civ::SiteKind::JungleRuin)) + .filter(|(_, site)| !matches!(&site.kind, civ::SiteKind::PirateHideout | civ::SiteKind::JungleRuin | civ::SiteKind::RockCircle)) .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 | civ::SiteKind::PirateHideout | civ::SiteKind::JungleRuin => 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 | civ::SiteKind::JungleRuin | civ::SiteKind::RockCircle => 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 2ea989759f..14220ef18b 100644 --- a/world/src/site/economy/context.rs +++ b/world/src/site/economy/context.rs @@ -136,6 +136,7 @@ impl Environment { SiteKind::DwarvenMine(_) => {}, SiteKind::Bridge(_) => {}, SiteKind::PirateHideout(_) => {}, + SiteKind::RockCircle(_) => {}, } } if towns.valid() { diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 7418d985a4..a2a4c01fc4 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -79,6 +79,7 @@ pub enum SiteKind { Adlet(site2::Site), PirateHideout(site2::Site), JungleRuin(site2::Site), + RockCircle(site2::Site), } impl Site { @@ -159,6 +160,13 @@ impl Site { } } + pub fn rock_circle(rc: site2::Site) -> Self { + Self { + kind: SiteKind::RockCircle(rc), + economy: Economy::default(), + } + } + pub fn desert_city(dc: site2::Site) -> Self { Self { kind: SiteKind::DesertCity(dc), @@ -212,6 +220,7 @@ impl Site { SiteKind::CoastalTown(ct) => ct.radius(), SiteKind::PirateHideout(ph) => ph.radius(), SiteKind::JungleRuin(jr) => jr.radius(), + SiteKind::RockCircle(rc) => rc.radius(), SiteKind::DesertCity(dc) => dc.radius(), SiteKind::ChapelSite(p) => p.radius(), SiteKind::DwarvenMine(dm) => dm.radius(), @@ -234,6 +243,7 @@ impl Site { SiteKind::CoastalTown(ct) => ct.origin, SiteKind::PirateHideout(ph) => ph.origin, SiteKind::JungleRuin(jr) => jr.origin, + SiteKind::RockCircle(rc) => rc.origin, SiteKind::DesertCity(dc) => dc.origin, SiteKind::ChapelSite(p) => p.origin, SiteKind::DwarvenMine(dm) => dm.origin, @@ -256,6 +266,7 @@ impl Site { SiteKind::CoastalTown(ct) => ct.spawn_rules(wpos), SiteKind::PirateHideout(ph) => ph.spawn_rules(wpos), SiteKind::JungleRuin(jr) => jr.spawn_rules(wpos), + SiteKind::RockCircle(rc) => rc.spawn_rules(wpos), SiteKind::DesertCity(dc) => dc.spawn_rules(wpos), SiteKind::ChapelSite(p) => p.spawn_rules(wpos), SiteKind::DwarvenMine(dm) => dm.spawn_rules(wpos), @@ -278,6 +289,7 @@ impl Site { SiteKind::CoastalTown(ct) => ct.name(), SiteKind::PirateHideout(ph) => ph.name(), SiteKind::JungleRuin(jr) => jr.name(), + SiteKind::RockCircle(rc) => rc.name(), SiteKind::DesertCity(dc) => dc.name(), SiteKind::ChapelSite(p) => p.name(), SiteKind::DwarvenMine(dm) => dm.name(), @@ -320,6 +332,7 @@ impl Site { SiteKind::CoastalTown(ct) => ct.render(canvas, dynamic_rng), SiteKind::PirateHideout(ph) => ph.render(canvas, dynamic_rng), SiteKind::JungleRuin(jr) => jr.render(canvas, dynamic_rng), + SiteKind::RockCircle(rc) => rc.render(canvas, dynamic_rng), SiteKind::DesertCity(dc) => dc.render(canvas, dynamic_rng), SiteKind::ChapelSite(p) => p.render(canvas, dynamic_rng), SiteKind::DwarvenMine(dm) => dm.render(canvas, dynamic_rng), @@ -355,6 +368,7 @@ impl Site { SiteKind::CoastalTown(_) => {}, SiteKind::PirateHideout(_) => {}, SiteKind::JungleRuin(_) => {}, + SiteKind::RockCircle(_) => {}, SiteKind::DesertCity(_) => {}, SiteKind::ChapelSite(p) => p.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::DwarvenMine(dm) => dm.apply_supplement(dynamic_rng, wpos2d, supplement), @@ -391,6 +405,7 @@ impl Site { SiteKind::CoastalTown(site2) => Some(site2), SiteKind::PirateHideout(site2) => Some(site2), SiteKind::JungleRuin(site2) => Some(site2), + SiteKind::RockCircle(site2) => Some(site2), SiteKind::Tree(_) => None, SiteKind::DesertCity(site2) => Some(site2), SiteKind::ChapelSite(site2) => Some(site2), diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 6161e56dd7..532b10e92b 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -1395,6 +1395,37 @@ impl Site { site } + pub fn generate_rock_circle(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 rock_circle = plot::RockCircle::generate(land, &mut reseed(&mut rng), &site, aabr); + let rock_circle_alt = rock_circle.alt; + let plot = site.create_plot(Plot { + kind: PlotKind::RockCircle(rock_circle), + 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(rock_circle_alt), + }); + } + site + } + pub fn generate_bridge( land: &Land, index: IndexRef, @@ -1755,6 +1786,7 @@ impl Site { PlotKind::PirateHideout(pirate_hideout) => { pirate_hideout.render_collect(self, canvas) }, + PlotKind::RockCircle(rock_circle) => rock_circle.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 b8439976c6..ee0c3cb4c5 100644 --- a/world/src/site2/plot.rs +++ b/world/src/site2/plot.rs @@ -16,6 +16,7 @@ mod gnarling; mod house; mod jungle_ruin; mod pirate_hideout; +mod rock_circle; mod savannah_hut; mod savannah_pit; mod savannah_workshop; @@ -29,8 +30,9 @@ pub use self::{ desert_city_multiplot::DesertCityMultiPlot, desert_city_temple::DesertCityTemple, dungeon::Dungeon, dwarven_mine::DwarvenMine, giant_tree::GiantTree, gnarling::GnarlingFortification, house::House, jungle_ruin::JungleRuin, - pirate_hideout::PirateHideout, savannah_hut::SavannahHut, savannah_pit::SavannahPit, - savannah_workshop::SavannahWorkshop, sea_chapel::SeaChapel, workshop::Workshop, + pirate_hideout::PirateHideout, rock_circle::RockCircle, savannah_hut::SavannahHut, + savannah_pit::SavannahPit, savannah_workshop::SavannahWorkshop, sea_chapel::SeaChapel, + workshop::Workshop, }; use super::*; @@ -95,5 +97,6 @@ pub enum PlotKind { SavannahWorkshop(SavannahWorkshop), Bridge(Bridge), PirateHideout(PirateHideout), + RockCircle(RockCircle), //DwarvenMine(DwarvenMine), } diff --git a/world/src/site2/plot/rock_circle.rs b/world/src/site2/plot/rock_circle.rs new file mode 100644 index 0000000000..64d07e3835 --- /dev/null +++ b/world/src/site2/plot/rock_circle.rs @@ -0,0 +1,65 @@ +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 vek::*; + +pub struct RockCircle { + bounds: Aabr, + pub(crate) alt: i32, +} +impl RockCircle { + 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.center())) as i32 + 2, + } + } +} + +impl Structure for RockCircle { + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"render_rock_circle\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "render_rock_circle_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); + // model + lazy_static! { + pub static ref MODEL: AssetHandle = + PrefabStructure::load_group("site_structures.rock_circle.rock_circle"); + } + 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 + if thread_rng.gen_range(0..=8) < 1 { + // dullahan + painter.spawn( + EntityInfo::at(center.with_z(base + 2).as_()) + .with_asset_expect("common.entity.wild.aggressive.dullahan", &mut thread_rng), + ) + } + } +}