Allow NPCs to migrate away from towns with a high population density

This commit is contained in:
Joshua Barretto 2023-04-13 12:00:59 +01:00
parent 9e17042bf6
commit 2a1ea63910
4 changed files with 157 additions and 125 deletions

View File

@ -225,6 +225,9 @@ npc-speech-prisoner =
.a4 = I wish i still had my pick!
npc-speech-moving_on =
.a0 = I've spent enough time here, onward to { $site }!
npc-speech-migrating =
.a0 = I'm no longer happy living here. Time to migrate to { $site }.
.a1 = Time to move to { $site }, I've had it with this place.
npc-speech-night_time =
.a0 = It's dark, time to head home.
.a1 = I'm tired.

View File

@ -55,6 +55,7 @@ pub struct PathingMemory {
pub struct Controller {
pub actions: Vec<NpcAction>,
pub activity: Option<NpcActivity>,
pub new_home: Option<SiteId>,
}
impl Controller {
@ -79,6 +80,8 @@ impl Controller {
pub fn attack(&mut self, target: impl Into<Actor>) {
self.actions.push(NpcAction::Attack(target.into()));
}
pub fn set_new_home(&mut self, new_home: SiteId) { self.new_home = Some(new_home); }
}
pub struct Brain {

View File

@ -599,28 +599,50 @@ fn choose_plaza(ctx: &mut NpcCtx, site: SiteId) -> Option<Vec2<f32>> {
fn villager(visiting_site: SiteId) -> impl Action {
choose(move |ctx| {
/*
if ctx
// Consider moving home if the home site gets too full
if ctx.rng.gen_bool(0.0001)
&& let Some(home) = ctx.npc.home
&& Some(home) == ctx.npc.current_site
&& let Some(home_pop_ratio) = ctx.state.data().sites.get(home)
.and_then(|site| Some((site, ctx.index.sites.get(site.world_site?).site2()?)))
.map(|(site, site2)| site.population.len() as f32 / site2.plots().len() as f32)
// Only consider moving if the population is more than 1.5x the number of homes
.filter(|pop_ratio| *pop_ratio > 1.5)
&& let Some(new_home) = ctx
.state
.data()
.sites
.get(visiting_site)
.map_or(true, |s| s.world_site.is_none())
.iter()
// Don't try to move to the site that's currently our home
.filter(|(site_id, _)| Some(*site_id) != ctx.npc.home)
// Only consider towns as potential homes
.filter_map(|(site_id, site)| {
let site2 = match site.world_site.map(|ws| &ctx.index.sites.get(ws).kind) {
Some(SiteKind::Refactor(site2)
| SiteKind::CliffTown(site2)
| SiteKind::SavannahPit(site2)
| SiteKind::DesertCity(site2)) => site2,
_ => return None,
};
Some((site_id, site, site2))
})
// Only select sites that are less densely populated than our own
.filter(|(_, site, site2)| (site.population.len() as f32 / site2.plots().len() as f32) < home_pop_ratio)
// Find the closest of the candidate sites
.min_by_key(|(_, site, _)| site.wpos.as_().distance(ctx.npc.wpos.xy()) as i32)
.map(|(site_id, _, _)| site_id)
{
return casual(idle()
.debug(|| "idling (visiting site does not exist, perhaps it's stale data?)"));
} else if ctx.npc.current_site != Some(visiting_site) {
let npc_home = ctx.npc.home;
// Travel to the site we're supposed to be in
return urgent(travel_to_site(visiting_site, 1.0).debug(move || {
if npc_home == Some(visiting_site) {
"travel home".to_string()
} else {
"travel to visiting site".to_string()
let site_name = ctx.state.data().sites[new_home].world_site
.map(|ws| ctx.index.sites.get(ws).name().to_string());
return important(just(move |ctx| {
if let Some(site_name) = &site_name {
ctx.controller.say(None, Content::localized_with_args("npc-speech-migrating", [("site", site_name.clone())]))
}
}));
} else
*/
})
.then(travel_to_site(new_home, 0.5))
.then(just(move |ctx| ctx.controller.set_new_home(new_home))));
}
if DayPeriod::from(ctx.time_of_day.0).is_dark()
&& !matches!(ctx.npc.profession, Some(Profession::Guard))
{

View File

@ -151,12 +151,8 @@ fn on_death(ctx: EventCtx<SimulateNpcs, OnDeath>) {
fn on_tick(ctx: EventCtx<SimulateNpcs, OnTick>) {
let data = &mut *ctx.state.data_mut();
for npc in data
.npcs
.npcs
.values_mut()
.filter(|npc| matches!(npc.mode, SimulationMode::Simulated) && !npc.is_dead)
{
for npc in data.npcs.npcs.values_mut().filter(|npc| !npc.is_dead) {
if matches!(npc.mode, SimulationMode::Simulated) {
// Simulate NPC movement when riding
if let Some(riding) = &npc.riding {
if let Some(vehicle) = data.npcs.vehicles.get_mut(riding.vehicle) {
@ -248,7 +244,9 @@ fn on_tick(ctx: EventCtx<SimulateNpcs, OnTick>) {
.with_z(0.0);
}
},
Some(NpcActivity::Gather(_) | NpcActivity::HuntAnimals | NpcActivity::Dance) => {
Some(
NpcActivity::Gather(_) | NpcActivity::HuntAnimals | NpcActivity::Dance,
) => {
// TODO: Maybe they should walk around randomly
// when gathering resources?
},
@ -272,4 +270,10 @@ fn on_tick(ctx: EventCtx<SimulateNpcs, OnTick>) {
.unwrap_or(0.0)
+ npc.body.flying_height();
}
// Move home if required
if let Some(new_home) = npc.controller.new_home.take() {
npc.home = Some(new_home);
}
}
}