mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Make the trading AI for pets only accept food.
This commit is contained in:
parent
ff781198d3
commit
e6576f0cf3
@ -97,6 +97,26 @@ bitflags::bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Copy, Clone, Debug)]
|
||||||
|
pub enum TradingBehavior {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
RequireBalanced {
|
||||||
|
trade_site: SiteId,
|
||||||
|
},
|
||||||
|
AcceptFood,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TradingBehavior {
|
||||||
|
fn can_trade(&self, alignment: Option<Alignment>, counterparty: Uid) -> bool {
|
||||||
|
match self {
|
||||||
|
TradingBehavior::RequireBalanced { .. } => true,
|
||||||
|
TradingBehavior::AcceptFood => alignment == Some(Alignment::Owned(counterparty)),
|
||||||
|
TradingBehavior::None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// # Behavior Component
|
/// # Behavior Component
|
||||||
/// This component allow an Entity to register one or more behavior tags.
|
/// This component allow an Entity to register one or more behavior tags.
|
||||||
/// These tags act as flags of what an Entity can do, or what it is doing.
|
/// These tags act as flags of what an Entity can do, or what it is doing.
|
||||||
@ -106,7 +126,7 @@ bitflags::bitflags! {
|
|||||||
pub struct Behavior {
|
pub struct Behavior {
|
||||||
capabilities: BehaviorCapability,
|
capabilities: BehaviorCapability,
|
||||||
state: BehaviorState,
|
state: BehaviorState,
|
||||||
pub trade_site: Option<SiteId>,
|
pub trading_behavior: TradingBehavior,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BehaviorCapability> for Behavior {
|
impl From<BehaviorCapability> for Behavior {
|
||||||
@ -114,7 +134,7 @@ impl From<BehaviorCapability> for Behavior {
|
|||||||
Behavior {
|
Behavior {
|
||||||
capabilities,
|
capabilities,
|
||||||
state: BehaviorState::default(),
|
state: BehaviorState::default(),
|
||||||
trade_site: None,
|
trading_behavior: TradingBehavior::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +157,9 @@ impl Behavior {
|
|||||||
/// Set trade_site if Option is Some
|
/// Set trade_site if Option is Some
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_trade_site(mut self, trade_site: Option<SiteId>) -> Self {
|
pub fn with_trade_site(mut self, trade_site: Option<SiteId>) -> Self {
|
||||||
self.trade_site = trade_site;
|
if let Some(trade_site) = trade_site {
|
||||||
|
self.trading_behavior = TradingBehavior::RequireBalanced { trade_site };
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +180,7 @@ impl Behavior {
|
|||||||
|
|
||||||
/// Check if the Behavior is able to trade
|
/// Check if the Behavior is able to trade
|
||||||
pub fn can_trade(&self, alignment: Option<Alignment>, counterparty: Uid) -> bool {
|
pub fn can_trade(&self, alignment: Option<Alignment>, counterparty: Uid) -> bool {
|
||||||
self.trade_site.is_some() || alignment == Some(Alignment::Owned(counterparty))
|
self.trading_behavior.can_trade(alignment, counterparty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a state to the Behavior
|
/// Set a state to the Behavior
|
||||||
@ -169,6 +191,15 @@ impl Behavior {
|
|||||||
|
|
||||||
/// Check if the Behavior has a specific state
|
/// Check if the Behavior has a specific state
|
||||||
pub fn is(&self, state: BehaviorState) -> bool { self.state.contains(state) }
|
pub fn is(&self, state: BehaviorState) -> bool { self.state.contains(state) }
|
||||||
|
|
||||||
|
/// Get the trade site at which this behavior evaluates prices, if it does
|
||||||
|
pub fn trade_site(&self) -> Option<SiteId> {
|
||||||
|
if let TradingBehavior::RequireBalanced { trade_site } = self.trading_behavior {
|
||||||
|
Some(trade_site)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -55,7 +55,10 @@ pub use self::{
|
|||||||
MAX_ABILITIES,
|
MAX_ABILITIES,
|
||||||
},
|
},
|
||||||
admin::{Admin, AdminRole},
|
admin::{Admin, AdminRole},
|
||||||
agent::{Agent, Alignment, Behavior, BehaviorCapability, BehaviorState, PidController},
|
agent::{
|
||||||
|
Agent, Alignment, Behavior, BehaviorCapability, BehaviorState, PidController,
|
||||||
|
TradingBehavior,
|
||||||
|
},
|
||||||
anchor::Anchor,
|
anchor::Anchor,
|
||||||
aura::{Aura, AuraChange, AuraKind, Auras},
|
aura::{Aura, AuraChange, AuraKind, Auras},
|
||||||
beam::{Beam, BeamSegment},
|
beam::{Beam, BeamSegment},
|
||||||
|
@ -9,7 +9,7 @@ use common::{
|
|||||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||||
shockwave, Agent, Alignment, Anchor, BehaviorCapability, Body, Health, Inventory, ItemDrop,
|
shockwave, Agent, Alignment, Anchor, BehaviorCapability, Body, Health, Inventory, ItemDrop,
|
||||||
LightEmitter, Object, Ori, PidController, Poise, Pos, Projectile, Scale, SkillSet, Stats,
|
LightEmitter, Object, Ori, PidController, Poise, Pos, Projectile, Scale, SkillSet, Stats,
|
||||||
Vel, WaypointArea,
|
TradingBehavior, Vel, WaypointArea,
|
||||||
},
|
},
|
||||||
event::{EventBus, UpdateCharacterMetadata},
|
event::{EventBus, UpdateCharacterMetadata},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
@ -104,6 +104,7 @@ pub fn handle_create_npc(
|
|||||||
if let Some(agent) = &mut agent {
|
if let Some(agent) = &mut agent {
|
||||||
if let Alignment::Owned(_) = &alignment {
|
if let Alignment::Owned(_) = &alignment {
|
||||||
agent.behavior.allow(BehaviorCapability::TRADE);
|
agent.behavior.allow(BehaviorCapability::TRADE);
|
||||||
|
agent.behavior.trading_behavior = TradingBehavior::AcceptFood;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,13 +246,13 @@ pub fn handle_invite_accept(server: &mut Server, entity: Entity) {
|
|||||||
.get(inviter)
|
.get(inviter)
|
||||||
.and_then(|a| {
|
.and_then(|a| {
|
||||||
a.behavior
|
a.behavior
|
||||||
.trade_site
|
.trade_site()
|
||||||
.and_then(|id| index.get_site_prices(id))
|
.and_then(|id| index.get_site_prices(id))
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
agents.get(entity).and_then(|a| {
|
agents.get(entity).and_then(|a| {
|
||||||
a.behavior
|
a.behavior
|
||||||
.trade_site
|
.trade_site()
|
||||||
.and_then(|id| index.get_site_prices(id))
|
.and_then(|id| index.get_site_prices(id))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -35,7 +35,7 @@ fn notify_agent_prices(
|
|||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
event: AgentEvent,
|
event: AgentEvent,
|
||||||
) {
|
) {
|
||||||
if let Some((site_id, agent)) = agents.get_mut(entity).map(|a| (a.behavior.trade_site, a)) {
|
if let Some((site_id, agent)) = agents.get_mut(entity).map(|a| (a.behavior.trade_site(), a)) {
|
||||||
if let AgentEvent::UpdatePendingTrade(boxval) = event {
|
if let AgentEvent::UpdatePendingTrade(boxval) = event {
|
||||||
// Prefer using this Agent's price data, but use the counterparty's price
|
// Prefer using this Agent's price data, but use the counterparty's price
|
||||||
// data if we don't have price data
|
// data if we don't have price data
|
||||||
@ -125,7 +125,7 @@ pub(super) fn handle_process_trade_action(
|
|||||||
prices = prices.or_else(|| {
|
prices = prices.or_else(|| {
|
||||||
agents
|
agents
|
||||||
.get(e)
|
.get(e)
|
||||||
.and_then(|a| a.behavior.trade_site)
|
.and_then(|a| a.behavior.trade_site())
|
||||||
.and_then(|id| server.index.get_site_prices(id))
|
.and_then(|id| server.index.get_site_prices(id))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::{client::Client, events::update_map_markers};
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
self, anchor::Anchor, group::GroupManager, Agent, Alignment, Behavior, BehaviorCapability,
|
self, anchor::Anchor, group::GroupManager, Agent, Alignment, Behavior, BehaviorCapability,
|
||||||
Pet,
|
Pet, TradingBehavior,
|
||||||
},
|
},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
@ -54,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
|
// Create an agent for this entity using its body
|
||||||
if let Some(body) = ecs.read_storage().get(pet_entity) {
|
if let Some(body) = ecs.read_storage().get(pet_entity) {
|
||||||
let agent = Agent::from_body(body).with_behavior(
|
let mut agent = Agent::from_body(body).with_behavior(
|
||||||
Behavior::default().maybe_with_capabilities(Some(BehaviorCapability::TRADE)),
|
Behavior::default().maybe_with_capabilities(Some(BehaviorCapability::TRADE)),
|
||||||
);
|
);
|
||||||
|
agent.behavior.trading_behavior = TradingBehavior::AcceptFood;
|
||||||
let _ = ecs.write_storage().insert(pet_entity, agent);
|
let _ = ecs.write_storage().insert(pet_entity, agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,10 @@ use common::{
|
|||||||
agent::{AgentEvent, Target, TimerAction},
|
agent::{AgentEvent, Target, TimerAction},
|
||||||
compass::{Direction, Distance},
|
compass::{Direction, Distance},
|
||||||
dialogue::{MoodContext, MoodState, Subject},
|
dialogue::{MoodContext, MoodState, Subject},
|
||||||
|
inventory::item::{ItemTag, MaterialStatManifest},
|
||||||
invite::{InviteKind, InviteResponse},
|
invite::{InviteKind, InviteResponse},
|
||||||
BehaviorState, ControlAction, UnresolvedChatMsg, UtteranceKind,
|
tool::AbilityMap,
|
||||||
|
BehaviorState, ControlAction, Item, TradingBehavior, UnresolvedChatMsg, UtteranceKind,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
rtsim::{Memory, MemoryItem, RtSimEvent},
|
rtsim::{Memory, MemoryItem, RtSimEvent},
|
||||||
@ -511,58 +513,100 @@ pub fn handle_inbox_update_pending_trade(bdata: &mut BehaviorData) -> bool {
|
|||||||
let (tradeid, pending, prices, inventories) = *boxval;
|
let (tradeid, pending, prices, inventories) = *boxval;
|
||||||
if agent.behavior.is(BehaviorState::TRADING) {
|
if agent.behavior.is(BehaviorState::TRADING) {
|
||||||
let who = usize::from(!agent.behavior.is(BehaviorState::TRADING_ISSUER));
|
let who = usize::from(!agent.behavior.is(BehaviorState::TRADING_ISSUER));
|
||||||
let balance0: f32 = prices.balance(&pending.offers, &inventories, 1 - who, true);
|
match agent.behavior.trading_behavior {
|
||||||
let balance1: f32 = prices.balance(&pending.offers, &inventories, who, false);
|
TradingBehavior::RequireBalanced { .. } => {
|
||||||
if balance0 >= balance1 {
|
let balance0: f32 =
|
||||||
// If the trade is favourable to us, only send an accept message if we're
|
prices.balance(&pending.offers, &inventories, 1 - who, true);
|
||||||
// not already accepting (since otherwise, spam-clicking the accept button
|
let balance1: f32 = prices.balance(&pending.offers, &inventories, who, false);
|
||||||
// results in lagging and moving to the review phase of an unfavorable trade
|
if balance0 >= balance1 {
|
||||||
// (although since the phase is included in the message, this shouldn't
|
// If the trade is favourable to us, only send an accept message if we're
|
||||||
// result in fully accepting an unfavourable trade))
|
// not already accepting (since otherwise, spam-clicking the accept button
|
||||||
if !pending.accept_flags[who] && !pending.is_empty_trade() {
|
// results in lagging and moving to the review phase of an unfavorable trade
|
||||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
// (although since the phase is included in the message, this shouldn't
|
||||||
*agent_data.entity,
|
// result in fully accepting an unfavourable trade))
|
||||||
tradeid,
|
if !pending.accept_flags[who] && !pending.is_empty_trade() {
|
||||||
TradeAction::Accept(pending.phase),
|
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||||
));
|
*agent_data.entity,
|
||||||
tracing::trace!(?tradeid, ?balance0, ?balance1, "Accept Pending Trade");
|
tradeid,
|
||||||
}
|
TradeAction::Accept(pending.phase),
|
||||||
} else {
|
));
|
||||||
if balance1 > 0.0 {
|
tracing::trace!(?tradeid, ?balance0, ?balance1, "Accept Pending Trade");
|
||||||
let msg = format!(
|
|
||||||
"That only covers {:.0}% of my costs!",
|
|
||||||
(balance0 / balance1 * 100.0).floor()
|
|
||||||
);
|
|
||||||
if let Some(tgt_data) = &agent.target {
|
|
||||||
// If talking with someone in particular, "tell" it only to them
|
|
||||||
if let Some(with) = read_data.uids.get(tgt_data.target) {
|
|
||||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc_tell(
|
|
||||||
*agent_data.uid,
|
|
||||||
*with,
|
|
||||||
msg,
|
|
||||||
)));
|
|
||||||
} else {
|
|
||||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc_say(
|
|
||||||
*agent_data.uid,
|
|
||||||
msg,
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc_say(
|
if balance1 > 0.0 {
|
||||||
*agent_data.uid,
|
let msg = format!(
|
||||||
msg,
|
"That only covers {:.0}% of my costs!",
|
||||||
)));
|
(balance0 / balance1 * 100.0).floor()
|
||||||
|
);
|
||||||
|
if let Some(tgt_data) = &agent.target {
|
||||||
|
// If talking with someone in particular, "tell" it only to them
|
||||||
|
if let Some(with) = read_data.uids.get(tgt_data.target) {
|
||||||
|
event_emitter.emit(ServerEvent::Chat(
|
||||||
|
UnresolvedChatMsg::npc_tell(*agent_data.uid, *with, msg),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
event_emitter.emit(ServerEvent::Chat(
|
||||||
|
UnresolvedChatMsg::npc_say(*agent_data.uid, msg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc_say(
|
||||||
|
*agent_data.uid,
|
||||||
|
msg,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pending.phase != TradePhase::Mutate {
|
||||||
|
// we got into the review phase but without balanced goods, decline
|
||||||
|
agent.behavior.unset(BehaviorState::TRADING);
|
||||||
|
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||||
|
*agent_data.entity,
|
||||||
|
tradeid,
|
||||||
|
TradeAction::Decline,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
if pending.phase != TradePhase::Mutate {
|
TradingBehavior::AcceptFood => {
|
||||||
// we got into the review phase but without balanced goods, decline
|
let mut only_food = true;
|
||||||
|
let ability_map = AbilityMap::load().read();
|
||||||
|
let msm = MaterialStatManifest::load().read();
|
||||||
|
if let Some(ri) = &inventories[1 - who] {
|
||||||
|
for (slot, _) in pending.offers[1 - who].iter() {
|
||||||
|
if let Some(item) = ri.inventory.get(slot) {
|
||||||
|
if let Ok(item) = Item::new_from_item_definition_id(
|
||||||
|
item.name.as_ref(),
|
||||||
|
&ability_map,
|
||||||
|
&msm,
|
||||||
|
) {
|
||||||
|
if !item.tags().contains(&ItemTag::Food) {
|
||||||
|
only_food = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !pending.accept_flags[who]
|
||||||
|
&& pending.offers[who].is_empty()
|
||||||
|
&& !pending.offers[1 - who].is_empty()
|
||||||
|
&& only_food
|
||||||
|
{
|
||||||
|
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||||
|
*agent_data.entity,
|
||||||
|
tradeid,
|
||||||
|
TradeAction::Accept(pending.phase),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TradingBehavior::None => {
|
||||||
agent.behavior.unset(BehaviorState::TRADING);
|
agent.behavior.unset(BehaviorState::TRADING);
|
||||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||||
*agent_data.entity,
|
*agent_data.entity,
|
||||||
tradeid,
|
tradeid,
|
||||||
TradeAction::Decline,
|
TradeAction::Decline,
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user