mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add basic NPC interaction and fix NPC chat spamming
This commit is contained in:
parent
7553983110
commit
23b1df3cdd
@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- 6 different gems. (Topaz, Amethyst, Sapphire, Emerald, Ruby and Diamond)
|
- 6 different gems. (Topaz, Amethyst, Sapphire, Emerald, Ruby and Diamond)
|
||||||
- Poise system (not currently accessible to players for balancing reasons)
|
- Poise system (not currently accessible to players for balancing reasons)
|
||||||
- Snow particles
|
- Snow particles
|
||||||
|
- Basic NPC interaction
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@ -37,7 +38,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Default inventory slots reduced to 18 - existing characters given 3x 6-slot bags as compensation
|
- Default inventory slots reduced to 18 - existing characters given 3x 6-slot bags as compensation
|
||||||
- Protection rating was moved to the top left of the loadout view
|
- Protection rating was moved to the top left of the loadout view
|
||||||
- Changed camera smoothing to be off by default.
|
- Changed camera smoothing to be off by default.
|
||||||
- Fixed AI behavior so only humanoids will attempt to roll
|
|
||||||
- Footstep SFX is now dependant on distance moved, not time since last play
|
- Footstep SFX is now dependant on distance moved, not time since last play
|
||||||
- Adjusted most NPCs hitboxes to better fit their models.
|
- Adjusted most NPCs hitboxes to better fit their models.
|
||||||
- Changed crafting recipes involving shiny gems to use diamonds instead.
|
- Changed crafting recipes involving shiny gems to use diamonds instead.
|
||||||
@ -57,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Fixed a bug where buff/debuff UI elements would flicker when you had more than
|
- Fixed a bug where buff/debuff UI elements would flicker when you had more than
|
||||||
one of them active at the same time
|
one of them active at the same time
|
||||||
- Made zooming work on wayland
|
- Made zooming work on wayland
|
||||||
|
- Fixed AI behavior so only humanoids will attempt to roll
|
||||||
|
|
||||||
## [0.8.0] - 2020-11-28
|
## [0.8.0] - 2020-11-28
|
||||||
|
|
||||||
|
@ -62,6 +62,35 @@
|
|||||||
"Sit near a campfire (with the 'K' key) to slowly recover from your injuries.",
|
"Sit near a campfire (with the 'K' key) to slowly recover from your injuries.",
|
||||||
"Need more bags or better armor to continue your journey? Press 'C' to open the crafting menu!",
|
"Need more bags or better armor to continue your journey? Press 'C' to open the crafting menu!",
|
||||||
],
|
],
|
||||||
|
"npc.speech.villager": [
|
||||||
|
"Isn't it such a lovely day?",
|
||||||
|
"How are you today?",
|
||||||
|
"Top of the morning to you!",
|
||||||
|
"I wonder what the Catobelpas thinks when it eats grass.",
|
||||||
|
"What do you think about this weather?",
|
||||||
|
"Thinking about those dungeons makes me scared. I hope someone will clear them out.",
|
||||||
|
"I'd like to go spelunking in a cave when I'm stronger.",
|
||||||
|
"Have you seen my cat?",
|
||||||
|
"Have you ever heard of the ferocious Land Sharks? I hear they live in deserts.",
|
||||||
|
"They say shiny gems of all kinds can be found in caves.",
|
||||||
|
"I'm just crackers about cheese!",
|
||||||
|
"Won't you come in? We were just about to have some cheese!",
|
||||||
|
"They say mushrooms are good for your health. Never eat them myself.",
|
||||||
|
"Don't forget the crackers!",
|
||||||
|
"I simply adore dwarven cheese. I wish I could make it.",
|
||||||
|
"I wonder what is on the other side of the mountains.",
|
||||||
|
"I hope to make my own glider someday.",
|
||||||
|
"Would you like to see my garden? Okay, maybe some other time.",
|
||||||
|
"Lovely day for a stroll in the woods!",
|
||||||
|
"To be, or not to be? I think I'll be a farmer.",
|
||||||
|
"Don't you think our village is the best?.",
|
||||||
|
"What do you suppose makes Glowing Remains glow?.",
|
||||||
|
"I think it's time for second breakfast!",
|
||||||
|
"Have you ever caught a firefly?",
|
||||||
|
"I just can't understand where those Sauroks keep coming from.",
|
||||||
|
"I wish someone would keep the wolves away from the village.",
|
||||||
|
"I had a wonderful dream about cheese last night. What does it mean?",
|
||||||
|
],
|
||||||
"npc.speech.villager_under_attack": [
|
"npc.speech.villager_under_attack": [
|
||||||
"Help, I'm under attack!",
|
"Help, I'm under attack!",
|
||||||
"Help! I'm under attack!",
|
"Help! I'm under attack!",
|
||||||
|
@ -637,6 +637,23 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn npc_interact(&mut self, npc_entity: EcsEntity) {
|
||||||
|
// If we're dead, exit before sending message
|
||||||
|
if self
|
||||||
|
.state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<comp::Health>()
|
||||||
|
.get(self.entity)
|
||||||
|
.map_or(false, |h| h.is_dead)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(uid) = self.state.read_component_copied(npc_entity) {
|
||||||
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Interact(uid)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn player_list(&self) -> &HashMap<Uid, PlayerInfo> { &self.player_list }
|
pub fn player_list(&self) -> &HashMap<Uid, PlayerInfo> { &self.player_list }
|
||||||
|
|
||||||
pub fn character_list(&self) -> &CharacterList { &self.character_list }
|
pub fn character_list(&self) -> &CharacterList { &self.character_list }
|
||||||
|
@ -6,7 +6,31 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use specs::{Component, Entity as EcsEntity};
|
use specs::{Component, Entity as EcsEntity};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
pub const DEFAULT_INTERACTION_TIME: f32 = 3.0;
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
|
pub enum Tactic {
|
||||||
|
Melee,
|
||||||
|
Axe,
|
||||||
|
Hammer,
|
||||||
|
Sword,
|
||||||
|
Bow,
|
||||||
|
Staff,
|
||||||
|
StoneGolemBoss,
|
||||||
|
CircleCharge { radius: u32, circle_time: u32 },
|
||||||
|
QuadLowRanged,
|
||||||
|
TailSlap,
|
||||||
|
QuadLowQuick,
|
||||||
|
QuadLowBasic,
|
||||||
|
QuadMedJump,
|
||||||
|
QuadMedBasic,
|
||||||
|
Lavadrake,
|
||||||
|
Theropod,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
/// Wild animals and gentle giants
|
/// Wild animals and gentle giants
|
||||||
@ -137,6 +161,15 @@ impl<'a> From<&'a Body> for Psyche {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
/// Events that affect agent behavior from other entities/players/environment
|
||||||
|
pub enum AgentEvent {
|
||||||
|
/// Engage in conversation with entity with Uid
|
||||||
|
Talk(Uid),
|
||||||
|
Trade(Uid),
|
||||||
|
// Add others here
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Agent {
|
pub struct Agent {
|
||||||
pub rtsim_controller: RtSimController,
|
pub rtsim_controller: RtSimController,
|
||||||
@ -146,6 +179,7 @@ pub struct Agent {
|
|||||||
// TODO move speech patterns into a Behavior component
|
// TODO move speech patterns into a Behavior component
|
||||||
pub can_speak: bool,
|
pub can_speak: bool,
|
||||||
pub psyche: Psyche,
|
pub psyche: Psyche,
|
||||||
|
pub inbox: VecDeque<AgentEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Agent {
|
impl Agent {
|
||||||
@ -179,6 +213,10 @@ impl Component for Agent {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Activity {
|
pub enum Activity {
|
||||||
|
Interact {
|
||||||
|
timer: f32,
|
||||||
|
interaction: AgentEvent,
|
||||||
|
},
|
||||||
Idle {
|
Idle {
|
||||||
bearing: Vec2<f32>,
|
bearing: Vec2<f32>,
|
||||||
chaser: Chaser,
|
chaser: Chaser,
|
||||||
@ -194,12 +232,19 @@ pub enum Activity {
|
|||||||
been_close: bool,
|
been_close: bool,
|
||||||
powerup: f32,
|
powerup: f32,
|
||||||
},
|
},
|
||||||
|
Flee {
|
||||||
|
target: EcsEntity,
|
||||||
|
chaser: Chaser,
|
||||||
|
timer: f32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Activity {
|
impl Activity {
|
||||||
pub fn is_follow(&self) -> bool { matches!(self, Activity::Follow { .. }) }
|
pub fn is_follow(&self) -> bool { matches!(self, Activity::Follow { .. }) }
|
||||||
|
|
||||||
pub fn is_attack(&self) -> bool { matches!(self, Activity::Attack { .. }) }
|
pub fn is_attack(&self) -> bool { matches!(self, Activity::Attack { .. }) }
|
||||||
|
|
||||||
|
pub fn is_flee(&self) -> bool { matches!(self, Activity::Flee { .. }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Activity {
|
impl Default for Activity {
|
||||||
|
@ -41,6 +41,7 @@ pub enum CharacterState {
|
|||||||
Climb,
|
Climb,
|
||||||
Sit,
|
Sit,
|
||||||
Dance,
|
Dance,
|
||||||
|
Talk,
|
||||||
Sneak,
|
Sneak,
|
||||||
Glide,
|
Glide,
|
||||||
GlideWield,
|
GlideWield,
|
||||||
@ -139,6 +140,7 @@ impl CharacterState {
|
|||||||
| CharacterState::BasicBeam(_)
|
| CharacterState::BasicBeam(_)
|
||||||
| CharacterState::Stunned(_)
|
| CharacterState::Stunned(_)
|
||||||
| CharacterState::Wielding
|
| CharacterState::Wielding
|
||||||
|
| CharacterState::Talk
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ pub enum ControlEvent {
|
|||||||
//ToggleLantern,
|
//ToggleLantern,
|
||||||
EnableLantern,
|
EnableLantern,
|
||||||
DisableLantern,
|
DisableLantern,
|
||||||
|
Interact(Uid),
|
||||||
Mount(Uid),
|
Mount(Uid),
|
||||||
Unmount,
|
Unmount,
|
||||||
InventoryManip(InventoryManip),
|
InventoryManip(InventoryManip),
|
||||||
@ -55,6 +56,7 @@ pub enum ControlAction {
|
|||||||
Dance,
|
Dance,
|
||||||
Sneak,
|
Sneak,
|
||||||
Stand,
|
Stand,
|
||||||
|
Talk,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -78,6 +78,7 @@ pub enum ServerEvent {
|
|||||||
},
|
},
|
||||||
EnableLantern(EcsEntity),
|
EnableLantern(EcsEntity),
|
||||||
DisableLantern(EcsEntity),
|
DisableLantern(EcsEntity),
|
||||||
|
NpcInteract(EcsEntity, EcsEntity),
|
||||||
Mount(EcsEntity, EcsEntity),
|
Mount(EcsEntity, EcsEntity),
|
||||||
Unmount(EcsEntity),
|
Unmount(EcsEntity),
|
||||||
Possess(Uid, Uid),
|
Possess(Uid, Uid),
|
||||||
|
@ -32,8 +32,9 @@ impl<T> FromIterator<T> for Path<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::len_without_is_empty)] // TODO: Pending review in #587
|
|
||||||
impl<T> Path<T> {
|
impl<T> Path<T> {
|
||||||
|
pub fn is_empty(&self) -> bool { self.nodes.is_empty() }
|
||||||
|
|
||||||
pub fn len(&self) -> usize { self.nodes.len() }
|
pub fn len(&self) -> usize { self.nodes.len() }
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> { self.nodes.iter() }
|
pub fn iter(&self) -> impl Iterator<Item = &T> { self.nodes.iter() }
|
||||||
|
@ -30,7 +30,7 @@ pub struct RtSimController {
|
|||||||
/// When this field is `Some(..)`, the agent should attempt to make progress
|
/// When this field is `Some(..)`, the agent should attempt to make progress
|
||||||
/// toward the given location, accounting for obstacles and other
|
/// toward the given location, accounting for obstacles and other
|
||||||
/// high-priority situations like being attacked.
|
/// high-priority situations like being attacked.
|
||||||
pub travel_to: Option<Vec3<f32>>,
|
pub travel_to: Option<(Vec3<f32>, String)>,
|
||||||
/// Proportion of full speed to move
|
/// Proportion of full speed to move
|
||||||
pub speed_factor: f32,
|
pub speed_factor: f32,
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ pub trait CharacterBehavior {
|
|||||||
fn dance(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
|
fn dance(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
|
||||||
fn sneak(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
|
fn sneak(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
|
||||||
fn stand(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
|
fn stand(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
|
||||||
|
fn talk(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
|
||||||
fn handle_event(&self, data: &JoinData, event: ControlAction) -> StateUpdate {
|
fn handle_event(&self, data: &JoinData, event: ControlAction) -> StateUpdate {
|
||||||
match event {
|
match event {
|
||||||
ControlAction::SwapLoadout => self.swap_loadout(data),
|
ControlAction::SwapLoadout => self.swap_loadout(data),
|
||||||
@ -34,6 +35,7 @@ pub trait CharacterBehavior {
|
|||||||
ControlAction::Dance => self.dance(data),
|
ControlAction::Dance => self.dance(data),
|
||||||
ControlAction::Sneak => self.sneak(data),
|
ControlAction::Sneak => self.sneak(data),
|
||||||
ControlAction::Stand => self.stand(data),
|
ControlAction::Stand => self.stand(data),
|
||||||
|
ControlAction::Talk => self.talk(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fn init(data: &JoinData) -> CharacterState;
|
// fn init(data: &JoinData) -> CharacterState;
|
||||||
|
@ -31,6 +31,12 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn talk(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_talk(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
fn dance(&self, data: &JoinData) -> StateUpdate {
|
fn dance(&self, data: &JoinData) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
attempt_dance(data, &mut update);
|
attempt_dance(data, &mut update);
|
||||||
|
@ -22,5 +22,6 @@ pub mod sit;
|
|||||||
pub mod sneak;
|
pub mod sneak;
|
||||||
pub mod spin_melee;
|
pub mod spin_melee;
|
||||||
pub mod stunned;
|
pub mod stunned;
|
||||||
|
pub mod talk;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod wielding;
|
pub mod wielding;
|
||||||
|
49
common/src/states/talk.rs
Normal file
49
common/src/states/talk.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use super::utils::*;
|
||||||
|
use crate::{
|
||||||
|
comp::{CharacterState, StateUpdate},
|
||||||
|
states::behavior::{CharacterBehavior, JoinData},
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
const TURN_RATE: f32 = 40.0;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
|
pub struct Data;
|
||||||
|
|
||||||
|
impl CharacterBehavior for Data {
|
||||||
|
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
|
||||||
|
handle_wield(data, &mut update);
|
||||||
|
handle_orientation(data, &mut update, TURN_RATE);
|
||||||
|
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wield(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_wield(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sit(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
update.character = CharacterState::Idle;
|
||||||
|
attempt_sit(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dance(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
update.character = CharacterState::Idle;
|
||||||
|
attempt_dance(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stand(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
// Try to Fall/Stand up/Move
|
||||||
|
update.character = CharacterState::Idle;
|
||||||
|
update
|
||||||
|
}
|
||||||
|
}
|
@ -322,6 +322,12 @@ pub fn attempt_dance(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn attempt_talk(data: &JoinData, update: &mut StateUpdate) {
|
||||||
|
if data.physics.on_ground {
|
||||||
|
update.character = CharacterState::Talk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) {
|
pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) {
|
||||||
if data.physics.on_ground && data.body.is_humanoid() {
|
if data.physics.on_ground && data.body.is_humanoid() {
|
||||||
update.character = CharacterState::Sneak;
|
update.character = CharacterState::Sneak;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -231,6 +231,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let j = JoinData::new(&tuple, &updater, &dt);
|
let j = JoinData::new(&tuple, &updater, &dt);
|
||||||
let mut state_update = match j.character {
|
let mut state_update = match j.character {
|
||||||
CharacterState::Idle => states::idle::Data.handle_event(&j, action),
|
CharacterState::Idle => states::idle::Data.handle_event(&j, action),
|
||||||
|
CharacterState::Talk => states::talk::Data.handle_event(&j, action),
|
||||||
CharacterState::Climb => states::climb::Data.handle_event(&j, action),
|
CharacterState::Climb => states::climb::Data.handle_event(&j, action),
|
||||||
CharacterState::Glide => states::glide::Data.handle_event(&j, action),
|
CharacterState::Glide => states::glide::Data.handle_event(&j, action),
|
||||||
CharacterState::GlideWield => {
|
CharacterState::GlideWield => {
|
||||||
@ -274,6 +275,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
let mut state_update = match j.character {
|
let mut state_update = match j.character {
|
||||||
CharacterState::Idle => states::idle::Data.behavior(&j),
|
CharacterState::Idle => states::idle::Data.behavior(&j),
|
||||||
|
CharacterState::Talk => states::talk::Data.behavior(&j),
|
||||||
CharacterState::Climb => states::climb::Data.behavior(&j),
|
CharacterState::Climb => states::climb::Data.behavior(&j),
|
||||||
CharacterState::Glide => states::glide::Data.behavior(&j),
|
CharacterState::Glide => states::glide::Data.behavior(&j),
|
||||||
CharacterState::GlideWield => states::glide_wield::Data.behavior(&j),
|
CharacterState::GlideWield => states::glide_wield::Data.behavior(&j),
|
||||||
|
@ -98,6 +98,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
ControlEvent::DisableLantern => {
|
ControlEvent::DisableLantern => {
|
||||||
server_emitter.emit(ServerEvent::DisableLantern(entity))
|
server_emitter.emit(ServerEvent::DisableLantern(entity))
|
||||||
},
|
},
|
||||||
|
ControlEvent::Interact(npc_uid) => {
|
||||||
|
if let Some(npc_entity) =
|
||||||
|
uid_allocator.retrieve_entity_internal(npc_uid.id())
|
||||||
|
{
|
||||||
|
server_emitter.emit(ServerEvent::NpcInteract(entity, npc_entity));
|
||||||
|
}
|
||||||
|
},
|
||||||
ControlEvent::InventoryManip(manip) => {
|
ControlEvent::InventoryManip(manip) => {
|
||||||
// Unwield if a wielded equipment slot is being modified, to avoid entering
|
// Unwield if a wielded equipment slot is being modified, to avoid entering
|
||||||
// a barehanded wielding state.
|
// a barehanded wielding state.
|
||||||
|
@ -168,6 +168,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
match character_state {
|
match character_state {
|
||||||
// Accelerate recharging energy.
|
// Accelerate recharging energy.
|
||||||
CharacterState::Idle { .. }
|
CharacterState::Idle { .. }
|
||||||
|
| CharacterState::Talk { .. }
|
||||||
| CharacterState::Sit { .. }
|
| CharacterState::Sit { .. }
|
||||||
| CharacterState::Dance { .. }
|
| CharacterState::Dance { .. }
|
||||||
| CharacterState::Sneak { .. }
|
| CharacterState::Sneak { .. }
|
||||||
|
@ -2,7 +2,7 @@ use specs::{world::WorldExt, Entity as EcsEntity};
|
|||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, inventory::slot::EquipSlot, item, slot::Slot, Inventory, Pos},
|
comp::{self, agent::AgentEvent, inventory::slot::EquipSlot, item, slot::Slot, Inventory, Pos},
|
||||||
consts::MAX_MOUNT_RANGE,
|
consts::MAX_MOUNT_RANGE,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
@ -55,6 +55,19 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_npc_interaction(server: &mut Server, interactor: EcsEntity, npc_entity: EcsEntity) {
|
||||||
|
let state = server.state_mut();
|
||||||
|
if let Some(agent) = state
|
||||||
|
.ecs()
|
||||||
|
.write_storage::<comp::Agent>()
|
||||||
|
.get_mut(npc_entity)
|
||||||
|
{
|
||||||
|
if let Some(interactor_uid) = state.ecs().uid_from_entity(interactor) {
|
||||||
|
agent.inbox.push_front(AgentEvent::Talk(interactor_uid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_mount(server: &mut Server, mounter: EcsEntity, mountee: EcsEntity) {
|
pub fn handle_mount(server: &mut Server, mounter: EcsEntity, mountee: EcsEntity) {
|
||||||
let state = server.state_mut();
|
let state = server.state_mut();
|
||||||
|
|
||||||
|
@ -12,7 +12,9 @@ use entity_manipulation::{
|
|||||||
handle_explosion, handle_knockback, handle_land_on_ground, handle_poise, handle_respawn,
|
handle_explosion, handle_knockback, handle_land_on_ground, handle_poise, handle_respawn,
|
||||||
};
|
};
|
||||||
use group_manip::handle_group;
|
use group_manip::handle_group;
|
||||||
use interaction::{handle_lantern, handle_mount, handle_possess, handle_unmount};
|
use interaction::{
|
||||||
|
handle_lantern, handle_mount, handle_npc_interaction, handle_possess, handle_unmount,
|
||||||
|
};
|
||||||
use inventory_manip::handle_inventory;
|
use inventory_manip::handle_inventory;
|
||||||
use player::{handle_client_disconnect, handle_exit_ingame};
|
use player::{handle_client_disconnect, handle_exit_ingame};
|
||||||
use specs::{Entity as EcsEntity, WorldExt};
|
use specs::{Entity as EcsEntity, WorldExt};
|
||||||
@ -98,6 +100,9 @@ impl Server {
|
|||||||
},
|
},
|
||||||
ServerEvent::EnableLantern(entity) => handle_lantern(self, entity, true),
|
ServerEvent::EnableLantern(entity) => handle_lantern(self, entity, true),
|
||||||
ServerEvent::DisableLantern(entity) => handle_lantern(self, entity, false),
|
ServerEvent::DisableLantern(entity) => handle_lantern(self, entity, false),
|
||||||
|
ServerEvent::NpcInteract(interactor, target) => {
|
||||||
|
handle_npc_interaction(self, interactor, target)
|
||||||
|
},
|
||||||
ServerEvent::Mount(mounter, mountee) => handle_mount(self, mounter, mountee),
|
ServerEvent::Mount(mounter, mountee) => handle_mount(self, mounter, mountee),
|
||||||
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
||||||
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
||||||
|
@ -3,7 +3,7 @@ use common::{comp::inventory::loadout_builder::LoadoutBuilder, store::Id, terrai
|
|||||||
use world::{
|
use world::{
|
||||||
civ::{Site, Track},
|
civ::{Site, Track},
|
||||||
util::RandomPerm,
|
util::RandomPerm,
|
||||||
World,
|
IndexRef, World,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Entity {
|
pub struct Entity {
|
||||||
@ -123,7 +123,7 @@ impl Entity {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self, terrain: &TerrainGrid, world: &World) {
|
pub fn tick(&mut self, terrain: &TerrainGrid, world: &World, index: &IndexRef) {
|
||||||
let tgt_site = self.brain.tgt.or_else(|| {
|
let tgt_site = self.brain.tgt.or_else(|| {
|
||||||
world
|
world
|
||||||
.civs()
|
.civs()
|
||||||
@ -146,6 +146,10 @@ impl Entity {
|
|||||||
tgt_site.map(|tgt_site| {
|
tgt_site.map(|tgt_site| {
|
||||||
let site = &world.civs().sites[tgt_site];
|
let site = &world.civs().sites[tgt_site];
|
||||||
|
|
||||||
|
let destination_name = site
|
||||||
|
.site_tmp
|
||||||
|
.map_or("".to_string(), |id| index.sites[id].name().to_string());
|
||||||
|
|
||||||
let wpos = site.center * TerrainChunk::RECT_SIZE.map(|e| e as i32);
|
let wpos = site.center * TerrainChunk::RECT_SIZE.map(|e| e as i32);
|
||||||
let dist = wpos.map(|e| e as f32).distance(self.pos.xy()) as u32;
|
let dist = wpos.map(|e| e as f32).distance(self.pos.xy()) as u32;
|
||||||
|
|
||||||
@ -171,7 +175,7 @@ impl Entity {
|
|||||||
))
|
))
|
||||||
.map(|e| e as f32)
|
.map(|e| e as f32)
|
||||||
+ Vec3::new(0.5, 0.5, 0.0);
|
+ Vec3::new(0.5, 0.5, 0.0);
|
||||||
self.controller.travel_to = Some(travel_to);
|
self.controller.travel_to = Some((travel_to, destination_name));
|
||||||
self.controller.speed_factor = 0.70;
|
self.controller.speed_factor = 0.70;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut rtsim,
|
mut rtsim,
|
||||||
terrain,
|
terrain,
|
||||||
world,
|
world,
|
||||||
_index,
|
index,
|
||||||
positions,
|
positions,
|
||||||
rtsim_entities,
|
rtsim_entities,
|
||||||
mut agents,
|
mut agents,
|
||||||
@ -60,10 +60,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
to_reify.push(id);
|
to_reify.push(id);
|
||||||
} else {
|
} else {
|
||||||
// Simulate behaviour
|
// Simulate behaviour
|
||||||
if let Some(travel_to) = entity.controller.travel_to {
|
if let Some(travel_to) = &entity.controller.travel_to {
|
||||||
// Move towards target at approximate character speed
|
// Move towards target at approximate character speed
|
||||||
entity.pos += Vec3::from(
|
entity.pos += Vec3::from(
|
||||||
(travel_to.xy() - entity.pos.xy())
|
(travel_to.0.xy() - entity.pos.xy())
|
||||||
.try_normalized()
|
.try_normalized()
|
||||||
.unwrap_or_else(Vec2::zero)
|
.unwrap_or_else(Vec2::zero)
|
||||||
* entity.get_body().max_speed_approx()
|
* entity.get_body().max_speed_approx()
|
||||||
@ -81,7 +81,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// Tick entity AI
|
// Tick entity AI
|
||||||
if entity.last_tick + ENTITY_TICK_PERIOD <= rtsim.tick {
|
if entity.last_tick + ENTITY_TICK_PERIOD <= rtsim.tick {
|
||||||
entity.tick(&terrain, &world);
|
entity.tick(&terrain, &world, &index.as_index_ref());
|
||||||
entity.last_tick = rtsim.tick;
|
entity.last_tick = rtsim.tick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -516,6 +516,8 @@ impl PlayState for SessionState {
|
|||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
client.pick_up(entity);
|
client.pick_up(entity);
|
||||||
|
} else {
|
||||||
|
client.npc_interact(entity);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1495,12 +1497,10 @@ fn select_interactable(
|
|||||||
scales.maybe(),
|
scales.maybe(),
|
||||||
colliders.maybe(),
|
colliders.maybe(),
|
||||||
char_states.maybe(),
|
char_states.maybe(),
|
||||||
// Must have this comp to be interactable (for now)
|
|
||||||
&ecs.read_storage::<comp::Item>(),
|
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(e, _, _, _, _, _)| *e != player_entity)
|
.filter(|(e, _, _, _, _)| *e != player_entity)
|
||||||
.map(|(e, p, s, c, cs, _)| {
|
.map(|(e, p, s, c, cs)| {
|
||||||
let cylinder = Cylinder::from_components(p.0, s.copied(), c.copied(), cs);
|
let cylinder = Cylinder::from_components(p.0, s.copied(), c.copied(), cs);
|
||||||
(e, cylinder)
|
(e, cylinder)
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user