mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'savalige/ai_spacing' into 'master'
Issue #1153 - Improve combat AI spacing between Agents of the same Alignment Closes #1153 See merge request veloren/veloren!2451
This commit is contained in:
commit
0492b8dc87
@ -412,6 +412,29 @@ impl Body {
|
||||
dim.x.max(dim.y) / 2.0
|
||||
}
|
||||
|
||||
// How far away other entities should try to be. Will be added uppon the other
|
||||
// entitys spacing_radius. So an entity with 2.0 and an entity with 3.0 will
|
||||
// lead to that both entities will try to keep 5.0 units away from each
|
||||
// other.
|
||||
pub fn spacing_radius(&self) -> f32 {
|
||||
self.radius()
|
||||
+ match self {
|
||||
Body::QuadrupedSmall(body) => match body.species {
|
||||
quadruped_small::Species::Rat => 0.0,
|
||||
_ => 2.0,
|
||||
},
|
||||
Body::QuadrupedLow(body) => match body.species {
|
||||
quadruped_low::Species::Hakulaq => 0.0,
|
||||
_ => 2.0,
|
||||
},
|
||||
Body::BipedSmall(body) => match body.species {
|
||||
biped_small::Species::Husk => 3.0,
|
||||
_ => 2.0,
|
||||
},
|
||||
_ => 2.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> f32 { self.dimensions().z }
|
||||
|
||||
pub fn base_energy(&self) -> u32 {
|
||||
|
@ -168,6 +168,8 @@ const FLEE_DURATION: f32 = 3.0;
|
||||
const MAX_FOLLOW_DIST: f32 = 12.0;
|
||||
const MAX_PATH_DIST: f32 = 170.0;
|
||||
const PARTIAL_PATH_DIST: f32 = 50.0;
|
||||
const SEPARATION_DIST: f32 = 10.0;
|
||||
const SEPARATION_BIAS: f32 = 0.8;
|
||||
const MAX_FLEE_DIST: f32 = 20.0;
|
||||
const SEARCH_DIST: f32 = 48.0;
|
||||
const SNEAK_COEFFICIENT: f32 = 0.25;
|
||||
@ -1986,7 +1988,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, true, None);
|
||||
|
||||
if self.body.map(|b| b.is_humanoid()).unwrap_or(false)
|
||||
&& attack_data.dist_sqrd < 16.0f32.powi(2)
|
||||
@ -1997,7 +1999,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, true, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2042,7 +2044,7 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
if attack_data.dist_sqrd < 32.0f32.powi(2)
|
||||
&& has_leap()
|
||||
&& has_energy(500)
|
||||
@ -2064,7 +2066,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2111,7 +2113,7 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
if attack_data.dist_sqrd < 32.0f32.powi(2)
|
||||
&& has_leap()
|
||||
&& has_energy(500)
|
||||
@ -2133,7 +2135,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2166,7 +2168,7 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
if self.path_toward_target(agent, controller, tgt_data, read_data, true, None)
|
||||
if self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None)
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
@ -2192,7 +2194,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2266,7 +2268,7 @@ impl<'a> AgentData<'a> {
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
if attack_data.angle < 15.0 {
|
||||
controller
|
||||
.actions
|
||||
@ -2355,7 +2357,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
} else {
|
||||
// If too far, move towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2452,7 +2454,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2577,7 +2579,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
} else {
|
||||
// If too far, move towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2601,7 +2603,7 @@ impl<'a> AgentData<'a> {
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
}
|
||||
if self.path_toward_target(agent, controller, tgt_data, read_data, true, None)
|
||||
if self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None)
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
@ -2620,7 +2622,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2708,9 +2710,9 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2786,7 +2788,7 @@ impl<'a> AgentData<'a> {
|
||||
agent.target = None;
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2823,9 +2825,9 @@ impl<'a> AgentData<'a> {
|
||||
.unwrap_or_else(Vec2::unit_y)
|
||||
* 0.1;
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2857,9 +2859,9 @@ impl<'a> AgentData<'a> {
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::unit_y);
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2889,9 +2891,9 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2917,7 +2919,7 @@ impl<'a> AgentData<'a> {
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
if self.path_toward_target(agent, controller, tgt_data, read_data, true, None)
|
||||
if self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None)
|
||||
&& attack_data.angle < 15.0
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
@ -2931,7 +2933,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2959,9 +2961,9 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3012,9 +3014,9 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3032,9 +3034,9 @@ impl<'a> AgentData<'a> {
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3201,7 +3203,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
}
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
|
||||
// If too far from target, throw a random number of necrotic spheres at them and
|
||||
@ -3232,9 +3234,9 @@ impl<'a> AgentData<'a> {
|
||||
select_pos: None,
|
||||
});
|
||||
}
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3351,7 +3353,7 @@ impl<'a> AgentData<'a> {
|
||||
// If further than 2.5 blocks and random chance
|
||||
else if attack_data.dist_sqrd > (2.5 * attack_data.min_attack_dist).powi(2) {
|
||||
// Walk to target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
}
|
||||
// If energy higher than 600 and random chance
|
||||
else if self.energy.current() > 600 && thread_rng().gen_bool(0.4) {
|
||||
@ -3366,7 +3368,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else {
|
||||
// Target is behind us. Turn around and chase target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3466,7 +3468,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Jump));
|
||||
controller.inputs.move_z = 1.0;
|
||||
} else if attack_data.dist_sqrd > (3.0 * attack_data.min_attack_dist).powi(2) {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
} else if self.energy.current() > 600
|
||||
&& agent.action_state.timer < 3.0
|
||||
&& attack_data.angle < 15.0
|
||||
@ -3476,7 +3478,15 @@ impl<'a> AgentData<'a> {
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
// Move towards the target slowly
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, Some(0.5));
|
||||
self.path_toward_target(
|
||||
agent,
|
||||
controller,
|
||||
tgt_data,
|
||||
read_data,
|
||||
true,
|
||||
false,
|
||||
Some(0.5),
|
||||
);
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
} else if agent.action_state.timer < 6.0
|
||||
&& attack_data.angle < 90.0
|
||||
@ -3491,7 +3501,7 @@ impl<'a> AgentData<'a> {
|
||||
// Reset timer
|
||||
agent.action_state.timer = 0.0;
|
||||
// Target is behind us or the timer needs to be reset. Chase target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3562,7 +3572,7 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.condition = true;
|
||||
}
|
||||
// Make bird move towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
}
|
||||
|
||||
fn handle_minotaur_attack(
|
||||
@ -3629,7 +3639,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
// Make minotaur move towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
}
|
||||
|
||||
fn handle_clay_golem_attack(
|
||||
@ -3717,7 +3727,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
// Make clay golem move towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, false, None);
|
||||
}
|
||||
|
||||
fn handle_tidal_warrior_attack(
|
||||
@ -3799,7 +3809,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
// Always attempt to path towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
|
||||
fn handle_yeti_attack(
|
||||
@ -3858,7 +3868,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
|
||||
// Always attempt to path towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
|
||||
fn handle_harvester_attack(
|
||||
@ -3930,7 +3940,7 @@ impl<'a> AgentData<'a> {
|
||||
.push(ControlAction::basic_input(InputKind::Ability(1)));
|
||||
}
|
||||
// Always attempt to path towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None);
|
||||
}
|
||||
|
||||
fn follow(
|
||||
@ -4048,6 +4058,7 @@ impl<'a> AgentData<'a> {
|
||||
/// multiplies the movement speed by a value less than 1.0.
|
||||
/// A `None` value implies a multiplier of 1.0.
|
||||
/// Returns `false` if the pathfinding algorithm fails to return a path
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn path_toward_target(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
@ -4055,9 +4066,47 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
full_path: bool,
|
||||
separate: bool,
|
||||
speed_multiplier: Option<f32>,
|
||||
) -> bool {
|
||||
let pathing_pos = if full_path {
|
||||
let pathing_pos = if separate {
|
||||
let mut sep_vec: Vec3<f32> = Vec3::<f32>::zero();
|
||||
|
||||
for entity in read_data
|
||||
.cached_spatial_grid
|
||||
.0
|
||||
.in_circle_aabr(self.pos.0.xy(), SEPARATION_DIST)
|
||||
{
|
||||
if let (Some(alignment), Some(other_alignment)) =
|
||||
(self.alignment, read_data.alignments.get(entity))
|
||||
{
|
||||
if Alignment::passive_towards(*alignment, *other_alignment) {
|
||||
if let (Some(pos), Some(body), Some(other_body)) = (
|
||||
read_data.positions.get(entity),
|
||||
self.body,
|
||||
read_data.bodies.get(entity),
|
||||
) {
|
||||
if self.pos.0.xy().distance(pos.0.xy())
|
||||
< body.spacing_radius() + other_body.spacing_radius()
|
||||
{
|
||||
sep_vec += (self.pos.0.xy() - pos.0.xy())
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::zero)
|
||||
* (((body.spacing_radius() + other_body.spacing_radius())
|
||||
- self.pos.0.xy().distance(pos.0.xy()))
|
||||
/ (body.spacing_radius() + other_body.spacing_radius()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.pos.0
|
||||
+ PARTIAL_PATH_DIST
|
||||
* (sep_vec * SEPARATION_BIAS
|
||||
+ (tgt_data.pos.0 - self.pos.0) * (1.0 - SEPARATION_BIAS))
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec3::zero)
|
||||
} else if full_path {
|
||||
tgt_data.pos.0
|
||||
} else {
|
||||
self.pos.0
|
||||
|
Loading…
Reference in New Issue
Block a user