Better NPC spawning

This commit is contained in:
Joshua Barretto 2023-03-31 23:57:01 +01:00
parent 64324262c7
commit 5062920b5c
3 changed files with 83 additions and 43 deletions

View File

@ -18,7 +18,7 @@ use common::{
use rand::prelude::*;
use tracing::info;
use vek::*;
use world::{site::SiteKind, IndexRef, World};
use world::{site::SiteKind, site2::PlotKind, IndexRef, World};
impl Data {
pub fn generate(settings: &WorldSettings, world: &World, index: IndexRef) -> Self {
@ -77,10 +77,16 @@ impl Data {
this.sites.len()
);
// Spawn some test entities at the sites
for (site_id, site) in this.sites.iter()
// TODO: Stupid
.filter(|(_, site)| site.world_site.map_or(false, |ws|
matches!(&index.sites.get(ws).kind, SiteKind::Refactor(_))))
for (site_id, site, site2) in this.sites.iter()
// TODO: Stupid. Only find site2 towns
.filter_map(|(site_id, site)| Some((site_id, site, site.world_site
.and_then(|ws| match &index.sites.get(ws).kind {
SiteKind::Refactor(site2)
| SiteKind::CliffTown(site2)
| SiteKind::SavannahPit(site2)
| SiteKind::DesertCity(site2) => Some(site2),
_ => None,
})?)))
{
let Some(good_or_evil) = site
.faction
@ -88,8 +94,13 @@ impl Data {
.map(|f| f.good_or_evil)
else { continue };
let rand_wpos = |rng: &mut SmallRng| {
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
let rand_wpos = |rng: &mut SmallRng, matches_plot: fn(&PlotKind) -> bool| {
let wpos2d = site2
.plots()
.filter(|plot| matches_plot(plot.kind()))
.choose(&mut thread_rng())
.map(|plot| site2.tile_center_wpos(plot.root_tile()))
.unwrap_or_else(|| site.wpos.map(|e| e + rng.gen_range(-10..10)));
wpos2d
.map(|e| e as f32 + 0.5)
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
@ -98,10 +109,21 @@ impl Data {
let species = comp::humanoid::ALL_SPECIES.choose(&mut *rng).unwrap();
Body::Humanoid(comp::humanoid::Body::random_with(rng, species))
};
let matches_buildings = (|kind: &PlotKind| {
matches!(
kind,
PlotKind::House(_) | PlotKind::Workshop(_) | PlotKind::Plaza
)
}) as _;
let matches_plazas = (|kind: &PlotKind| matches!(kind, PlotKind::Plaza)) as _;
if good_or_evil {
for _ in 0..32 {
for _ in 0..site2.plots().len() {
this.npcs.create_npc(
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
Npc::new(
rng.gen(),
rand_wpos(&mut rng, matches_buildings),
random_humanoid(&mut rng),
)
.with_faction(site.faction)
.with_home(site_id)
.with_personality(Personality::random(&mut rng))
@ -112,7 +134,7 @@ impl Data {
3 => Profession::Alchemist,
5..=8 => Profession::Farmer,
9..=10 => Profession::Herbalist,
11..=15 => Profession::Guard,
11..=16 => Profession::Guard,
_ => Profession::Adventurer(rng.gen_range(0..=3)),
}),
);
@ -120,7 +142,11 @@ impl Data {
} else {
for _ in 0..15 {
this.npcs.create_npc(
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
Npc::new(
rng.gen(),
rand_wpos(&mut rng, matches_buildings),
random_humanoid(&mut rng),
)
.with_personality(Personality::random_evil(&mut rng))
.with_faction(site.faction)
.with_home(site_id)
@ -130,15 +156,24 @@ impl Data {
);
}
}
// Merchants
if good_or_evil {
for _ in 0..(site2.plots().len() / 6) + 1 {
this.npcs.create_npc(
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
Npc::new(
rng.gen(),
rand_wpos(&mut rng, matches_plazas),
random_humanoid(&mut rng),
)
.with_home(site_id)
.with_personality(Personality::random_good(&mut rng))
.with_profession(Profession::Merchant),
);
}
}
if rng.gen_bool(0.4) {
let wpos = rand_wpos(&mut rng) + Vec3::unit_z() * 50.0;
let wpos = rand_wpos(&mut rng, matches_plazas) + Vec3::unit_z() * 50.0;
let vehicle_id = this
.npcs
.create_vehicle(Vehicle::new(wpos, comp::body::ship::Body::DefaultAirship));

View File

@ -395,13 +395,13 @@ fn travel_to_point(wpos: Vec2<f32>) -> impl Action {
const WAYPOINT: f32 = 24.0;
let start = ctx.npc.wpos.xy();
let diff = wpos - start;
// if diff.magnitude() > 1.0 {
let n = (diff.magnitude() / WAYPOINT).max(1.0);
let mut points = (1..n as usize + 1).map(move |i| start + diff * (i as f32 / n));
if diff.magnitude() > 1.0 {
traverse_points(move |_| points.next()).boxed()
} else {
finish().boxed()
}
// } else {
// finish().boxed()
// }
})
.debug(|| "travel to point")
}
@ -525,11 +525,16 @@ fn adventure() -> impl Action {
.min_by_key(|(_, site)| site.wpos.as_().distance(ctx.npc.wpos.xy()) as i32)
.map(|(site_id, _)| site_id)
{
let wait_time = if matches!(ctx.npc.profession, Some(Profession::Merchant)) {
60.0 * 15.0
} else {
60.0 * 3.0
};
// Travel to the site
important(
travel_to_site(tgt_site)
// Stop for a few minutes
.then(villager(tgt_site).repeat().stop_if(timeout(60.0 * 3.0)))
.then(villager(tgt_site).repeat().stop_if(timeout(wait_time)))
.map(|_| ())
.boxed(),
)

View File

@ -212,7 +212,7 @@ impl RtSim {
});
if wait_until_finished {
handle.join();
handle.join().expect("Save thread failed to join");
}
self.last_saved = Some(Instant::now());