Fix rtsim NPC respawning

This commit is contained in:
Joshua Barretto 2023-04-11 13:25:45 +01:00
parent 08afe26112
commit bc4d1a71f6
5 changed files with 31 additions and 14 deletions

View File

@ -100,6 +100,8 @@ pub struct Npc {
pub faction: Option<FactionId>,
pub riding: Option<Riding>,
pub is_dead: bool,
/// The [`Report`]s that the NPC is aware of.
pub known_reports: HashSet<ReportId>,
@ -139,6 +141,7 @@ impl Clone for Npc {
home: self.home,
faction: self.faction,
riding: self.riding.clone(),
is_dead: self.is_dead,
known_reports: self.known_reports.clone(),
body: self.body,
personality: self.personality,
@ -169,6 +172,7 @@ impl Npc {
home: None,
faction: None,
riding: None,
is_dead: false,
known_reports: Default::default(),
chunk_pos: None,
current_site: None,

View File

@ -22,12 +22,27 @@ impl Rule for CleanUp {
// TODO: Use `.into_par_iter()` for these by implementing rayon traits in upstream slotmap.
// Decay NPC sentiments
data.npcs
.iter_mut()
// Only cleanup NPCs every few ticks
.filter(|(_, npc)| (npc.seed as u64 + ctx.event.tick) % NPC_SENTIMENT_TICK_SKIP == 0)
.for_each(|(_, npc)| npc.sentiments.decay(&mut rng, ctx.event.dt * NPC_SENTIMENT_TICK_SKIP as f32));
// Remove dead NPCs
// TODO: Don't do this every tick, find a sensible way to gradually remove dead NPCs after they've been
// forgotten
data.npcs
.retain(|npc_id, npc| if npc.is_dead {
// Remove NPC from home population
if let Some(home) = npc.home.and_then(|home| data.sites.get_mut(home)) {
home.population.remove(&npc_id);
}
false
} else {
true
});
// Clean up entities
data.npcs
.iter_mut()

View File

@ -215,6 +215,8 @@ impl Rule for NpcAi {
let mut data = ctx.state.data_mut();
data.npcs
.iter_mut()
// Don't run AI for dead NPCs
.filter(|(_, npc)| !npc.is_dead)
// Don't run AI for simulated NPCs every tick
.filter(|(_, npc)| matches!(npc.mode, SimulationMode::Loaded) || (npc.seed as u64 + ctx.event.tick) % SIMULATED_TICK_SKIP == 0)
.map(|(npc_id, npc)| {

View File

@ -61,7 +61,10 @@ fn on_death(ctx: EventCtx<SimulateNpcs, OnDeath>) {
Some(*id) != npc.home
&& site.faction == npc.faction
&& site.world_site.map_or(false, |s| {
matches!(ctx.index.sites.get(s).kind, SiteKind::Refactor(_))
matches!(ctx.index.sites.get(s).kind, SiteKind::Refactor(_)
| SiteKind::CliffTown(_)
| SiteKind::SavannahPit(_)
| SiteKind::DesertCity(_))
})
})
.min_by_key(|(_, site)| site.population.len())
@ -142,6 +145,8 @@ fn on_death(ctx: EventCtx<SimulateNpcs, OnDeath>) {
home.population.insert(npc_id);
}
}
} else {
error!("Trying to respawn non-existent NPC");
}
}
@ -151,7 +156,7 @@ fn on_tick(ctx: EventCtx<SimulateNpcs, OnTick>) {
.npcs
.npcs
.values_mut()
.filter(|npc| matches!(npc.mode, SimulationMode::Simulated))
.filter(|npc| matches!(npc.mode, SimulationMode::Simulated) && !npc.is_dead)
{
// Simulate NPC movement when riding
if let Some(riding) = &npc.riding {

View File

@ -34,19 +34,10 @@ fn on_death(ctx: EventCtx<SyncNpcs, OnDeath>) {
let data = &mut *ctx.state.data_mut();
if let Actor::Npc(npc_id) = ctx.event.actor {
// Remove NPC from home population
if let Some(home) = data
.npcs
.get(npc_id)
.and_then(|npc| npc.home)
.and_then(|home| data.sites.get_mut(home))
{
home.population.remove(&npc_id);
if let Some(npc) = data.npcs.get_mut(npc_id) {
// Mark the NPC as dead, allowing us to clear them up later
npc.is_dead = true;
}
// TODO: Maybe death should be a marker flag instead of outright deleting the
// NPC so that we can still query details about them?
data.npcs.remove(npc_id);
}
}