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 rand::{seq::IteratorRandom, Rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::Component;
|
use specs::Component;
|
||||||
use std::collections::VecDeque;
|
use std::{borrow::Cow, collections::VecDeque};
|
||||||
use strum::{EnumIter, IntoEnumIterator};
|
use strum::{EnumIter, IntoEnumIterator};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -219,9 +219,11 @@ pub enum NpcActivity {
|
|||||||
Dance,
|
Dance,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum NpcAction {
|
pub enum NpcAction {
|
||||||
Greet(Actor),
|
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)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, enum_map::Enum)]
|
||||||
|
@ -15,6 +15,7 @@ use rand::prelude::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slotmap::HopSlotMap;
|
use slotmap::HopSlotMap;
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
@ -69,6 +70,10 @@ impl Controller {
|
|||||||
pub fn do_dance(&mut self) { self.activity = Some(NpcActivity::Dance); }
|
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 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 {
|
pub struct Brain {
|
||||||
|
@ -348,7 +348,7 @@ where
|
|||||||
/// Try to travel to a site. Where practical, paths will be taken.
|
/// Try to travel to a site. Where practical, paths will be taken.
|
||||||
fn travel_to_point(wpos: Vec2<f32>) -> impl Action {
|
fn travel_to_point(wpos: Vec2<f32>) -> impl Action {
|
||||||
now(move |ctx| {
|
now(move |ctx| {
|
||||||
const WAYPOINT: f32 = 24.0;
|
const WAYPOINT: f32 = 48.0;
|
||||||
let start = ctx.npc.wpos.xy();
|
let start = ctx.npc.wpos.xy();
|
||||||
let diff = wpos - start;
|
let diff = wpos - start;
|
||||||
let n = (diff.magnitude() / WAYPOINT).max(1.0);
|
let n = (diff.magnitude() / WAYPOINT).max(1.0);
|
||||||
@ -506,9 +506,12 @@ fn adventure() -> impl Action {
|
|||||||
} else {
|
} else {
|
||||||
60.0 * 3.0
|
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
|
// Travel to the site
|
||||||
important(
|
important(just(move |ctx| ctx.controller.say(format!("I've spent enough time here, onward to {}!", site_name)))
|
||||||
travel_to_site(tgt_site)
|
.then(travel_to_site(tgt_site))
|
||||||
// Stop for a few minutes
|
// Stop for a few minutes
|
||||||
.then(villager(tgt_site).repeat().stop_if(timeout(wait_time)))
|
.then(villager(tgt_site).repeat().stop_if(timeout(wait_time)))
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
@ -597,10 +600,12 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
Some(site2.tile_center_wpos(house.root_tile()).as_())
|
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")
|
.debug(|| "walk to house")
|
||||||
.then(socialize().repeat().debug(|| "wait in house"))
|
.then(socialize().repeat().debug(|| "wait in house"))
|
||||||
.stop_if(|ctx| DayPeriod::from(ctx.time_of_day.0).is_light())
|
.stop_if(|ctx| DayPeriod::from(ctx.time_of_day.0).is_light())
|
||||||
|
.then(just(|ctx| ctx.controller.say("A new day begins!")))
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.boxed()
|
.boxed()
|
||||||
} else {
|
} else {
|
||||||
@ -610,9 +615,11 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
.debug(|| "find somewhere to sleep"),
|
.debug(|| "find somewhere to sleep"),
|
||||||
);
|
);
|
||||||
// Villagers with roles should perform those roles
|
// 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) {
|
if let Some(forest_wpos) = find_forest(ctx) {
|
||||||
return important(
|
return casual(
|
||||||
travel_to_point(forest_wpos)
|
travel_to_point(forest_wpos)
|
||||||
.debug(|| "walk to forest")
|
.debug(|| "walk to forest")
|
||||||
.then({
|
.then({
|
||||||
@ -622,10 +629,13 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
.map(|_| ()),
|
.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) {
|
if let Some(forest_wpos) = find_forest(ctx) {
|
||||||
return important(
|
return casual(
|
||||||
travel_to_point(forest_wpos)
|
just(|ctx| ctx.controller.say("Time to go hunting!"))
|
||||||
|
.then(travel_to_point(forest_wpos))
|
||||||
.debug(|| "walk to forest")
|
.debug(|| "walk to forest")
|
||||||
.then({
|
.then({
|
||||||
let wait_time = thread_rng().gen_range(30.0..60.0);
|
let wait_time = thread_rng().gen_range(30.0..60.0);
|
||||||
|
@ -292,7 +292,7 @@ impl Rule for SimulateNpcs {
|
|||||||
// Consume NPC actions
|
// Consume NPC actions
|
||||||
for action in std::mem::take(&mut npc.controller.actions) {
|
for action in std::mem::take(&mut npc.controller.actions) {
|
||||||
match action {
|
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
|
false
|
||||||
|
Loading…
Reference in New Issue
Block a user