Allow pets to be traded with.

This commit is contained in:
Avi Weinstock 2022-09-21 19:21:46 -04:00
parent dd5523785c
commit c62bc0dd0d
8 changed files with 99 additions and 49 deletions

View File

@ -86,6 +86,7 @@ bitflags::bitflags! {
#[derive(Default)]
pub struct BehaviorCapability: u8 {
const SPEAK = 0b00000001;
const TRADE = 0b00000010;
}
}
bitflags::bitflags! {
@ -156,7 +157,9 @@ impl Behavior {
}
/// Check if the Behavior is able to trade
pub fn can_trade(&self) -> bool { self.trade_site.is_some() }
pub fn can_trade(&self, alignment: Option<&Alignment>, counterparty: Uid) -> bool {
self.trade_site.is_some() || alignment == Some(&Alignment::Owned(counterparty))
}
/// Set a state to the Behavior
pub fn set(&mut self, state: BehaviorState) { self.state.set(state, true) }

View File

@ -7,9 +7,9 @@ use common::{
aura::{Aura, AuraKind, AuraTarget},
beam,
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
shockwave, Agent, Alignment, Anchor, Body, Health, Inventory, ItemDrop, LightEmitter,
Object, Ori, PidController, Poise, Pos, Projectile, Scale, SkillSet, Stats, Vel,
WaypointArea,
shockwave, Agent, Alignment, Anchor, BehaviorCapability, Body, Health, Inventory, ItemDrop,
LightEmitter, Object, Ori, PidController, Poise, Pos, Projectile, Scale, SkillSet, Stats,
Vel, WaypointArea,
},
event::{EventBus, UpdateCharacterMetadata},
lottery::LootSpec,
@ -98,10 +98,18 @@ pub fn handle_create_npc(
let entity = server
.state
.create_npc(pos, stats, skill_set, health, poise, inventory, body)
.with(scale)
.with(alignment);
.with(scale);
let entity = if let Some(agent) = agent.into() {
let mut agent = agent.into();
if let Some(agent) = &mut agent {
if let Alignment::Owned(_) = &alignment {
agent.behavior.allow(BehaviorCapability::TRADE);
}
}
let entity = entity.with(alignment);
let entity = if let Some(agent) = agent {
entity.with(agent)
} else {
entity

View File

@ -35,20 +35,18 @@ fn notify_agent_prices(
entity: EcsEntity,
event: AgentEvent,
) {
if let Some((Some(site_id), agent)) = agents.get_mut(entity).map(|a| (a.behavior.trade_site, a))
{
let prices = index.get_site_prices(site_id);
if let Some((site_id, agent)) = agents.get_mut(entity).map(|a| (a.behavior.trade_site, a)) {
if let AgentEvent::UpdatePendingTrade(boxval) = event {
// Prefer using this Agent's price data, but use the counterparty's price
// data if we don't have price data
let prices = site_id
.and_then(|site_id| index.get_site_prices(site_id))
.unwrap_or(boxval.2);
// Box<(tid, pend, _, inventories)>) = event {
agent
.inbox
.push_back(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,
prices.unwrap_or(boxval.2),
boxval.3,
boxval.0, boxval.1, prices, boxval.3,
))));
}
}

View File

@ -1,6 +1,9 @@
use crate::{client::Client, events::update_map_markers};
use common::{
comp::{self, anchor::Anchor, group::GroupManager, Agent, Alignment, Pet},
comp::{
self, anchor::Anchor, group::GroupManager, Agent, Alignment, Behavior, BehaviorCapability,
Pet,
},
uid::Uid,
};
use common_net::msg::ServerGeneral;
@ -51,9 +54,10 @@ fn tame_pet_internal(ecs: &specs::World, pet_entity: Entity, owner: Entity, pet:
// Create an agent for this entity using its body
if let Some(body) = ecs.read_storage().get(pet_entity) {
let _ = ecs
.write_storage()
.insert(pet_entity, Agent::from_body(body));
let agent = Agent::from_body(body).with_behavior(
Behavior::default().maybe_with_capabilities(Some(BehaviorCapability::TRADE)),
);
let _ = ecs.write_storage().insert(pet_entity, agent);
}
// Add to group system

View File

@ -108,17 +108,21 @@ impl BehaviorTree {
/// events.
pub fn interaction(agent: &Agent) -> Self {
let is_in_combat = agent.target.map_or(false, |t| t.hostile);
if !is_in_combat && agent.behavior.can(BehaviorCapability::SPEAK) {
Self {
tree: vec![
increment_timer_deltatime,
handle_inbox_talk,
handle_inbox_trade_invite,
handle_inbox_trade_accepted,
handle_inbox_finished_trade,
handle_inbox_update_pending_trade,
],
if !is_in_combat
&& (agent.behavior.can(BehaviorCapability::SPEAK)
|| agent.behavior.can(BehaviorCapability::TRADE))
{
let mut tree: Vec<BehaviorFn> = vec![increment_timer_deltatime];
if agent.behavior.can(BehaviorCapability::SPEAK) {
tree.push(handle_inbox_talk);
}
tree.extend_from_slice(&[
handle_inbox_trade_invite,
handle_inbox_trade_accepted,
handle_inbox_finished_trade,
handle_inbox_update_pending_trade,
]);
Self { tree }
} else {
Self {
tree: vec![handle_inbox_cancel_interactions],

View File

@ -167,7 +167,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
standard_response_msg()
};
agent_data.chat_npc(msg, event_emitter);
} else if agent.behavior.can_trade() {
} else if agent.behavior.can_trade(agent_data.alignment, by) {
if !agent.behavior.is(BehaviorState::TRADING) {
controller.push_initiate_invite(by, InviteKind::Trade);
agent_data.chat_npc(
@ -250,21 +250,29 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
}
},
Subject::Trade => {
if agent.behavior.can_trade() {
if agent.behavior.can_trade(agent_data.alignment, by) {
if !agent.behavior.is(BehaviorState::TRADING) {
controller.push_initiate_invite(by, InviteKind::Trade);
agent_data.chat_npc(
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_advertisement",
agent,
event_emitter,
);
} else {
agent_data.chat_npc("npc-speech-merchant_busy", event_emitter);
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_busy",
agent,
event_emitter,
);
}
} else {
// TODO: maybe make some travellers willing to trade with
// simpler goods like potions
agent_data
.chat_npc("npc-speech-villager_decline_trade", event_emitter);
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-villager_decline_trade",
agent,
event_emitter,
);
}
},
Subject::Mood => {
@ -387,7 +395,7 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
}
if let Some(AgentEvent::TradeInvite(with)) = agent.inbox.pop_front() {
if agent.behavior.can_trade() {
if agent.behavior.can_trade(agent_data.alignment, with) {
if !agent.behavior.is(BehaviorState::TRADING) {
// stand still and looking towards the trading player
controller.push_action(ControlAction::Stand);
@ -458,10 +466,18 @@ pub fn handle_inbox_finished_trade(bdata: &mut BehaviorData) -> bool {
if agent.behavior.is(BehaviorState::TRADING) {
match result {
TradeResult::Completed => {
agent_data.chat_npc("npc-speech-merchant_trade_successful", event_emitter);
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_trade_successful",
agent,
event_emitter,
);
},
_ => {
agent_data.chat_npc("npc-speech-merchant_trade_declined", event_emitter);
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_trade_declined",
agent,
event_emitter,
);
},
}
agent.behavior.unset(BehaviorState::TRADING);
@ -587,7 +603,7 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
{
// in combat, speak to players that aren't the current target
if !target.hostile || target.target != speaker {
if agent.behavior.can_trade() {
if agent.behavior.can_trade(agent_data.alignment, *by) {
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_busy",
agent,
@ -610,12 +626,18 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
if agent.behavior.is(BehaviorState::TRADING) {
match result {
TradeResult::Completed => {
agent_data
.chat_npc("npc-speech-merchant_trade_successful", event_emitter);
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_trade_successful",
agent,
event_emitter,
);
},
_ => {
agent_data
.chat_npc("npc-speech-merchant_trade_declined", event_emitter);
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_trade_declined",
agent,
event_emitter,
);
},
}
agent.behavior.unset(BehaviorState::TRADING);
@ -631,7 +653,11 @@ pub fn handle_inbox_cancel_interactions(bdata: &mut BehaviorData) -> bool {
*tradeid,
TradeAction::Decline,
));
agent_data.chat_npc("npc-speech-merchant_trade_cancelled_hostile", event_emitter);
agent_data.chat_npc_if_allowed_to_speak(
"npc-speech-merchant_trade_cancelled_hostile",
agent,
event_emitter,
);
true
},
AgentEvent::ServerSound(_) | AgentEvent::Hurt => false,

View File

@ -517,6 +517,7 @@ impl NpcData {
.with_behavior(
Behavior::default()
.maybe_with_capabilities(can_speak.then_some(BehaviorCapability::SPEAK))
.maybe_with_capabilities(trade_for_site.map(|_| BehaviorCapability::TRADE))
.with_trade_site(trade_for_site),
)
.with_patrol_origin(pos)

View File

@ -2103,12 +2103,18 @@ impl Hud {
},
Some(comp::Alignment::Owned(owner))
if Some(*owner) == client.uid()
&& !client.is_riding()
&& is_mount.is_none()
&& is_mountable(body, bodies.get(client.entity()))
&& dist_sqr < common::consts::MAX_MOUNT_RANGE.powi(2) =>
{
vec![(GameInput::Mount, i18n.get_msg("hud-mount").to_string())]
let mut options =
vec![(GameInput::Trade, i18n.get_msg("hud-trade").to_string())];
if !client.is_riding()
&& is_mount.is_none()
&& is_mountable(body, bodies.get(client.entity()))
{
options
.push((GameInput::Mount, i18n.get_msg("hud-mount").to_string()))
}
options
},
_ => Vec::new(),
},