mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Initial mercenary hiring work
This commit is contained in:
parent
a37174d6f2
commit
2b7e9ceec0
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, Body},
|
comp::{chat::GenericChatMsg, humanoid, quadruped_low, quadruped_medium, quadruped_small, Body},
|
||||||
path::Chaser,
|
path::Chaser,
|
||||||
rtsim::RtSimController,
|
rtsim::RtSimController,
|
||||||
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
|
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
|
||||||
@ -8,6 +8,7 @@ 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 std::collections::VecDeque;
|
||||||
|
use hashbrown::HashMap;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
use super::dialogue::Subject;
|
use super::dialogue::Subject;
|
||||||
@ -266,6 +267,7 @@ impl<'a> From<&'a Body> for Psyche {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
/// Events that affect agent behavior from other entities/players/environment
|
/// Events that affect agent behavior from other entities/players/environment
|
||||||
pub enum AgentEvent {
|
pub enum AgentEvent {
|
||||||
|
IncomingChat(GenericChatMsg<String>),
|
||||||
/// Engage in conversation with entity with Uid
|
/// Engage in conversation with entity with Uid
|
||||||
Talk(Uid, Subject),
|
Talk(Uid, Subject),
|
||||||
TradeInvite(Uid),
|
TradeInvite(Uid),
|
||||||
@ -290,6 +292,14 @@ pub struct Target {
|
|||||||
pub selected_at: f64,
|
pub selected_at: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Occupation {
|
||||||
|
Traveler,
|
||||||
|
TravelingMercenary,
|
||||||
|
Merchant,
|
||||||
|
TravelingMerchant,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Agent {
|
pub struct Agent {
|
||||||
pub rtsim_controller: RtSimController,
|
pub rtsim_controller: RtSimController,
|
||||||
@ -301,6 +311,19 @@ pub struct Agent {
|
|||||||
pub inbox: VecDeque<AgentEvent>,
|
pub inbox: VecDeque<AgentEvent>,
|
||||||
pub action_timer: f32,
|
pub action_timer: f32,
|
||||||
pub bearing: Vec2<f32>,
|
pub bearing: Vec2<f32>,
|
||||||
|
pub offer: Option<InteractionOffer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum InteractionOffer {
|
||||||
|
Trade,
|
||||||
|
MercenaryHire,
|
||||||
|
FetchQuest(FetchResult),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FetchResult {
|
||||||
|
items: HashMap<String, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Agent {
|
impl Agent {
|
||||||
|
@ -6,6 +6,7 @@ use specs_idvs::IdvStorage;
|
|||||||
pub enum InviteKind {
|
pub enum InviteKind {
|
||||||
Group,
|
Group,
|
||||||
Trade,
|
Trade,
|
||||||
|
JoinGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -37,6 +37,8 @@ pub enum MemoryItem {
|
|||||||
// such as clothing worn, weapon used, etc.
|
// such as clothing worn, weapon used, etc.
|
||||||
CharacterInteraction { name: String },
|
CharacterInteraction { name: String },
|
||||||
CharacterFight { name: String },
|
CharacterFight { name: String },
|
||||||
|
/// A contract with another character to join their group and fight for them
|
||||||
|
MercenaryContract { name: String },
|
||||||
Mood { state: MoodState },
|
Mood { state: MoodState },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +211,27 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
InviteKind::JoinGroup => {
|
||||||
|
let mut group_manager = state.ecs().write_resource::<GroupManager>();
|
||||||
|
group_manager.add_group_member(
|
||||||
|
entity,
|
||||||
|
inviter,
|
||||||
|
&state.ecs().entities(),
|
||||||
|
&mut state.ecs().write_storage(),
|
||||||
|
&state.ecs().read_storage(),
|
||||||
|
&uids,
|
||||||
|
|entity, group_change| {
|
||||||
|
clients
|
||||||
|
.get(entity)
|
||||||
|
.and_then(|c| {
|
||||||
|
group_change
|
||||||
|
.try_map(|e| uids.get(e).copied())
|
||||||
|
.map(|g| (g, c))
|
||||||
|
})
|
||||||
|
.map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g)));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
InviteKind::Trade => {
|
InviteKind::Trade => {
|
||||||
if let (Some(inviter_uid), Some(invitee_uid)) =
|
if let (Some(inviter_uid), Some(invitee_uid)) =
|
||||||
(uids.get(inviter).copied(), uids.get(entity).copied())
|
(uids.get(inviter).copied(), uids.get(entity).copied())
|
||||||
|
@ -32,7 +32,8 @@ fn notify_agent_prices(
|
|||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
event: AgentEvent,
|
event: AgentEvent,
|
||||||
) {
|
) {
|
||||||
if let Some((Some(site_id), agent)) = agents.get_mut(entity).map(|a| (a.behavior.trade_site, a))
|
if let Some(agent) = agents.get_mut(entity) {
|
||||||
|
if let Some(site_id) = agent.behavior.trade_site
|
||||||
{
|
{
|
||||||
let prices = index.get_site_prices(site_id);
|
let prices = index.get_site_prices(site_id);
|
||||||
if let AgentEvent::UpdatePendingTrade(boxval) = event {
|
if let AgentEvent::UpdatePendingTrade(boxval) = event {
|
||||||
@ -48,6 +49,21 @@ fn notify_agent_prices(
|
|||||||
boxval.3,
|
boxval.3,
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
} else if agent.offer.is_some() {
|
||||||
|
if let AgentEvent::UpdatePendingTrade(boxval) = event {
|
||||||
|
// Box<(tid, pend, _, inventories)>) = event {
|
||||||
|
agent
|
||||||
|
.inbox
|
||||||
|
.push_front(AgentEvent::UpdatePendingTrade(Box::new((
|
||||||
|
// Prefer using this Agent's price data, but use the counterparty's price
|
||||||
|
// data if we don't have price data
|
||||||
|
boxval.0,
|
||||||
|
boxval.1,
|
||||||
|
boxval.2,
|
||||||
|
boxval.3,
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +74,7 @@ pub fn handle_process_trade_action(
|
|||||||
trade_id: TradeId,
|
trade_id: TradeId,
|
||||||
action: TradeAction,
|
action: TradeAction,
|
||||||
) {
|
) {
|
||||||
|
println!("trade action: {:?}", action);
|
||||||
if let Some(uid) = server.state.ecs().uid_from_entity(entity) {
|
if let Some(uid) = server.state.ecs().uid_from_entity(entity) {
|
||||||
let mut trades = server.state.ecs().write_resource::<Trades>();
|
let mut trades = server.state.ecs().write_resource::<Trades>();
|
||||||
if let TradeAction::Decline = action {
|
if let TradeAction::Decline = action {
|
||||||
@ -86,8 +103,10 @@ pub fn handle_process_trade_action(
|
|||||||
trades.process_trade_action(trade_id, uid, action, get_inventory);
|
trades.process_trade_action(trade_id, uid, action, get_inventory);
|
||||||
}
|
}
|
||||||
if let Entry::Occupied(entry) = trades.trades.entry(trade_id) {
|
if let Entry::Occupied(entry) = trades.trades.entry(trade_id) {
|
||||||
|
println!("trade entry occupied: {:?}", entry);
|
||||||
let parties = entry.get().parties;
|
let parties = entry.get().parties;
|
||||||
if entry.get().should_commit() {
|
if entry.get().should_commit() {
|
||||||
|
println!("trade should be committed");
|
||||||
let result = commit_trade(server.state.ecs(), entry.get());
|
let result = commit_trade(server.state.ecs(), entry.get());
|
||||||
entry.remove();
|
entry.remove();
|
||||||
for party in parties.iter() {
|
for party in parties.iter() {
|
||||||
@ -101,6 +120,7 @@ pub fn handle_process_trade_action(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
println!("trade is not ready to be committed");
|
||||||
let mut entities: [Option<specs::Entity>; 2] = [None, None];
|
let mut entities: [Option<specs::Entity>; 2] = [None, None];
|
||||||
let mut inventories: [Option<ReducedInventory>; 2] = [None, None];
|
let mut inventories: [Option<ReducedInventory>; 2] = [None, None];
|
||||||
let mut prices = None;
|
let mut prices = None;
|
||||||
@ -132,6 +152,7 @@ pub fn handle_process_trade_action(
|
|||||||
drop(agents);
|
drop(agents);
|
||||||
for party in entities.iter() {
|
for party in entities.iter() {
|
||||||
if let Some(e) = *party {
|
if let Some(e) = *party {
|
||||||
|
println!("sending trade update");
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
e,
|
e,
|
||||||
ServerGeneral::UpdatePendingTrade(
|
ServerGeneral::UpdatePendingTrade(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use common::{
|
use common::{
|
||||||
comp::inventory::loadout_builder::LoadoutBuilder,
|
comp::{agent::Occupation, inventory::loadout_builder::LoadoutBuilder},
|
||||||
resources::Time,
|
resources::Time,
|
||||||
rtsim::{Memory, MemoryItem},
|
rtsim::{Memory, MemoryItem},
|
||||||
store::Id,
|
store::Id,
|
||||||
@ -23,6 +23,7 @@ pub struct Entity {
|
|||||||
pub controller: RtSimController,
|
pub controller: RtSimController,
|
||||||
|
|
||||||
pub brain: Brain,
|
pub brain: Brain,
|
||||||
|
pub occupation: Option<Occupation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const PERM_SPECIES: u32 = 0;
|
const PERM_SPECIES: u32 = 0;
|
||||||
@ -30,6 +31,7 @@ const PERM_BODY: u32 = 1;
|
|||||||
const PERM_LOADOUT: u32 = 2;
|
const PERM_LOADOUT: u32 = 2;
|
||||||
const PERM_LEVEL: u32 = 3;
|
const PERM_LEVEL: u32 = 3;
|
||||||
const PERM_GENUS: u32 = 4;
|
const PERM_GENUS: u32 = 4;
|
||||||
|
const PERM_OCCUPATION: u32 = 5;
|
||||||
|
|
||||||
impl Entity {
|
impl Entity {
|
||||||
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed + perm) }
|
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed + perm) }
|
||||||
@ -59,6 +61,18 @@ impl Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_occupation(&self) -> Option<Occupation> {
|
||||||
|
// If the body is humanoid
|
||||||
|
if self.rng(PERM_GENUS).gen::<f32>() > 0.50 {
|
||||||
|
match self.rng(PERM_OCCUPATION).gen::<f32>() {
|
||||||
|
x if x < 0.5 => Some(Occupation::TravelingMercenary),
|
||||||
|
_ => Some(Occupation::Traveler),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_name(&self) -> String {
|
pub fn get_name(&self) -> String {
|
||||||
use common::{generation::get_npc_name, npc::NPC_NAMES};
|
use common::{generation::get_npc_name, npc::NPC_NAMES};
|
||||||
let npc_names = NPC_NAMES.read();
|
let npc_names = NPC_NAMES.read();
|
||||||
@ -120,9 +134,15 @@ impl Entity {
|
|||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let chest = Some(comp::Item::new_from_asset_expect(
|
let chest = if matches!(self.get_occupation(), Some(Occupation::TravelingMercenary)) {
|
||||||
|
Some(comp::Item::new_from_asset_expect(
|
||||||
|
"common.items.armor.plate.chest",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Some(comp::Item::new_from_asset_expect(
|
||||||
"common.items.npc_armor.chest.leather_blue",
|
"common.items.npc_armor.chest.leather_blue",
|
||||||
));
|
))
|
||||||
|
};
|
||||||
let pants = Some(comp::Item::new_from_asset_expect(
|
let pants = Some(comp::Item::new_from_asset_expect(
|
||||||
"common.items.npc_armor.pants.leather_blue",
|
"common.items.npc_armor.pants.leather_blue",
|
||||||
));
|
));
|
||||||
|
@ -120,6 +120,7 @@ pub fn init(state: &mut State, #[cfg(feature = "worldgen")] world: &world::World
|
|||||||
controller: RtSimController::default(),
|
controller: RtSimController::default(),
|
||||||
last_tick: 0,
|
last_tick: 0,
|
||||||
brain: Default::default(),
|
brain: Default::default(),
|
||||||
|
occupation: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use common::{
|
|||||||
combat,
|
combat,
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
|
Agent, agent::AgentEvent,
|
||||||
skills::{GeneralSkill, Skill},
|
skills::{GeneralSkill, Skill},
|
||||||
Group, Inventory,
|
Group, Inventory,
|
||||||
},
|
},
|
||||||
@ -581,6 +582,11 @@ impl StateExt for State {
|
|||||||
client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (agent, pos) in (&mut ecs.write_storage::<Agent>(), &positions).join() {
|
||||||
|
if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) {
|
||||||
|
agent.inbox.push_front(AgentEvent::IncomingChat(resolved_msg.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
comp::ChatType::Region(uid) => {
|
comp::ChatType::Region(uid) => {
|
||||||
|
@ -2,10 +2,11 @@ use crate::rtsim::{Entity as RtSimData, RtSim};
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
agent::{AgentEvent, Tactic, Target, DEFAULT_INTERACTION_TIME, TRADE_INTERACTION_TIME},
|
agent::{InteractionOffer, Occupation, AgentEvent, Tactic, Target, DEFAULT_INTERACTION_TIME, TRADE_INTERACTION_TIME},
|
||||||
buff::{BuffKind, Buffs},
|
buff::{BuffKind, Buffs},
|
||||||
compass::{Direction, Distance},
|
compass::{Direction, Distance},
|
||||||
dialogue::{MoodContext, MoodState, Subject},
|
dialogue::{MoodContext, MoodState, Subject},
|
||||||
|
chat::ChatType,
|
||||||
group,
|
group,
|
||||||
inventory::{item::ItemTag, slot::EquipSlot},
|
inventory::{item::ItemTag, slot::EquipSlot},
|
||||||
invite::{InviteKind, InviteResponse},
|
invite::{InviteKind, InviteResponse},
|
||||||
@ -562,6 +563,7 @@ impl<'a> AgentData<'a> {
|
|||||||
{
|
{
|
||||||
self.interact(agent, controller, &read_data, event_emitter);
|
self.interact(agent, controller, &read_data, event_emitter);
|
||||||
} else {
|
} else {
|
||||||
|
agent.offer = None;
|
||||||
agent.action_timer = 0.0;
|
agent.action_timer = 0.0;
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
controller.actions.push(ControlAction::Stand);
|
controller.actions.push(ControlAction::Stand);
|
||||||
@ -710,6 +712,7 @@ impl<'a> AgentData<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
agent.action_timer = 0.0;
|
agent.action_timer = 0.0;
|
||||||
|
if agent.rtsim_controller.travel_to.is_some() && !matches!(self.alignment, Some(Alignment::Owned(_))) {
|
||||||
if let Some((travel_to, _destination)) = &agent.rtsim_controller.travel_to {
|
if let Some((travel_to, _destination)) = &agent.rtsim_controller.travel_to {
|
||||||
// if it has an rtsim destination and can fly then it should
|
// if it has an rtsim destination and can fly then it should
|
||||||
// if it is flying and bumps something above it then it should move down
|
// if it is flying and bumps something above it then it should move down
|
||||||
@ -825,6 +828,7 @@ impl<'a> AgentData<'a> {
|
|||||||
controller.actions.push(ControlAction::Unwield);
|
controller.actions.push(ControlAction::Unwield);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
agent.bearing += Vec2::new(
|
agent.bearing += Vec2::new(
|
||||||
thread_rng().gen::<f32>() - 0.5,
|
thread_rng().gen::<f32>() - 0.5,
|
||||||
@ -860,8 +864,12 @@ impl<'a> AgentData<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if agent.bearing.magnitude_squared() > 0.5f32.powi(2) {
|
if agent.bearing.magnitude_squared() > 0.5f32.powi(2) {
|
||||||
|
if matches!(Some(Alignment::Owned(_)), data.alignment) {
|
||||||
|
controller.inputs.move_dir = agent.bearing * 0.30;
|
||||||
|
} else {
|
||||||
controller.inputs.move_dir = agent.bearing * 0.65;
|
controller.inputs.move_dir = agent.bearing * 0.65;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Put away weapon
|
// Put away weapon
|
||||||
if thread_rng().gen_bool(0.1)
|
if thread_rng().gen_bool(0.1)
|
||||||
@ -901,9 +909,71 @@ impl<'a> AgentData<'a> {
|
|||||||
// .events
|
// .events
|
||||||
// .push(ControlEvent::InviteResponse(InviteResponse::Decline));
|
// .push(ControlEvent::InviteResponse(InviteResponse::Decline));
|
||||||
// }
|
// }
|
||||||
|
// TODO allow npcs to handle more than one msg per tick
|
||||||
|
if agent.target.is_some() {
|
||||||
|
agent.action_timer += read_data.dt.0 / 10.0;
|
||||||
|
} else {
|
||||||
agent.action_timer += read_data.dt.0;
|
agent.action_timer += read_data.dt.0;
|
||||||
|
}
|
||||||
let msg = agent.inbox.pop_back();
|
let msg = agent.inbox.pop_back();
|
||||||
|
if msg.is_some() {
|
||||||
|
println!("agent message: {:?}", msg);
|
||||||
|
}
|
||||||
match msg {
|
match msg {
|
||||||
|
Some(AgentEvent::IncomingChat(chat_msg)) => {
|
||||||
|
if agent.behavior.can(BehaviorCapability::SPEAK) {
|
||||||
|
if let ChatType::Say(by) = chat_msg.chat_type {
|
||||||
|
if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id())
|
||||||
|
{
|
||||||
|
agent.target = Some(Target {
|
||||||
|
target,
|
||||||
|
hostile: false,
|
||||||
|
selected_at: read_data.time.0,
|
||||||
|
});
|
||||||
|
match agent.offer {
|
||||||
|
Some(InteractionOffer::Trade) => {
|
||||||
|
if chat_msg.message == "yes".to_string() {
|
||||||
|
let msg = "Come see my wares".to_string();
|
||||||
|
event_emitter.emit(ServerEvent::Chat(
|
||||||
|
UnresolvedChatMsg::npc(*self.uid, msg),
|
||||||
|
));
|
||||||
|
controller.events.push(ControlEvent::InitiateInvite(
|
||||||
|
by,
|
||||||
|
InviteKind::Trade,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(InteractionOffer::MercenaryHire) => {
|
||||||
|
if chat_msg.message == "yes".to_string() {
|
||||||
|
if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id()) {
|
||||||
|
if let Some(tgt_stats) = read_data.stats.get(target) {
|
||||||
|
agent.rtsim_controller.events.push(
|
||||||
|
RtSimEvent::AddMemory(Memory {
|
||||||
|
item: MemoryItem::MercenaryContract {
|
||||||
|
name: tgt_stats.name.clone(),
|
||||||
|
},
|
||||||
|
time_to_forget: read_data.time.0 + 1440.0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controller.events.push(ControlEvent::InitiateInvite(
|
||||||
|
by,
|
||||||
|
InviteKind::Trade,
|
||||||
|
));
|
||||||
|
agent.behavior.set(BehaviorState::TRADING);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
event_emitter.emit(ServerEvent::Chat(
|
||||||
|
UnresolvedChatMsg::npc(*self.uid, chat_msg.message),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Some(AgentEvent::Talk(by, subject)) => {
|
Some(AgentEvent::Talk(by, subject)) => {
|
||||||
if agent.behavior.can(BehaviorCapability::SPEAK) {
|
if agent.behavior.can(BehaviorCapability::SPEAK) {
|
||||||
if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id())
|
if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id())
|
||||||
@ -934,6 +1004,12 @@ impl<'a> AgentData<'a> {
|
|||||||
time_to_forget: read_data.time.0 + 600.0,
|
time_to_forget: read_data.time.0 + 600.0,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
match rtsim_entity.get_occupation() {
|
||||||
|
Some(Occupation::TravelingMercenary) => {
|
||||||
|
agent.offer = Some(InteractionOffer::MercenaryHire);
|
||||||
|
"You look like an adventurer in need of an armed consort. For 1000 c. I will protect you for the next 24 hours.".to_string()
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
if rtsim_entity
|
if rtsim_entity
|
||||||
.brain
|
.brain
|
||||||
.remembers_character(&tgt_stats.name)
|
.remembers_character(&tgt_stats.name)
|
||||||
@ -950,6 +1026,8 @@ impl<'a> AgentData<'a> {
|
|||||||
destination_name
|
destination_name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"I'm heading to {}! Want to come along?",
|
"I'm heading to {}! Want to come along?",
|
||||||
@ -964,6 +1042,7 @@ impl<'a> AgentData<'a> {
|
|||||||
event_emitter.emit(ServerEvent::Chat(
|
event_emitter.emit(ServerEvent::Chat(
|
||||||
UnresolvedChatMsg::npc(*self.uid, msg),
|
UnresolvedChatMsg::npc(*self.uid, msg),
|
||||||
));
|
));
|
||||||
|
agent.offer = Some(InteractionOffer::Trade);
|
||||||
} else {
|
} else {
|
||||||
let msg = "npc.speech.villager".to_string();
|
let msg = "npc.speech.villager".to_string();
|
||||||
event_emitter.emit(ServerEvent::Chat(
|
event_emitter.emit(ServerEvent::Chat(
|
||||||
@ -1165,6 +1244,14 @@ impl<'a> AgentData<'a> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(AgentEvent::TradeAccepted(with)) => {
|
Some(AgentEvent::TradeAccepted(with)) => {
|
||||||
|
//let msg = "I will guard you with my life until the contract ends or your coffers run dry".to_string();
|
||||||
|
//event_emitter.emit(ServerEvent::Chat(
|
||||||
|
// UnresolvedChatMsg::npc(*self.uid, msg),
|
||||||
|
//));
|
||||||
|
//controller.events.push(ControlEvent::InitiateInvite(
|
||||||
|
// by,
|
||||||
|
// InviteKind::JoinGroup,
|
||||||
|
//));
|
||||||
if !agent.behavior.is(BehaviorState::TRADING) {
|
if !agent.behavior.is(BehaviorState::TRADING) {
|
||||||
if let Some(target) =
|
if let Some(target) =
|
||||||
read_data.uid_allocator.retrieve_entity_internal(with.id())
|
read_data.uid_allocator.retrieve_entity_internal(with.id())
|
||||||
@ -1183,10 +1270,26 @@ impl<'a> AgentData<'a> {
|
|||||||
if agent.behavior.is(BehaviorState::TRADING) {
|
if agent.behavior.is(BehaviorState::TRADING) {
|
||||||
match result {
|
match result {
|
||||||
TradeResult::Completed => {
|
TradeResult::Completed => {
|
||||||
|
if matches!(agent.offer, Some(InteractionOffer::MercenaryHire)) {
|
||||||
|
agent.offer = None;
|
||||||
|
let msg = "I will guard you with my life until the contract ends or your coffers run dry".to_string();
|
||||||
|
event_emitter.emit(ServerEvent::Chat(
|
||||||
|
UnresolvedChatMsg::npc(*self.uid, msg),
|
||||||
|
));
|
||||||
|
if let Some(target) = &agent.target {
|
||||||
|
if let Some(by) = read_data.uids.get(target.target) {
|
||||||
|
controller.events.push(ControlEvent::InitiateInvite(
|
||||||
|
*by,
|
||||||
|
InviteKind::JoinGroup,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
||||||
*self.uid,
|
*self.uid,
|
||||||
"npc.speech.merchant_trade_successful".to_string(),
|
"npc.speech.merchant_trade_successful".to_string(),
|
||||||
)))
|
)));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
_ => event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
||||||
*self.uid,
|
*self.uid,
|
||||||
@ -1194,6 +1297,7 @@ impl<'a> AgentData<'a> {
|
|||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
agent.behavior.unset(BehaviorState::TRADING);
|
agent.behavior.unset(BehaviorState::TRADING);
|
||||||
|
agent.action_timer = DEFAULT_INTERACTION_TIME + read_data.dt.0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(AgentEvent::UpdatePendingTrade(boxval)) => {
|
Some(AgentEvent::UpdatePendingTrade(boxval)) => {
|
||||||
|
@ -789,6 +789,10 @@ impl<'a> Widget for Group<'a> {
|
|||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.group.invite_to_join")
|
.get("hud.group.invite_to_join")
|
||||||
.replace("{name}", &name),
|
.replace("{name}", &name),
|
||||||
|
InviteKind::JoinGroup => self
|
||||||
|
.localized_strings
|
||||||
|
.get("hud.group.invite_to_join_your_group")
|
||||||
|
.replace("{name}", &name),
|
||||||
InviteKind::Trade => self
|
InviteKind::Trade => self
|
||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.group.invite_to_trade")
|
.get("hud.group.invite_to_trade")
|
||||||
|
@ -146,6 +146,7 @@ impl SessionState {
|
|||||||
// not be grammatical in some languages)
|
// not be grammatical in some languages)
|
||||||
let kind_str = match kind {
|
let kind_str = match kind {
|
||||||
InviteKind::Group => "Group",
|
InviteKind::Group => "Group",
|
||||||
|
InviteKind::JoinGroup => "Join Group",
|
||||||
InviteKind::Trade => "Trade",
|
InviteKind::Trade => "Trade",
|
||||||
};
|
};
|
||||||
let target_name = match client.player_list().get(&target) {
|
let target_name = match client.player_list().get(&target) {
|
||||||
|
Loading…
Reference in New Issue
Block a user