1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Pathfinding behavior for NPC navigation
use async_trait::async_trait;
use crate::agent::AgentContext;
use crate::oxyde_game::intent::{Intent, IntentType};
use crate::Result;
use super::base::{Behavior, BehaviorResult, BaseBehavior};
/// Pathfinding behavior that controls NPC movement
#[derive(Debug)]
pub struct PathfindingBehavior {
/// Base behavior
#[allow(dead_code)]
base: BaseBehavior,
/// Whether the NPC should follow the player
follow_player: bool,
/// Maximum follow distance
max_follow_distance: f32,
/// Movement speed
speed: f32,
}
impl PathfindingBehavior {
/// Create a new pathfinding behavior
///
/// # Arguments
///
/// * `follow_player` - Whether to follow the player
/// * `max_follow_distance` - Maximum distance to follow
/// * `speed` - Movement speed
///
/// # Returns
///
/// A new PathfindingBehavior
pub fn new(follow_player: bool, max_follow_distance: f32, speed: f32) -> Self {
Self {
base: BaseBehavior::new(
"pathfinding",
"Controls NPC movement and pathfinding",
5,
vec!["movement".to_string(), "follow".to_string()],
0, // No cooldown for movement
),
follow_player,
max_follow_distance,
speed,
}
}
/// Create a behavior for following the player
///
/// # Returns
///
/// A new PathfindingBehavior configured to follow the player
pub fn new_follow_player() -> Self {
Self::new(true, 10.0, 1.5)
}
/// Create a behavior for staying in place
///
/// # Returns
///
/// A new PathfindingBehavior configured to stay in place
pub fn new_stationary() -> Self {
Self::new(false, 0.0, 0.0)
}
}
#[async_trait]
impl Behavior for PathfindingBehavior {
async fn matches_intent(&self, intent: &Intent) -> bool {
// Only respond to movement/follow intents if configured to follow player
if !self.follow_player {
return false;
}
intent.intent_type == IntentType::Custom || // movement/follow are custom types
(intent.intent_type == IntentType::Command && intent.keywords.contains(&"follow".to_string()))
}
async fn execute(&self, _intent: &Intent, context: &AgentContext) -> Result<BehaviorResult> {
if !self.follow_player {
return Ok(BehaviorResult::None);
}
// Extract player position from context
let player_x = context.get("player_x").and_then(|v| v.as_f64()).unwrap_or(0.0) as f32;
let player_y = context.get("player_y").and_then(|v| v.as_f64()).unwrap_or(0.0) as f32;
// Check if we should start following
if _intent.intent_type == IntentType::Command && _intent.keywords.contains(&"follow".to_string()) {
// Send action to start following
return Ok(BehaviorResult::Action(format!(
"follow|{:.2}|{:.2}|{:.2}",
player_x, player_y, self.speed
)));
}
// Check distance to player
let npc_x = context.get("npc_x").and_then(|v| v.as_f64()).unwrap_or(0.0) as f32;
let npc_y = context.get("npc_y").and_then(|v| v.as_f64()).unwrap_or(0.0) as f32;
let dx = player_x - npc_x;
let dy = player_y - npc_y;
let distance = (dx * dx + dy * dy).sqrt();
if distance > self.max_follow_distance {
// Too far, stop following
return Ok(BehaviorResult::Action("stop_follow".to_string()));
}
// Move towards player
Ok(BehaviorResult::Action(format!(
"move_to|{:.2}|{:.2}|{:.2}",
player_x, player_y, self.speed
)))
}
}