diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 1a40e344f3..3516e4459e 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -65,7 +65,6 @@ struct AgentData<'a> { is_gliding: bool, health: Option<&'a Health>, char_state: &'a CharacterState, - behavior: &'a mut Behavior, } #[derive(SystemData)] @@ -260,7 +259,7 @@ impl<'a> System<'a> for Sys { .and_then(|rtsim_ent| rtsim.get_entity(rtsim_ent.0)); // Package all this agent's data into a convenient struct - let mut data = AgentData { + let data = AgentData { entity: &entity, rtsim_entity, uid, @@ -282,7 +281,6 @@ impl<'a> System<'a> for Sys { is_gliding, health: read_data.healths.get(entity), char_state, - behavior, }; /////////////////////////////////////////////////////////// @@ -320,6 +318,7 @@ impl<'a> System<'a> for Sys { if hostile { data.hostile_tree( agent, + behavior, controller, &read_data, &mut event_emitter, @@ -397,6 +396,7 @@ impl<'a> System<'a> for Sys { } else { data.idle_tree( agent, + behavior, controller, &read_data, &mut event_emitter, @@ -406,6 +406,7 @@ impl<'a> System<'a> for Sys { } else { data.idle_tree( agent, + behavior, controller, &read_data, &mut event_emitter, @@ -413,11 +414,23 @@ impl<'a> System<'a> for Sys { } } else { agent.target = None; - data.idle_tree(agent, controller, &read_data, &mut event_emitter); + data.idle_tree( + agent, + behavior, + controller, + &read_data, + &mut event_emitter, + ); } } else { agent.target = None; - data.idle_tree(agent, controller, &read_data, &mut event_emitter); + data.idle_tree( + agent, + behavior, + controller, + &read_data, + &mut event_emitter, + ); } } else { // Target an entity that's attacking us if the attack was recent and we @@ -441,6 +454,7 @@ impl<'a> System<'a> for Sys { agent.target = None; data.idle_tree( agent, + behavior, controller, &read_data, &mut event_emitter, @@ -481,6 +495,7 @@ impl<'a> System<'a> for Sys { agent.target = None; data.idle_tree( agent, + behavior, controller, &read_data, &mut event_emitter, @@ -491,6 +506,7 @@ impl<'a> System<'a> for Sys { agent.target = None; data.idle_tree( agent, + behavior, controller, &read_data, &mut event_emitter, @@ -498,7 +514,13 @@ impl<'a> System<'a> for Sys { } }, _ => { - data.idle_tree(agent, controller, &read_data, &mut event_emitter); + data.idle_tree( + agent, + behavior, + controller, + &read_data, + &mut event_emitter, + ); }, } } @@ -530,8 +552,9 @@ impl<'a> AgentData<'a> { // Subtrees //////////////////////////////////////// fn idle_tree( - &mut self, + &self, agent: &mut Agent, + behavior: &mut Behavior, controller: &mut Controller, read_data: &ReadData, event_emitter: &mut Emitter<'_, ServerEvent>, @@ -554,13 +577,13 @@ impl<'a> AgentData<'a> { } if agent.action_timer > 0.0 { if agent.action_timer - < (if self.behavior.has(BehaviorFlag::IS_TRADING) { + < (if behavior.has(BehaviorFlag::IS_TRADING) { TRADE_INTERACTION_TIME } else { DEFAULT_INTERACTION_TIME }) { - self.interact(agent, controller, &read_data, event_emitter); + self.interact(agent, behavior, controller, &read_data, event_emitter); } else { agent.action_timer = 0.0; agent.target = None; @@ -568,7 +591,7 @@ impl<'a> AgentData<'a> { self.idle(agent, controller, &read_data); } } else if thread_rng().gen::() < 0.1 { - self.choose_target(agent, controller, &read_data, event_emitter); + self.choose_target(agent, behavior, controller, &read_data, event_emitter); } else { self.idle(agent, controller, &read_data); } @@ -577,6 +600,7 @@ impl<'a> AgentData<'a> { fn hostile_tree( &self, agent: &mut Agent, + behavior: &mut Behavior, controller: &mut Controller, read_data: &ReadData, event_emitter: &mut Emitter<'_, ServerEvent>, @@ -591,7 +615,7 @@ impl<'a> AgentData<'a> { let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0); // Should the agent flee? if 1.0 - agent.psyche.aggro > self.damage && self.flees { - if agent.action_timer == 0.0 && self.behavior.has(BehaviorFlag::CAN_SPEAK) { + if agent.action_timer == 0.0 && behavior.has(BehaviorFlag::CAN_SPEAK) { let msg = "npc.speech.villager_under_attack".to_string(); event_emitter .emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); @@ -619,7 +643,7 @@ impl<'a> AgentData<'a> { read_data.buffs.get(target), ) { agent.target = None; - if self.behavior.has(BehaviorFlag::CAN_SPEAK) { + if behavior.has(BehaviorFlag::CAN_SPEAK) { let msg = "npc.speech.villager_enemy_killed".to_string(); event_emitter .emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); @@ -630,7 +654,7 @@ impl<'a> AgentData<'a> { // weapon, etc, into the decision to change // target. } else if read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS { - self.choose_target(agent, controller, &read_data, event_emitter); + self.choose_target(agent, behavior, controller, &read_data, event_emitter); } else if dist_sqrd < SIGHT_DIST.powi(2) { self.attack( agent, @@ -858,8 +882,9 @@ impl<'a> AgentData<'a> { } fn interact( - &mut self, + &self, agent: &mut Agent, + behavior: &mut Behavior, controller: &mut Controller, read_data: &ReadData, event_emitter: &mut Emitter<'_, ServerEvent>, @@ -882,7 +907,7 @@ impl<'a> AgentData<'a> { let msg = agent.inbox.pop_back(); match msg { Some(AgentEvent::Talk(by, subject)) => { - if self.behavior.has(BehaviorFlag::CAN_SPEAK) { + if behavior.has(BehaviorFlag::CAN_SPEAK) { if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id()) { agent.target = Some(Target { @@ -935,7 +960,7 @@ impl<'a> AgentData<'a> { event_emitter.emit(ServerEvent::Chat( UnresolvedChatMsg::npc(*self.uid, msg), )); - } else if self.behavior.has(BehaviorFlag::CAN_TRADE) { + } else if behavior.has(BehaviorFlag::CAN_TRADE) { let msg = "npc.speech.merchant_advertisement".to_string(); event_emitter.emit(ServerEvent::Chat( UnresolvedChatMsg::npc(*self.uid, msg), @@ -948,8 +973,8 @@ impl<'a> AgentData<'a> { } }, Subject::Trade => { - if self.behavior.has(BehaviorFlag::CAN_TRADE) { - if !self.behavior.has(BehaviorFlag::IS_TRADING) { + if behavior.has(BehaviorFlag::CAN_TRADE) { + if !behavior.has(BehaviorFlag::IS_TRADING) { controller.events.push(ControlEvent::InitiateInvite( by, InviteKind::Trade, @@ -1097,8 +1122,8 @@ impl<'a> AgentData<'a> { } }, Some(AgentEvent::TradeInvite(with)) => { - if self.behavior.has(BehaviorFlag::CAN_TRADE) { - if !self.behavior.has(BehaviorFlag::IS_TRADING) { + if behavior.has(BehaviorFlag::CAN_TRADE) { + if !behavior.has(BehaviorFlag::IS_TRADING) { // stand still and looking towards the trading player controller.actions.push(ControlAction::Stand); controller.actions.push(ControlAction::Talk); @@ -1114,13 +1139,13 @@ impl<'a> AgentData<'a> { controller .events .push(ControlEvent::InviteResponse(InviteResponse::Accept)); - self.behavior.unset(BehaviorFlag::IS_TRADING_ISSUER); - self.behavior.set(BehaviorFlag::IS_TRADING); + behavior.unset(BehaviorFlag::IS_TRADING_ISSUER); + behavior.set(BehaviorFlag::IS_TRADING); } else { controller .events .push(ControlEvent::InviteResponse(InviteResponse::Decline)); - if self.behavior.has(BehaviorFlag::CAN_SPEAK) { + if behavior.has(BehaviorFlag::CAN_SPEAK) { event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc( *self.uid, "npc.speech.merchant_busy".to_string(), @@ -1132,7 +1157,7 @@ impl<'a> AgentData<'a> { controller .events .push(ControlEvent::InviteResponse(InviteResponse::Decline)); - if self.behavior.has(BehaviorFlag::CAN_SPEAK) { + if behavior.has(BehaviorFlag::CAN_SPEAK) { event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc( *self.uid, "npc.speech.villager_decline_trade".to_string(), @@ -1141,7 +1166,7 @@ impl<'a> AgentData<'a> { } }, Some(AgentEvent::TradeAccepted(with)) => { - if !self.behavior.has(BehaviorFlag::IS_TRADING) { + if !behavior.has(BehaviorFlag::IS_TRADING) { if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(with.id()) { @@ -1151,12 +1176,12 @@ impl<'a> AgentData<'a> { selected_at: read_data.time.0, }); } - self.behavior.set(BehaviorFlag::IS_TRADING); - self.behavior.set(BehaviorFlag::IS_TRADING_ISSUER); + behavior.set(BehaviorFlag::IS_TRADING); + behavior.set(BehaviorFlag::IS_TRADING_ISSUER); } }, Some(AgentEvent::FinishedTrade(result)) => { - if self.behavior.has(BehaviorFlag::IS_TRADING) { + if behavior.has(BehaviorFlag::IS_TRADING) { match result { TradeResult::Completed => { event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc( @@ -1169,13 +1194,13 @@ impl<'a> AgentData<'a> { "npc.speech.merchant_trade_declined".to_string(), ))), } - self.behavior.unset(BehaviorFlag::IS_TRADING); + behavior.unset(BehaviorFlag::IS_TRADING); } }, Some(AgentEvent::UpdatePendingTrade(boxval)) => { let (tradeid, pending, prices, inventories) = *boxval; - if self.behavior.has(BehaviorFlag::IS_TRADING) { - let who: usize = if self.behavior.has(BehaviorFlag::IS_TRADING_ISSUER) { + if behavior.has(BehaviorFlag::IS_TRADING) { + let who: usize = if behavior.has(BehaviorFlag::IS_TRADING_ISSUER) { 0 } else { 1 @@ -1209,7 +1234,7 @@ impl<'a> AgentData<'a> { } if pending.phase != TradePhase::Mutate { // we got into the review phase but without balanced goods, decline - self.behavior.unset(BehaviorFlag::IS_TRADING); + behavior.unset(BehaviorFlag::IS_TRADING); event_emitter.emit(ServerEvent::ProcessTradeAction( *self.entity, tradeid, @@ -1220,7 +1245,7 @@ impl<'a> AgentData<'a> { } }, None => { - if self.behavior.has(BehaviorFlag::CAN_SPEAK) { + if behavior.has(BehaviorFlag::CAN_SPEAK) { // no new events, continue looking towards the last interacting player for some // time if let Some(Target { target, .. }) = &agent.target { @@ -1296,6 +1321,7 @@ impl<'a> AgentData<'a> { fn choose_target( &self, agent: &mut Agent, + behavior: &mut Behavior, controller: &mut Controller, read_data: &ReadData, event_emitter: &mut Emitter<'_, ServerEvent>, @@ -1343,7 +1369,7 @@ impl<'a> AgentData<'a> { ( self.alignment.map_or(false, |alignment| { if matches!(alignment, Alignment::Npc) && e_inventory.equipped_items().filter(|item| item.tags().contains(&ItemTag::Cultist)).count() > 2 { - if self.behavior.has(BehaviorFlag::CAN_SPEAK) { + if behavior.has(BehaviorFlag::CAN_SPEAK) { if self.rtsim_entity.is_some() { agent.rtsim_controller.events.push( RtSimEvent::AddMemory(Memory {