From 6fbd007cead02f30e465ede20a272fad26404d2e Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 1 May 2023 21:48:33 +0100 Subject: [PATCH] Non-humanoid rtsim respawning, made monsters wander --- rtsim/src/gen/mod.rs | 13 ++--- rtsim/src/rule/npc_ai.rs | 39 ++++++++++---- rtsim/src/rule/simulate_npcs.rs | 94 ++++++++++++++++----------------- 3 files changed, 84 insertions(+), 62 deletions(-) diff --git a/rtsim/src/gen/mod.rs b/rtsim/src/gen/mod.rs index f8972e2333..67adbddbb5 100644 --- a/rtsim/src/gen/mod.rs +++ b/rtsim/src/gen/mod.rs @@ -224,15 +224,16 @@ impl Data { // Spawn monsters into the world for _ in 0..100 { // Try a few times to find a location that's not underwater - if let Some(pos) = (0..10) + if let Some(wpos) = (0..10) .map(|_| world.sim().get_size().map(|sz| rng.gen_range(0..sz as i32))) .find(|pos| world.sim().get(*pos).map_or(false, |c| !c.is_underwater())) + .map(|pos| { + let wpos2d = pos.cpos_to_wpos_center(); + wpos2d + .map(|e| e as f32 + 0.5) + .with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0)) + }) { - let wpos2d = pos.cpos_to_wpos_center(); - let wpos = wpos2d - .map(|e| e as f32 + 0.5) - .with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0)); - let species = match rng.gen_range(0..3) { 0 => comp::body::biped_large::Species::Cyclops, 1 => comp::body::biped_large::Species::Wendigo, diff --git a/rtsim/src/rule/npc_ai.rs b/rtsim/src/rule/npc_ai.rs index f78e67ad30..85c86186a1 100644 --- a/rtsim/src/rule/npc_ai.rs +++ b/rtsim/src/rule/npc_ai.rs @@ -975,7 +975,9 @@ fn check_inbox(ctx: &mut NpcCtx) -> Option { Some(report_id) if !ctx.known_reports.contains(&report_id) => { #[allow(clippy::single_match)] match ctx.state.data().reports.get(report_id).map(|r| r.kind) { - Some(ReportKind::Death { killer, actor, .. }) => { + Some(ReportKind::Death { killer, actor, .. }) + if matches!(&ctx.npc.role, Role::Civilised(_)) => + { // TODO: Don't report self let phrase = if let Some(killer) = killer { // TODO: For now, we don't make sentiment changes if the killer was an @@ -1015,7 +1017,8 @@ fn check_inbox(ctx: &mut NpcCtx) -> Option { ctx.controller.say(killer, Content::localized(phrase)) })); }, - None => {}, // Stale report, ignore + Some(ReportKind::Death { .. }) => {}, // We don't care about death + None => {}, // Stale report, ignore } }, Some(_) => {}, // Reports we already know of are ignored @@ -1069,11 +1072,11 @@ fn humanoid() -> impl Action { ctx.npc.profession(), Some(Profession::Adventurer(_) | Profession::Merchant) ) { - adventure().boxed() + adventure().l().l() } else if let Some(home) = ctx.npc.home { - villager(home).boxed() + villager(home).r().l() } else { - idle().boxed() // Homeless + idle().r() // Homeless }; casual(action.interrupt_with(react_to_events)) @@ -1123,10 +1126,28 @@ fn bird_large() -> impl Action { }) } +fn monster() -> impl Action { + let mut bearing = Vec2::zero(); + now(move |ctx| { + bearing = bearing + .map(|e| e + ctx.rng.gen_range(-0.1..0.1)) + .try_normalized() + .unwrap_or_default(); + goto_2d(ctx.npc.wpos.xy() + bearing * 24.0, 0.7, 8.0) + .debug(move || format!("Moving with a bearing of {:?}", bearing)) + }) + .repeat() + .map(|_| ()) +} + fn think() -> impl Action { - choose(|ctx| match ctx.npc.body { - common::comp::Body::Humanoid(_) => casual(humanoid()), - common::comp::Body::BirdLarge(_) => casual(bird_large()), - _ => casual(socialize()), + now(|ctx| match ctx.npc.body { + common::comp::Body::Humanoid(_) => humanoid().l().l().l(), + common::comp::Body::BirdLarge(_) => bird_large().r().l().l(), + _ => match &ctx.npc.role { + Role::Civilised(_) => socialize().l().r().l(), + Role::Monster => monster().r().r().l(), + Role::Wild => idle().r(), + }, }) } diff --git a/rtsim/src/rule/simulate_npcs.rs b/rtsim/src/rule/simulate_npcs.rs index 5a362a2166..82cf095394 100644 --- a/rtsim/src/rule/simulate_npcs.rs +++ b/rtsim/src/rule/simulate_npcs.rs @@ -5,7 +5,7 @@ use crate::{ }; use common::{ comp::{self, Body}, - rtsim::{Actor, NpcAction, NpcActivity, Personality, Role}, + rtsim::{Actor, NpcAction, NpcActivity, Personality}, terrain::CoordinateConversions, }; use rand::prelude::*; @@ -92,62 +92,62 @@ fn on_death(ctx: EventCtx) { .with_home(site_id) .with_faction(npc.faction), ); - Some((npc_id, site_id)) + Some((npc_id, Some(site_id))) } else { warn!("No site found for respawning humanoid"); None } }, - Body::BirdLarge(_) => { - if let Some((site_id, site)) = data - .sites - .iter() - .filter(|(id, site)| { - Some(*id) != npc.home - && site.world_site.map_or(false, |s| { - matches!(ctx.index.sites.get(s).kind, SiteKind::Dungeon(_)) - }) - }) - .min_by_key(|(_, site)| site.population.len()) - { - let rand_wpos = |rng: &mut ChaChaRng| { - let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10)); - wpos2d - .map(|e| e as f32 + 0.5) - .with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0)) - }; - let species = [ - comp::body::bird_large::Species::Phoenix, - comp::body::bird_large::Species::Cockatrice, - comp::body::bird_large::Species::Roc, - ] - .choose(&mut rng) - .unwrap(); - let npc_id = data.npcs.create_npc( - Npc::new( - rng.gen(), - rand_wpos(&mut rng), - Body::BirdLarge(comp::body::bird_large::Body::random_with( - &mut rng, species, - )), - Role::Wild, - ) - .with_home(site_id), - ); - Some((npc_id, site_id)) - } else { - warn!("No site found for respawning bird"); - None - } - }, body => { - error!("Tried to respawn rtsim NPC with invalid body: {:?}", body); - None + let home = npc.home.and_then(|_| { + data.sites + .iter() + .filter(|(id, site)| { + Some(*id) != npc.home + && site.world_site.map_or(false, |s| { + matches!(ctx.index.sites.get(s).kind, SiteKind::Dungeon(_)) + }) + }) + .min_by_key(|(_, site)| site.population.len()) + }); + + let wpos = if let Some((_, home)) = home { + let wpos2d = home.wpos.map(|e| e + rng.gen_range(-10..10)); + wpos2d + .map(|e| e as f32 + 0.5) + .with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0)) + } else { + let pos = (0..10) + .map(|_| { + ctx.world + .sim() + .get_size() + .map(|sz| rng.gen_range(0..sz as i32)) + }) + .find(|pos| { + ctx.world + .sim() + .get(*pos) + .map_or(false, |c| !c.is_underwater()) + }) + .unwrap_or(ctx.world.sim().get_size().as_() / 2); + let wpos2d = pos.cpos_to_wpos_center(); + wpos2d + .map(|e| e as f32 + 0.5) + .with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0)) + }; + + let home = home.map(|(site_id, _)| site_id); + + let npc_id = data.npcs.create_npc( + Npc::new(rng.gen(), wpos, body, npc.role.clone()).with_home(home), + ); + Some((npc_id, home)) }, }; // Add the NPC to their home site - if let Some((npc_id, home_site)) = details { + if let Some((npc_id, Some(home_site))) = details { if let Some(home) = data.sites.get_mut(home_site) { home.population.insert(npc_id); }