diff --git a/assets/world/manifests/site_structures/camp/camp_forest.ron b/assets/world/manifests/site_structures/camp/camp_forest.ron new file mode 100644 index 0000000000..d32670c8d7 --- /dev/null +++ b/assets/world/manifests/site_structures/camp/camp_forest.ron @@ -0,0 +1,20 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.spots.camp", + center: (3, 3, 1), + custom_indices: { + 8: Filled(Air, (r: 255, g: 255, b: 255)), + 9: RotatedSprite(Bedroll, 0), + 10: Grass, + 11: Sprite(CookingPot), + 12: Sprite(Chest), + 13: RotatedSprite(Tent, 0), + 14: RotatedSprite(Tent, 4), + 15: RotatedSprite(Tent, 6), + 16: RotatedSprite(Tent, 2), + 251: Sprite(SmokeDummy), + }, + ), +] diff --git a/assets/world/manifests/site_structures/camp/camp_pirate.ron b/assets/world/manifests/site_structures/camp/camp_pirate.ron new file mode 100644 index 0000000000..32c85fbe56 --- /dev/null +++ b/assets/world/manifests/site_structures/camp/camp_pirate.ron @@ -0,0 +1,20 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.spots.camp", + center: (3, 3, 1), + custom_indices: { + 8: Filled(Air, (r: 255, g: 255, b: 255)), + 9: RotatedSprite(BedrollPirate, 0), + 10: Grass, + 11: Sprite(CommonLockedChest), + 12: Filled(Air, (r: 255, g: 255, b: 255)), + 13: Filled(Air, (r: 255, g: 255, b: 255)), + 14: Filled(Air, (r: 255, g: 255, b: 255)), + 15: Filled(Air, (r: 255, g: 255, b: 255)), + 16: Filled(Air, (r: 255, g: 255, b: 255)), + 251: Grass, + }, + ), +] \ No newline at end of file diff --git a/assets/world/manifests/site_structures/camp/camp_snow.ron b/assets/world/manifests/site_structures/camp/camp_snow.ron new file mode 100644 index 0000000000..97930e9c32 --- /dev/null +++ b/assets/world/manifests/site_structures/camp/camp_snow.ron @@ -0,0 +1,20 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.spots.camp", + center: (3, 3, 1), + custom_indices: { + 8: Filled(Air, (r: 255, g: 255, b: 255)), + 9: RotatedSprite(BedrollSnow, 0), + 10: Grass, + 11: Sprite(CookingPot), + 12: Sprite(Chest), + 13: RotatedSprite(Tent, 0), + 14: RotatedSprite(Tent, 4), + 15: RotatedSprite(Tent, 6), + 16: RotatedSprite(Tent, 2), + 251: Sprite(SmokeDummy), + }, + ), +] diff --git a/assets/world/manifests/site_structures/troll_cave/troll_cave.ron b/assets/world/manifests/site_structures/troll_cave/troll_cave.ron new file mode 100644 index 0000000000..90ee6fff68 --- /dev/null +++ b/assets/world/manifests/site_structures/troll_cave/troll_cave.ron @@ -0,0 +1,14 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.natural.troll_cave", + center: (23, 25, 17), + custom_indices: { + 8: Filled(Air, (r: 255, g: 255, b: 255)), + 242: Sprite(Mushroom), + 243: Sprite(CommonLockedChest), + 244: Sprite(Bones), + }, + ), +] \ No newline at end of file diff --git a/rtsim/src/gen/site.rs b/rtsim/src/gen/site.rs index b89cf1177d..4eb43e0363 100644 --- a/rtsim/src/gen/site.rs +++ b/rtsim/src/gen/site.rs @@ -34,6 +34,8 @@ impl Site { | SiteKind::PirateHideout(_) | SiteKind::JungleRuin(_) | SiteKind::RockCircle(_) + | SiteKind::TrollCave(_) + | SiteKind::Camp(_) | SiteKind::Adlet(_) => Some(false), SiteKind::DwarvenMine(_) => Some(false), // Neutral diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index a8e8a6fae6..7496a65bf5 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..85) { + let (loc, kind) = match ctx.rng.gen_range(0..105) { 0..=4 => { if index.features().site2_giant_trees { ( @@ -362,7 +362,27 @@ impl Civs { )?, SiteKind::RockCircle, ), - /*50..=55 => ( + 50..=59 => ( + find_site_loc( + &mut ctx, + &ProximityRequirementsBuilder::new() + .avoid_all_of(this.troll_cave_enemies(), 40) + .finalize(&world_dims), + &SiteKind::TrollCave, + )?, + SiteKind::TrollCave, + ), + 60..=69 => ( + find_site_loc( + &mut ctx, + &ProximityRequirementsBuilder::new() + .avoid_all_of(this.camp_enemies(), 40) + .finalize(&world_dims), + &SiteKind::Camp, + )?, + SiteKind::Camp, + ), + /*70..=75 => ( find_site_loc( &mut ctx, &ProximityRequirementsBuilder::new() @@ -372,7 +392,7 @@ impl Civs { )?, SiteKind::DwarvenMine, ), - 56..=61 => ( + 76..=81 => ( find_site_loc( &mut ctx, &ProximityRequirementsBuilder::new() @@ -383,7 +403,7 @@ impl Civs { )?, SiteKind::Castle, ), - 62..=67 => (SiteKind::Citadel, (&castle_enemies, 20)), + 82..=87 => (SiteKind::Citadel, (&castle_enemies, 20)), */ _ => ( find_site_loc( @@ -433,6 +453,8 @@ impl Civs { SiteKind::Adlet => (16i32, 0.0), SiteKind::PirateHideout => (8i32, 3.0), SiteKind::RockCircle => (8i32, 3.0), + SiteKind::TrollCave => (4i32, 1.5), + SiteKind::Camp => (4i32, 1.5), //SiteKind::DwarvenMine => (8i32, 3.0), }; @@ -554,6 +576,16 @@ impl Civs { SiteKind::RockCircle => WorldSite::rock_circle( site2::Site::generate_rock_circle(&Land::from_sim(ctx.sim), &mut rng, wpos), ), + SiteKind::TrollCave => WorldSite::troll_cave(site2::Site::generate_troll_cave( + &Land::from_sim(ctx.sim), + &mut rng, + wpos, + )), + SiteKind::Camp => WorldSite::troll_cave(site2::Site::generate_camp( + &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), ), @@ -1531,6 +1563,20 @@ impl Civs { _ => Some(s.center), }) } + + fn troll_cave_enemies(&self) -> impl Iterator> + '_ { + self.sites().filter_map(|s| match s.kind { + SiteKind::Tree | SiteKind::GiantTree => None, + _ => Some(s.center), + }) + } + + fn camp_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 @@ -1864,6 +1910,8 @@ pub enum SiteKind { Adlet, PirateHideout, RockCircle, + TrollCave, + Camp, //DwarvenMine, JungleRuin, } @@ -1945,6 +1993,15 @@ impl SiteKind { matches!(chunk.get_biome(), BiomeKind::Jungle) }, SiteKind::RockCircle => !chunk.near_cliffs() && !chunk.river.near_water(), + SiteKind::TrollCave => { + !chunk.near_cliffs() + && on_flat_terrain() + && !chunk.river.near_water() + && chunk.temp < 0.6 + }, + SiteKind::Camp => { + !chunk.near_cliffs() && on_flat_terrain() && !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 47eced614c..53673276fa 100644 --- a/world/src/layer/spot.rs +++ b/world/src/layer/spot.rs @@ -44,9 +44,6 @@ pub enum Spot { GnomeSpring, WolfBurrow, Igloo, - ForestCamp, - SnowCamp, - PirateCamp, //BanditCamp, //EnchantedRock, //TowerRuin, @@ -63,9 +60,6 @@ pub enum Spot { Shipwreck, Shipwreck2, FallenTree, - TrollCave, - TrollCaveMountain, - TrollCaveSwamp, GraveSmall, JungleTemple, SaurokTotem, @@ -216,48 +210,6 @@ impl Spot { }, false, ); - Self::generate_spots( - Spot::TrollCave, - world, - 1.0, - |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 | Grassland) - }, - false, - ); - Self::generate_spots( - Spot::TrollCaveSwamp, - world, - 1.0, - |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(), Jungle) - }, - false, - ); - Self::generate_spots( - Spot::TrollCaveMountain, - world, - 1.0, - |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(), Snowland | Taiga) - }, - false, - ); // Random World Objects -> Themed to their Biome and the NPCs that regularly // spawn there Self::generate_spots( @@ -390,49 +342,6 @@ impl Spot { }, true, ); - Self::generate_spots( - Spot::ForestCamp, - world, - 4.0, - |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 | Taiga | Jungle | Savannah) - }, - false, - ); - Self::generate_spots( - Spot::SnowCamp, - world, - 1.0, - |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(), Snowland) - }, - false, - ); - - Self::generate_spots( - Spot::PirateCamp, - world, - 1.0, - |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(), Desert | Jungle) - }, - false, - ); // Small Grave Self::generate_spots( Spot::GraveSmall, @@ -607,21 +516,6 @@ pub fn apply_spots_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) { (0..2, "common.entity.wild.aggressive.mossdrake"), ], }, - Spot::TrollCave => SpotConfig { - base_structures: Some("spots_general.troll_cave"), - entity_radius: 0.0, - entities: &[], - }, - Spot::TrollCaveSwamp => SpotConfig { - base_structures: Some("spots_general.troll_cave_swamp"), - entity_radius: 0.0, - entities: &[], - }, - Spot::TrollCaveMountain => SpotConfig { - base_structures: Some("spots_general.troll_cave_mountain"), - entity_radius: 0.0, - entities: &[], - }, // Random World Objects Spot::LionRock => SpotConfig { base_structures: Some("spots_savannah.lion_rock"), @@ -676,31 +570,6 @@ pub fn apply_spots_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) { entity_radius: 20.0, entities: &[(0..3, "common.entity.wild.peaceful.clownfish")], }, - Spot::ForestCamp => SpotConfig { - base_structures: Some("spots.camp_forest"), - entity_radius: 2.0, - entities: &[ - (0..2, "common.entity.village.bowman"), - (0..2, "common.entity.village.skinner"), - ], - }, - Spot::SnowCamp => SpotConfig { - base_structures: Some("spots.camp_snow"), - entity_radius: 2.0, - entities: &[ - (0..2, "common.entity.village.bowman"), - (0..2, "common.entity.village.skinner"), - ], - }, - Spot::PirateCamp => SpotConfig { - base_structures: Some("spots.camp_pirate"), - entity_radius: 2.0, - entities: &[ - (1..4, "common.entity.spot.pirate"), - (0..2, "common.entity.wild.peaceful.parrot"), - (0..2, "common.entity.wild.peaceful.rat"), - ], - }, Spot::GraveSmall => SpotConfig { base_structures: Some("spots.grave_small"), entity_radius: 2.0, diff --git a/world/src/lib.rs b/world/src/lib.rs index af2d97d1d7..51afc9d525 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -176,14 +176,29 @@ impl World { .civs() .sites .iter() - .filter(|(_, site)| !matches!(&site.kind, civ::SiteKind::PirateHideout | civ::SiteKind::JungleRuin | civ::SiteKind::RockCircle)) + .filter(|(_, site)| !matches!(&site.kind, + civ::SiteKind::PirateHideout + | civ::SiteKind::JungleRuin + | civ::SiteKind::RockCircle + | civ::SiteKind::TrollCave + | civ::SiteKind::Camp)) .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 | civ::SiteKind::RockCircle => 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 + | civ::SiteKind::TrollCave + | civ::SiteKind::Camp => 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 14220ef18b..7352613fda 100644 --- a/world/src/site/economy/context.rs +++ b/world/src/site/economy/context.rs @@ -137,6 +137,8 @@ impl Environment { SiteKind::Bridge(_) => {}, SiteKind::PirateHideout(_) => {}, SiteKind::RockCircle(_) => {}, + SiteKind::TrollCave(_) => {}, + SiteKind::Camp(_) => {}, } } if towns.valid() { diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index a2a4c01fc4..16945af6c0 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -80,6 +80,8 @@ pub enum SiteKind { PirateHideout(site2::Site), JungleRuin(site2::Site), RockCircle(site2::Site), + TrollCave(site2::Site), + Camp(site2::Site), } impl Site { @@ -167,6 +169,20 @@ impl Site { } } + pub fn troll_cave(tc: site2::Site) -> Self { + Self { + kind: SiteKind::TrollCave(tc), + economy: Economy::default(), + } + } + + pub fn camp(cp: site2::Site) -> Self { + Self { + kind: SiteKind::Camp(cp), + economy: Economy::default(), + } + } + pub fn desert_city(dc: site2::Site) -> Self { Self { kind: SiteKind::DesertCity(dc), @@ -221,6 +237,8 @@ impl Site { SiteKind::PirateHideout(ph) => ph.radius(), SiteKind::JungleRuin(jr) => jr.radius(), SiteKind::RockCircle(rc) => rc.radius(), + SiteKind::TrollCave(tc) => tc.radius(), + SiteKind::Camp(cp) => cp.radius(), SiteKind::DesertCity(dc) => dc.radius(), SiteKind::ChapelSite(p) => p.radius(), SiteKind::DwarvenMine(dm) => dm.radius(), @@ -244,6 +262,8 @@ impl Site { SiteKind::PirateHideout(ph) => ph.origin, SiteKind::JungleRuin(jr) => jr.origin, SiteKind::RockCircle(rc) => rc.origin, + SiteKind::TrollCave(tc) => tc.origin, + SiteKind::Camp(cp) => cp.origin, SiteKind::DesertCity(dc) => dc.origin, SiteKind::ChapelSite(p) => p.origin, SiteKind::DwarvenMine(dm) => dm.origin, @@ -267,6 +287,8 @@ impl Site { SiteKind::PirateHideout(ph) => ph.spawn_rules(wpos), SiteKind::JungleRuin(jr) => jr.spawn_rules(wpos), SiteKind::RockCircle(rc) => rc.spawn_rules(wpos), + SiteKind::TrollCave(tc) => tc.spawn_rules(wpos), + SiteKind::Camp(cp) => cp.spawn_rules(wpos), SiteKind::DesertCity(dc) => dc.spawn_rules(wpos), SiteKind::ChapelSite(p) => p.spawn_rules(wpos), SiteKind::DwarvenMine(dm) => dm.spawn_rules(wpos), @@ -290,6 +312,8 @@ impl Site { SiteKind::PirateHideout(ph) => ph.name(), SiteKind::JungleRuin(jr) => jr.name(), SiteKind::RockCircle(rc) => rc.name(), + SiteKind::TrollCave(tc) => tc.name(), + SiteKind::Camp(cp) => cp.name(), SiteKind::DesertCity(dc) => dc.name(), SiteKind::ChapelSite(p) => p.name(), SiteKind::DwarvenMine(dm) => dm.name(), @@ -333,6 +357,8 @@ impl Site { 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::TrollCave(tc) => tc.render(canvas, dynamic_rng), + SiteKind::Camp(cp) => cp.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), @@ -369,6 +395,8 @@ impl Site { SiteKind::PirateHideout(_) => {}, SiteKind::JungleRuin(_) => {}, SiteKind::RockCircle(_) => {}, + SiteKind::TrollCave(_) => {}, + SiteKind::Camp(_) => {}, SiteKind::DesertCity(_) => {}, SiteKind::ChapelSite(p) => p.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::DwarvenMine(dm) => dm.apply_supplement(dynamic_rng, wpos2d, supplement), @@ -406,6 +434,8 @@ impl Site { SiteKind::PirateHideout(site2) => Some(site2), SiteKind::JungleRuin(site2) => Some(site2), SiteKind::RockCircle(site2) => Some(site2), + SiteKind::TrollCave(site2) => Some(site2), + SiteKind::Camp(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 532b10e92b..ea47fada99 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -11,6 +11,7 @@ pub use self::{ util::Dir, }; use crate::{ + config::CONFIG, sim::Path, site::{namegen::NameGen, SpawnRules}, util::{attempt, DHashSet, Grid, CARDINALS, SQUARE_4, SQUARE_9}, @@ -1426,6 +1427,71 @@ impl Site { site } + pub fn generate_troll_cave(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 = 2.0 as i32; + let aabr = Aabr { + min: Vec2::broadcast(-size), + max: Vec2::broadcast(size), + }; + let site_temp = temp_at_wpos(land, origin); + { + let troll_cave = + plot::TrollCave::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp); + let troll_cave_alt = troll_cave.alt; + let plot = site.create_plot(Plot { + kind: PlotKind::TrollCave(troll_cave), + 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(troll_cave_alt), + }); + } + site + } + + pub fn generate_camp(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 = 2.0 as i32; + let aabr = Aabr { + min: Vec2::broadcast(-size), + max: Vec2::broadcast(size), + }; + let site_temp = temp_at_wpos(land, origin); + { + let camp = plot::Camp::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp); + let camp_alt = camp.alt; + let plot = site.create_plot(Plot { + kind: PlotKind::Camp(camp), + 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(camp_alt), + }); + } + site + } + pub fn generate_bridge( land: &Land, index: IndexRef, @@ -1787,6 +1853,8 @@ impl Site { pirate_hideout.render_collect(self, canvas) }, PlotKind::RockCircle(rock_circle) => rock_circle.render_collect(self, canvas), + PlotKind::TrollCave(troll_cave) => troll_cave.render_collect(self, canvas), + PlotKind::Camp(camp) => camp.render_collect(self, canvas), PlotKind::Plaza | PlotKind::Road(_) => continue, // _ => continue, Avoid using a wildcard here!! }; @@ -1905,6 +1973,12 @@ fn wpos_is_hazard(land: &Land, wpos: Vec2) -> Option { } } +fn temp_at_wpos(land: &Land, wpos: Vec2) -> f32 { + land.get_chunk_wpos(wpos) + .map(|c| c.temp) + .unwrap_or(CONFIG.temperate_temp) +} + pub fn aabr_tiles(aabr: Aabr) -> impl Iterator> { (0..aabr.size().h) .flat_map(move |y| (0..aabr.size().w).map(move |x| aabr.min + Vec2::new(x, y))) diff --git a/world/src/site2/plot.rs b/world/src/site2/plot.rs index ee0c3cb4c5..15693441fc 100644 --- a/world/src/site2/plot.rs +++ b/world/src/site2/plot.rs @@ -1,6 +1,7 @@ mod adlet; mod airship_dock; mod bridge; +mod camp; mod castle; mod citadel; mod cliff_tower; @@ -21,10 +22,11 @@ mod savannah_hut; mod savannah_pit; mod savannah_workshop; mod sea_chapel; +mod troll_cave; mod workshop; pub use self::{ - adlet::AdletStronghold, airship_dock::AirshipDock, bridge::Bridge, castle::Castle, + adlet::AdletStronghold, airship_dock::AirshipDock, bridge::Bridge, camp::Camp, castle::Castle, citadel::Citadel, cliff_tower::CliffTower, coastal_house::CoastalHouse, coastal_workshop::CoastalWorkshop, desert_city_arena::DesertCityArena, desert_city_multiplot::DesertCityMultiPlot, desert_city_temple::DesertCityTemple, @@ -32,7 +34,7 @@ pub use self::{ gnarling::GnarlingFortification, house::House, jungle_ruin::JungleRuin, pirate_hideout::PirateHideout, rock_circle::RockCircle, savannah_hut::SavannahHut, savannah_pit::SavannahPit, savannah_workshop::SavannahWorkshop, sea_chapel::SeaChapel, - workshop::Workshop, + troll_cave::TrollCave, workshop::Workshop, }; use super::*; @@ -98,5 +100,7 @@ pub enum PlotKind { Bridge(Bridge), PirateHideout(PirateHideout), RockCircle(RockCircle), + TrollCave(TrollCave), + Camp(Camp), //DwarvenMine(DwarvenMine), } diff --git a/world/src/site2/plot/camp.rs b/world/src/site2/plot/camp.rs new file mode 100644 index 0000000000..b6babc6551 --- /dev/null +++ b/world/src/site2/plot/camp.rs @@ -0,0 +1,121 @@ +use super::*; +use crate::{assets::AssetHandle, site2::gen::PrimitiveTransform, Land}; +use common::{ + generation::EntityInfo, + terrain::{Structure as PrefabStructure, StructuresGroup}, +}; +use lazy_static::lazy_static; + +use rand::prelude::*; +use vek::*; + +pub struct Camp { + bounds: Aabr, + pub(crate) alt: i32, + temp: f32, +} + +#[derive(Copy, Clone)] +enum CampType { + Pirate, + Snow, + Forest, +} + +impl Camp { + pub fn generate( + land: &Land, + _rng: &mut impl Rng, + site: &Site, + tile_aabr: Aabr, + site_temp: f32, + ) -> Self { + let bounds = Aabr { + min: site.tile_wpos(tile_aabr.min), + max: site.tile_wpos(tile_aabr.max), + }; + let temp = site_temp; + Self { + bounds, + alt: land.get_alt_approx(site.tile_center_wpos(tile_aabr.center())) as i32 + 2, + temp, + } + } +} + +impl Structure for Camp { + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"render_camp\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "render_camp")] + 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); + let temp = self.temp; + let camp_type = if temp >= CONFIG.tropical_temp { + CampType::Pirate + } else if temp <= (CONFIG.snow_temp) { + CampType::Snow + } else { + CampType::Forest + }; + // models + lazy_static! { + pub static ref MODEL_PIRATE: AssetHandle = + PrefabStructure::load_group("site_structures.camp.camp_pirate"); + pub static ref MODEL_SNOW: AssetHandle = + PrefabStructure::load_group("site_structures.camp.camp_snow"); + pub static ref MODEL_FOREST: AssetHandle = + PrefabStructure::load_group("site_structures.camp.camp_forest"); + } + let prefab_structure = match camp_type { + CampType::Pirate => MODEL_PIRATE.read(), + CampType::Snow => MODEL_SNOW.read(), + CampType::Forest => MODEL_FOREST.read(), + }[0] + .clone(); + + painter + .prim(Primitive::Prefab(Box::new(prefab_structure.clone()))) + .translate(model_pos) + .fill(Fill::Prefab(Box::new(prefab_structure), model_pos, 0)); + + // npcs + let npc_rng = thread_rng.gen_range(1..=5); + match camp_type { + CampType::Pirate => { + for p in 0..npc_rng { + painter.spawn( + EntityInfo::at((center + p).with_z(base + 2).as_()) + .with_asset_expect("common.entity.spot.pirate", &mut thread_rng), + ) + } + let pet = if npc_rng < 3 { + "common.entity.wild.peaceful.parrot" + } else { + "common.entity.wild.peaceful.rat" + }; + painter.spawn( + EntityInfo::at(center.with_z(base + 2).as_()) + .with_asset_expect(pet, &mut thread_rng), + ) + }, + _ => { + if npc_rng > 2 { + painter.spawn( + EntityInfo::at((center - 1).with_z(base + 2).as_()) + .with_asset_expect("common.entity.village.bowman", &mut thread_rng), + ); + } + if npc_rng < 4 { + painter.spawn( + EntityInfo::at((center + 1).with_z(base + 2).as_()) + .with_asset_expect("common.entity.village.skinner", &mut thread_rng), + ) + } + }, + }; + } +} diff --git a/world/src/site2/plot/rock_circle.rs b/world/src/site2/plot/rock_circle.rs index 64d07e3835..dfcb022f2a 100644 --- a/world/src/site2/plot/rock_circle.rs +++ b/world/src/site2/plot/rock_circle.rs @@ -34,7 +34,7 @@ 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")] + #[cfg_attr(feature = "be-dyn-lib", export_name = "render_rock_circle")] fn render_inner(&self, _site: &Site, land: &Land, painter: &Painter) { let center = self.bounds.center(); let base = land.get_alt_approx(center) as i32; diff --git a/world/src/site2/plot/troll_cave.rs b/world/src/site2/plot/troll_cave.rs new file mode 100644 index 0000000000..903ff7016a --- /dev/null +++ b/world/src/site2/plot/troll_cave.rs @@ -0,0 +1,85 @@ +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 TrollCave { + bounds: Aabr, + pub(crate) alt: i32, + temp: f32, +} +impl TrollCave { + pub fn generate( + land: &Land, + _rng: &mut impl Rng, + site: &Site, + tile_aabr: Aabr, + site_temp: f32, + ) -> Self { + let bounds = Aabr { + min: site.tile_wpos(tile_aabr.min), + max: site.tile_wpos(tile_aabr.max), + }; + let temp = site_temp; + Self { + bounds, + alt: land.get_alt_approx(site.tile_center_wpos(tile_aabr.center())) as i32 + 2, + temp, + } + } +} + +impl Structure for TrollCave { + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"render_troll_cave\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "render_troll_cave")] + 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.troll_cave.troll_cave"); + } + 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)); + let temp = self.temp; + // npcs + let troll = if temp >= CONFIG.tropical_temp { + "common.entity.wild.aggressive.swamp_troll" + } else if temp <= (CONFIG.snow_temp) { + "common.entity.wild.aggressive.mountain_troll" + } else { + "common.entity.wild.aggressive.cave_troll" + }; + + // troll + painter.spawn( + EntityInfo::at(center.with_z(base - 15).as_()) + .with_asset_expect(troll, &mut thread_rng), + ); + // bat + painter.spawn( + EntityInfo::at((center - 2).with_z(base + 5).as_()) + .with_asset_expect("common.entity.wild.aggressive.bat", &mut thread_rng), + ) + } +}