mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added the ability for rtsim to tell NPCs to speak
This commit is contained in:
parent
7dfbc2bdab
commit
b72d8f3192
@ -7,7 +7,7 @@ use crate::character::CharacterId;
|
||||
use rand::{seq::IteratorRandom, Rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::Component;
|
||||
use std::collections::VecDeque;
|
||||
use std::{borrow::Cow, collections::VecDeque};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use vek::*;
|
||||
|
||||
@ -219,9 +219,11 @@ pub enum NpcActivity {
|
||||
Dance,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NpcAction {
|
||||
Greet(Actor),
|
||||
// TODO: Use some sort of structured, language-independent value that frontends can translate instead
|
||||
Say(Cow<'static, str>),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, enum_map::Enum)]
|
||||
|
@ -15,6 +15,7 @@ use rand::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::VecDeque,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
@ -69,6 +70,10 @@ impl Controller {
|
||||
pub fn do_dance(&mut self) { self.activity = Some(NpcActivity::Dance); }
|
||||
|
||||
pub fn greet(&mut self, actor: Actor) { self.actions.push(NpcAction::Greet(actor)); }
|
||||
|
||||
pub fn say(&mut self, msg: impl Into<Cow<'static, str>>) {
|
||||
self.actions.push(NpcAction::Say(msg.into()));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Brain {
|
||||
|
@ -348,7 +348,7 @@ where
|
||||
/// Try to travel to a site. Where practical, paths will be taken.
|
||||
fn travel_to_point(wpos: Vec2<f32>) -> impl Action {
|
||||
now(move |ctx| {
|
||||
const WAYPOINT: f32 = 24.0;
|
||||
const WAYPOINT: f32 = 48.0;
|
||||
let start = ctx.npc.wpos.xy();
|
||||
let diff = wpos - start;
|
||||
let n = (diff.magnitude() / WAYPOINT).max(1.0);
|
||||
@ -506,9 +506,12 @@ fn adventure() -> impl Action {
|
||||
} else {
|
||||
60.0 * 3.0
|
||||
};
|
||||
let site_name = ctx.state.data().sites[tgt_site].world_site
|
||||
.map(|ws| ctx.index.sites.get(ws).name().to_string())
|
||||
.unwrap_or_default();
|
||||
// Travel to the site
|
||||
important(
|
||||
travel_to_site(tgt_site)
|
||||
important(just(move |ctx| ctx.controller.say(format!("I've spent enough time here, onward to {}!", site_name)))
|
||||
.then(travel_to_site(tgt_site))
|
||||
// Stop for a few minutes
|
||||
.then(villager(tgt_site).repeat().stop_if(timeout(wait_time)))
|
||||
.map(|_| ())
|
||||
@ -597,10 +600,12 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
||||
Some(site2.tile_center_wpos(house.root_tile()).as_())
|
||||
})
|
||||
{
|
||||
travel_to_point(house_wpos)
|
||||
just(|ctx| ctx.controller.say("It's dark, time to go home"))
|
||||
.then(travel_to_point(house_wpos))
|
||||
.debug(|| "walk to house")
|
||||
.then(socialize().repeat().debug(|| "wait in house"))
|
||||
.stop_if(|ctx| DayPeriod::from(ctx.time_of_day.0).is_light())
|
||||
.then(just(|ctx| ctx.controller.say("A new day begins!")))
|
||||
.map(|_| ())
|
||||
.boxed()
|
||||
} else {
|
||||
@ -610,9 +615,11 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
||||
.debug(|| "find somewhere to sleep"),
|
||||
);
|
||||
// Villagers with roles should perform those roles
|
||||
} else if matches!(ctx.npc.profession, Some(Profession::Herbalist)) {
|
||||
} else if matches!(ctx.npc.profession, Some(Profession::Herbalist))
|
||||
&& thread_rng().gen_bool(0.8)
|
||||
{
|
||||
if let Some(forest_wpos) = find_forest(ctx) {
|
||||
return important(
|
||||
return casual(
|
||||
travel_to_point(forest_wpos)
|
||||
.debug(|| "walk to forest")
|
||||
.then({
|
||||
@ -622,10 +629,13 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
||||
.map(|_| ()),
|
||||
);
|
||||
}
|
||||
} else if matches!(ctx.npc.profession, Some(Profession::Hunter)) {
|
||||
} else if matches!(ctx.npc.profession, Some(Profession::Hunter))
|
||||
&& thread_rng().gen_bool(0.8)
|
||||
{
|
||||
if let Some(forest_wpos) = find_forest(ctx) {
|
||||
return important(
|
||||
travel_to_point(forest_wpos)
|
||||
return casual(
|
||||
just(|ctx| ctx.controller.say("Time to go hunting!"))
|
||||
.then(travel_to_point(forest_wpos))
|
||||
.debug(|| "walk to forest")
|
||||
.then({
|
||||
let wait_time = thread_rng().gen_range(30.0..60.0);
|
||||
|
@ -292,7 +292,7 @@ impl Rule for SimulateNpcs {
|
||||
// Consume NPC actions
|
||||
for action in std::mem::take(&mut npc.controller.actions) {
|
||||
match action {
|
||||
NpcAction::Greet(_) => {}, // Currently, just swallow greeting actions
|
||||
NpcAction::Greet(_) | NpcAction::Say(_) => {}, // Currently, just swallow interactions
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -505,6 +505,9 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
||||
}
|
||||
}
|
||||
},
|
||||
NpcAction::Say(msg) => {
|
||||
bdata.agent_data.chat_npc(msg, &mut bdata.event_emitter);
|
||||
},
|
||||
}
|
||||
}
|
||||
false
|
||||
|
Loading…
Reference in New Issue
Block a user