Made adventurers explore sites before moving on

This commit is contained in:
Joshua Barretto 2023-01-05 17:15:33 +00:00
parent 2b3f0737d0
commit 84eb7b0653
3 changed files with 95 additions and 86 deletions

View File

@ -423,7 +423,7 @@ fn timeout(time: f64) -> impl FnMut(&mut NpcCtx) -> bool + Clone + Send + Sync {
}
fn adventure() -> impl Action {
now(|ctx| {
choose(|ctx| {
// Choose a random site that's fairly close by
if let Some(tgt_site) = ctx
.state
@ -441,33 +441,43 @@ fn adventure() -> impl Action {
.map(|(site_id, _)| site_id)
{
// Travel to the site
travel_to_site(tgt_site)
important(
travel_to_site(tgt_site)
// Stop for a few minutes
.then(villager().stop_if(timeout(60.0 * 3.0)))
.then(villager(tgt_site).repeat().stop_if(timeout(60.0 * 3.0)))
.map(|_| ())
.boxed()
.boxed(),
)
} else {
finish().boxed()
casual(finish().boxed())
}
})
.debug(move || format!("adventure"))
}
fn villager() -> impl Action {
choose(|ctx| {
if let Some(home) = ctx.npc.home {
if ctx.npc.current_site != Some(home) {
// Travel home if we're not there already
urgent(travel_to_site(home).debug(move || format!("travel home")))
} else if DayPeriod::from(ctx.time_of_day.0).is_dark() {
important(now(move |ctx| {
fn villager(visiting_site: SiteId) -> impl Action {
choose(move |ctx| {
if ctx.npc.current_site != Some(visiting_site) {
let npc_home = ctx.npc.home;
// Travel to the site we're supposed to be in
urgent(travel_to_site(visiting_site).debug(move || {
if npc_home == Some(visiting_site) {
format!("travel home")
} else {
format!("travel to visiting site")
}
}))
} else if DayPeriod::from(ctx.time_of_day.0).is_dark() {
important(
now(move |ctx| {
if let Some(house_wpos) = ctx
.state
.data()
.sites
.get(home)
.and_then(|home| ctx.index.sites.get(home.world_site?).site2())
.get(visiting_site)
.and_then(|site| ctx.index.sites.get(site.world_site?).site2())
.and_then(|site2| {
// Find a house
// Find a house in the site we're visiting
let house = site2
.plots()
.filter(|p| matches!(p.kind(), PlotKind::House(_)))
@ -484,57 +494,55 @@ fn villager() -> impl Action {
} else {
finish().boxed()
}
}))
} else if matches!(
ctx.npc.profession,
Some(Profession::Merchant | Profession::Blacksmith)
) {
// Trade professions just walk between town plazas
casual(now(move |ctx| {
// Choose a plaza in the NPC's home site to walk to
if let Some(plaza_wpos) = ctx
.state
.data()
.sites
.get(home)
.and_then(|home| ctx.index.sites.get(home.world_site?).site2())
.and_then(|site2| {
let plaza = &site2.plots[site2.plazas().choose(&mut thread_rng())?];
Some(site2.tile_center_wpos(plaza.root_tile()).as_())
})
{
// Walk to the plaza...
goto_2d(plaza_wpos, 0.5)
.debug(|| "walk to plaza")
// ...then wait for some time before moving on
.then({
let wait_time = thread_rng().gen_range(10.0..30.0);
idle().repeat().stop_if(timeout(wait_time))
.debug(|| "wait at plaza")
})
.map(|_| ())
.boxed()
} else {
// No plazas? :(
finish().boxed()
}
}))
} else {
casual(idle())
}
})
.debug(|| "find somewhere to sleep"),
)
} else {
casual(finish()) // Nothing to do if we're homeless!
casual(now(move |ctx| {
// Choose a plaza in the site we're visiting to walk to
if let Some(plaza_wpos) = ctx
.state
.data()
.sites
.get(visiting_site)
.and_then(|site| ctx.index.sites.get(site.world_site?).site2())
.and_then(|site2| {
let plaza = &site2.plots[site2.plazas().choose(&mut thread_rng())?];
Some(site2.tile_center_wpos(plaza.root_tile()).as_())
})
{
// Walk to the plaza...
goto_2d(plaza_wpos, 0.5)
.debug(|| "walk to plaza")
// ...then wait for some time before moving on
.then({
let wait_time = thread_rng().gen_range(10.0..30.0);
idle().repeat().stop_if(timeout(wait_time))
.debug(|| "wait at plaza")
})
.map(|_| ())
.boxed()
} else {
// No plazas? :(
finish().boxed()
}
}))
}
})
.debug(move || format!("villager"))
.debug(move || format!("villager at site {:?}", visiting_site))
}
fn think() -> impl Action {
choose(|ctx| {
if matches!(ctx.npc.profession, Some(Profession::Adventurer(_))) {
if matches!(
ctx.npc.profession,
Some(Profession::Adventurer(_) | Profession::Merchant)
) {
casual(adventure())
} else if let Some(home) = ctx.npc.home {
casual(villager(home))
} else {
casual(villager())
casual(finish()) // Homeless
}
})
}

View File

@ -9,40 +9,39 @@ impl Rule for SimulateNpcs {
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
rtstate.bind::<Self, OnTick>(|ctx| {
let data = &mut *ctx.state.data_mut();
for npc in data
.npcs
.values_mut()
.filter(|npc| matches!(npc.mode, NpcMode::Simulated))
{
let body = npc.get_body();
// Move NPCs if they have a target destination
if let Some((target, speed_factor)) = npc.goto {
let diff = target.xy() - npc.wpos.xy();
let dist2 = diff.magnitude_squared();
if dist2 > 0.5f32.powi(2) {
npc.wpos += (diff
* (body.max_speed_approx() * speed_factor * ctx.event.dt
/ dist2.sqrt())
.min(1.0))
.with_z(0.0);
}
}
// Make sure NPCs remain on the surface
npc.wpos.z = ctx
.world
.sim()
.get_alt_approx(npc.wpos.xy().map(|e| e as i32))
.unwrap_or(0.0);
for npc in data.npcs.values_mut() {
// Update the NPC's current site, if any
npc.current_site = ctx
.world
.sim()
.get(npc.wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_())
.and_then(|chunk| data.sites.world_site_map.get(chunk.sites.first()?).copied());
// Simulate the NPC's movement and interactions
if matches!(npc.mode, NpcMode::Simulated) {
let body = npc.get_body();
// Move NPCs if they have a target destination
if let Some((target, speed_factor)) = npc.goto {
let diff = target.xy() - npc.wpos.xy();
let dist2 = diff.magnitude_squared();
if dist2 > 0.5f32.powi(2) {
npc.wpos += (diff
* (body.max_speed_approx() * speed_factor * ctx.event.dt
/ dist2.sqrt())
.min(1.0))
.with_z(0.0);
}
}
// Make sure NPCs remain on the surface
npc.wpos.z = ctx
.world
.sim()
.get_alt_approx(npc.wpos.xy().map(|e| e as i32))
.unwrap_or(0.0);
}
}
});

View File

@ -1237,6 +1237,8 @@ fn handle_npc_info(
let _ = writeln!(&mut info, "Seed: {}", npc.seed);
let _ = writeln!(&mut info, "Profession: {:?}", npc.profession);
let _ = writeln!(&mut info, "Home: {:?}", npc.home);
let _ = writeln!(&mut info, "-- Status --");
let _ = writeln!(&mut info, "Current site: {:?}", npc.current_site);
let _ = writeln!(&mut info, "Current mode: {:?}", npc.mode);
let _ = writeln!(&mut info, "-- Action State --");
if let Some(brain) = &npc.brain {