mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Gnarling stalker AI
This commit is contained in:
parent
c15fb2b68f
commit
40bb74c42f
@ -74,6 +74,11 @@
|
|||||||
secondary: "common.abilities.gnarling.dagger.stab",
|
secondary: "common.abilities.gnarling.dagger.stab",
|
||||||
abilities: [],
|
abilities: [],
|
||||||
),
|
),
|
||||||
|
Custom("Gnarling Blowgun"): (
|
||||||
|
primary: "common.abilities.gnarling.blowgun.dart",
|
||||||
|
secondary: "common.abilities.gnarling.blowgun.dart",
|
||||||
|
abilities: [],
|
||||||
|
),
|
||||||
Custom("Sword Simple"): (
|
Custom("Sword Simple"): (
|
||||||
primary: "common.abilities.swordsimple.doublestrike",
|
primary: "common.abilities.swordsimple.doublestrike",
|
||||||
secondary: "common.abilities.swordsimple.dash",
|
secondary: "common.abilities.swordsimple.dash",
|
||||||
@ -90,12 +95,6 @@
|
|||||||
abilities: [
|
abilities: [
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Tool(Blowgun): (
|
|
||||||
primary: "common.abilities.blowgun.basic",
|
|
||||||
secondary: "common.abilities.blowgun.basic",
|
|
||||||
abilities: [
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Tool(Dagger): (
|
Tool(Dagger): (
|
||||||
primary: "common.abilities.dagger.tempbasic",
|
primary: "common.abilities.dagger.tempbasic",
|
||||||
secondary: "common.abilities.dagger.tempbasic",
|
secondary: "common.abilities.dagger.tempbasic",
|
||||||
|
@ -3,13 +3,13 @@ BasicRanged(
|
|||||||
buildup_duration: 0.5,
|
buildup_duration: 0.5,
|
||||||
recover_duration: 0.3,
|
recover_duration: 0.3,
|
||||||
projectile: Arrow(
|
projectile: Arrow(
|
||||||
damage: 3.5,
|
damage: 4,
|
||||||
knockback: 5.0,
|
knockback: 0,
|
||||||
energy_regen: 4.0,
|
energy_regen: 0,
|
||||||
),
|
),
|
||||||
projectile_body: Object(Arrow),
|
projectile_body: Object(Arrow),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
projectile_speed: 100.0,
|
projectile_speed: 80.0,
|
||||||
num_projectiles: 1,
|
num_projectiles: 1,
|
||||||
projectile_spread: 0.0,
|
projectile_spread: 0.0,
|
||||||
)
|
)
|
@ -17,5 +17,5 @@ ItemDef(
|
|||||||
)),
|
)),
|
||||||
quality: Low,
|
quality: Low,
|
||||||
tags: [],
|
tags: [],
|
||||||
ability_spec: None,
|
ability_spec: Some(Custom("Gnarling Blowgun")),
|
||||||
)
|
)
|
@ -1692,7 +1692,7 @@ impl<'a> AgentData<'a> {
|
|||||||
ToolKind::Hammer => Tactic::Hammer,
|
ToolKind::Hammer => Tactic::Hammer,
|
||||||
ToolKind::Sword | ToolKind::Spear | ToolKind::Blowgun => Tactic::Sword,
|
ToolKind::Sword | ToolKind::Spear | ToolKind::Blowgun => Tactic::Sword,
|
||||||
ToolKind::Axe => Tactic::Axe,
|
ToolKind::Axe => Tactic::Axe,
|
||||||
_ => Tactic::Melee,
|
_ => Tactic::SimpleMelee,
|
||||||
};
|
};
|
||||||
|
|
||||||
let tactic = self
|
let tactic = self
|
||||||
@ -1742,21 +1742,22 @@ impl<'a> AgentData<'a> {
|
|||||||
"Minotaur" => Tactic::Minotaur,
|
"Minotaur" => Tactic::Minotaur,
|
||||||
"Clay Golem" => Tactic::ClayGolem,
|
"Clay Golem" => Tactic::ClayGolem,
|
||||||
"Tidal Warrior" => Tactic::TidalWarrior,
|
"Tidal Warrior" => Tactic::TidalWarrior,
|
||||||
"Tidal Totem" => Tactic::RadialTurret,
|
"Tidal Totem" | "Tornado" => Tactic::RadialTurret,
|
||||||
"Yeti" => Tactic::Yeti,
|
"Yeti" => Tactic::Yeti,
|
||||||
"Harvester" => Tactic::Harvester,
|
"Harvester" => Tactic::Harvester,
|
||||||
"Gnarling Dagger" => Tactic::Backstab,
|
"Gnarling Dagger" => Tactic::SimpleBackstab,
|
||||||
_ => Tactic::Melee,
|
"Gnarling Blowgun" => Tactic::ElevatedRanged,
|
||||||
|
_ => Tactic::SimpleMelee,
|
||||||
},
|
},
|
||||||
AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind),
|
AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind),
|
||||||
}
|
}
|
||||||
} else if let ItemKind::Tool(tool) = &item.kind() {
|
} else if let ItemKind::Tool(tool) = &item.kind() {
|
||||||
tool_tactic(tool.kind)
|
tool_tactic(tool.kind)
|
||||||
} else {
|
} else {
|
||||||
Tactic::Melee
|
Tactic::SimpleMelee
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(Tactic::Melee);
|
.unwrap_or(Tactic::SimpleMelee);
|
||||||
|
|
||||||
// Wield the weapon as running towards the target
|
// Wield the weapon as running towards the target
|
||||||
controller.push_action(ControlAction::Wield);
|
controller.push_action(ControlAction::Wield);
|
||||||
@ -1770,6 +1771,12 @@ impl<'a> AgentData<'a> {
|
|||||||
.look_vec()
|
.look_vec()
|
||||||
.angle_between(tgt_data.pos.0 - self.pos.0)
|
.angle_between(tgt_data.pos.0 - self.pos.0)
|
||||||
.to_degrees();
|
.to_degrees();
|
||||||
|
let angle_xy = self
|
||||||
|
.ori
|
||||||
|
.look_vec()
|
||||||
|
.xy()
|
||||||
|
.angle_between((tgt_data.pos.0 - self.pos.0).xy())
|
||||||
|
.to_degrees();
|
||||||
|
|
||||||
let eye_offset = self.body.map_or(0.0, |b| b.eye_height());
|
let eye_offset = self.body.map_or(0.0, |b| b.eye_height());
|
||||||
|
|
||||||
@ -1911,13 +1918,14 @@ impl<'a> AgentData<'a> {
|
|||||||
min_attack_dist,
|
min_attack_dist,
|
||||||
dist_sqrd,
|
dist_sqrd,
|
||||||
angle,
|
angle,
|
||||||
|
angle_xy,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Match on tactic. Each tactic has different controls
|
// Match on tactic. Each tactic has different controls
|
||||||
// depending on the distance from the agent to the target
|
// depending on the distance from the agent to the target
|
||||||
match tactic {
|
match tactic {
|
||||||
Tactic::Melee => {
|
Tactic::SimpleMelee => {
|
||||||
self.handle_melee_attack(agent, controller, &attack_data, tgt_data, read_data, rng)
|
self.handle_simple_melee(agent, controller, &attack_data, tgt_data, read_data, rng)
|
||||||
},
|
},
|
||||||
Tactic::Axe => {
|
Tactic::Axe => {
|
||||||
self.handle_axe_attack(agent, controller, &attack_data, tgt_data, read_data, rng)
|
self.handle_axe_attack(agent, controller, &attack_data, tgt_data, read_data, rng)
|
||||||
@ -2052,7 +2060,6 @@ impl<'a> AgentData<'a> {
|
|||||||
tgt_data,
|
tgt_data,
|
||||||
read_data,
|
read_data,
|
||||||
),
|
),
|
||||||
Tactic::Tornado => self.handle_tornado_attack(controller),
|
|
||||||
Tactic::Mindflayer => self.handle_mindflayer_attack(
|
Tactic::Mindflayer => self.handle_mindflayer_attack(
|
||||||
agent,
|
agent,
|
||||||
controller,
|
controller,
|
||||||
@ -2111,14 +2118,12 @@ impl<'a> AgentData<'a> {
|
|||||||
Tactic::Harvester => {
|
Tactic::Harvester => {
|
||||||
self.handle_harvester_attack(agent, controller, &attack_data, tgt_data, read_data)
|
self.handle_harvester_attack(agent, controller, &attack_data, tgt_data, read_data)
|
||||||
},
|
},
|
||||||
Tactic::Backstab => self.handle_backstab_attack(
|
Tactic::SimpleBackstab => {
|
||||||
agent,
|
self.handle_simple_backstab(agent, controller, &attack_data, tgt_data, read_data)
|
||||||
controller,
|
},
|
||||||
&attack_data,
|
Tactic::ElevatedRanged => {
|
||||||
tgt_data,
|
self.handle_elevated_ranged(agent, controller, &attack_data, tgt_data, read_data)
|
||||||
read_data,
|
},
|
||||||
rng,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ use vek::*;
|
|||||||
impl<'a> AgentData<'a> {
|
impl<'a> AgentData<'a> {
|
||||||
// Intended for any agent that has one attack, that attack is a melee attack,
|
// Intended for any agent that has one attack, that attack is a melee attack,
|
||||||
// and the agent is able to freely walk around
|
// and the agent is able to freely walk around
|
||||||
pub fn handle_melee_attack(
|
pub fn handle_simple_melee(
|
||||||
&self,
|
&self,
|
||||||
agent: &mut Agent,
|
agent: &mut Agent,
|
||||||
controller: &mut Controller,
|
controller: &mut Controller,
|
||||||
@ -50,14 +50,13 @@ impl<'a> AgentData<'a> {
|
|||||||
// Intended for any agent that has one attack, that attack is a melee attack,
|
// Intended for any agent that has one attack, that attack is a melee attack,
|
||||||
// the agent is able to freely walk around, and the agent is trying to attack
|
// the agent is able to freely walk around, and the agent is trying to attack
|
||||||
// from behind its target
|
// from behind its target
|
||||||
pub fn handle_backstab_attack(
|
pub fn handle_simple_backstab(
|
||||||
&self,
|
&self,
|
||||||
agent: &mut Agent,
|
agent: &mut Agent,
|
||||||
controller: &mut Controller,
|
controller: &mut Controller,
|
||||||
attack_data: &AttackData,
|
attack_data: &AttackData,
|
||||||
tgt_data: &TargetData,
|
tgt_data: &TargetData,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
rng: &mut impl Rng,
|
|
||||||
) {
|
) {
|
||||||
// Handle attacking of agent
|
// Handle attacking of agent
|
||||||
if attack_data.in_min_range() && attack_data.angle < 30.0 {
|
if attack_data.in_min_range() && attack_data.angle < 30.0 {
|
||||||
@ -111,6 +110,72 @@ impl<'a> AgentData<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_elevated_ranged(
|
||||||
|
&self,
|
||||||
|
agent: &mut Agent,
|
||||||
|
controller: &mut Controller,
|
||||||
|
attack_data: &AttackData,
|
||||||
|
tgt_data: &TargetData,
|
||||||
|
read_data: &ReadData,
|
||||||
|
) {
|
||||||
|
let elevation = self.pos.0.z - tgt_data.pos.0.z;
|
||||||
|
const PREF_DIST: f32 = 30_f32;
|
||||||
|
if attack_data.angle_xy < 30.0
|
||||||
|
&& (elevation > 10.0 || attack_data.dist_sqrd > PREF_DIST.powi(2))
|
||||||
|
&& can_see_tgt(
|
||||||
|
&read_data.terrain,
|
||||||
|
self.pos,
|
||||||
|
tgt_data.pos,
|
||||||
|
attack_data.dist_sqrd,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
controller.push_basic_input(InputKind::Primary);
|
||||||
|
} else if attack_data.dist_sqrd < (PREF_DIST / 2.).powi(2) {
|
||||||
|
// Attempt to move quickly away from target if too close
|
||||||
|
if let Some((bearing, _)) = agent.chaser.chase(
|
||||||
|
&*read_data.terrain,
|
||||||
|
self.pos.0,
|
||||||
|
self.vel.0,
|
||||||
|
tgt_data.pos.0,
|
||||||
|
TraversalConfig {
|
||||||
|
min_tgt_dist: 1.25,
|
||||||
|
..self.traversal_config
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
controller.inputs.move_dir =
|
||||||
|
-bearing.xy().try_normalized().unwrap_or_else(Vec2::zero);
|
||||||
|
if !self.char_state.is_attack() {
|
||||||
|
controller.inputs.look_dir = -controller.inputs.look_dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if attack_data.dist_sqrd < PREF_DIST.powi(2) {
|
||||||
|
// Attempt to move away from target if too close, while still attacking
|
||||||
|
if let Some((bearing, _)) = agent.chaser.chase(
|
||||||
|
&*read_data.terrain,
|
||||||
|
self.pos.0,
|
||||||
|
self.vel.0,
|
||||||
|
tgt_data.pos.0,
|
||||||
|
TraversalConfig {
|
||||||
|
min_tgt_dist: 1.25,
|
||||||
|
..self.traversal_config
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
if can_see_tgt(
|
||||||
|
&read_data.terrain,
|
||||||
|
self.pos,
|
||||||
|
tgt_data.pos,
|
||||||
|
attack_data.dist_sqrd,
|
||||||
|
) {
|
||||||
|
controller.push_basic_input(InputKind::Primary);
|
||||||
|
}
|
||||||
|
controller.inputs.move_dir =
|
||||||
|
-bearing.xy().try_normalized().unwrap_or_else(Vec2::zero);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.path_toward_target(agent, controller, tgt_data, read_data, false, true, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_axe_attack(
|
pub fn handle_axe_attack(
|
||||||
&self,
|
&self,
|
||||||
agent: &mut Agent,
|
agent: &mut Agent,
|
||||||
@ -1186,10 +1251,6 @@ impl<'a> AgentData<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_tornado_attack(&self, controller: &mut Controller) {
|
|
||||||
controller.push_basic_input(InputKind::Primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_mindflayer_attack(
|
pub fn handle_mindflayer_attack(
|
||||||
&self,
|
&self,
|
||||||
agent: &mut Agent,
|
agent: &mut Agent,
|
||||||
|
@ -60,6 +60,7 @@ pub struct AttackData {
|
|||||||
pub min_attack_dist: f32,
|
pub min_attack_dist: f32,
|
||||||
pub dist_sqrd: f32,
|
pub dist_sqrd: f32,
|
||||||
pub angle: f32,
|
pub angle: f32,
|
||||||
|
pub angle_xy: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttackData {
|
impl AttackData {
|
||||||
@ -67,15 +68,27 @@ impl AttackData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Eq, PartialEq)]
|
||||||
|
// When adding a new variant, first decide if it should instead fall under one
|
||||||
|
// of the pre-exisitng tactics
|
||||||
pub enum Tactic {
|
pub enum Tactic {
|
||||||
Melee,
|
// General tactics
|
||||||
|
SimpleMelee,
|
||||||
|
SimpleBackstab,
|
||||||
|
ElevatedRanged,
|
||||||
|
Turret,
|
||||||
|
FixedTurret,
|
||||||
|
RotatingTurret,
|
||||||
|
RadialTurret,
|
||||||
|
|
||||||
|
// Tool specific tactics
|
||||||
Axe,
|
Axe,
|
||||||
Hammer,
|
Hammer,
|
||||||
Sword,
|
Sword,
|
||||||
Bow,
|
Bow,
|
||||||
Staff,
|
Staff,
|
||||||
Sceptre,
|
Sceptre,
|
||||||
StoneGolem,
|
|
||||||
|
// Broad creature tactics
|
||||||
CircleCharge { radius: u32, circle_time: u32 },
|
CircleCharge { radius: u32, circle_time: u32 },
|
||||||
QuadLowRanged,
|
QuadLowRanged,
|
||||||
TailSlap,
|
TailSlap,
|
||||||
@ -85,11 +98,6 @@ pub enum Tactic {
|
|||||||
QuadMedJump,
|
QuadMedJump,
|
||||||
QuadMedBasic,
|
QuadMedBasic,
|
||||||
Theropod,
|
Theropod,
|
||||||
Turret,
|
|
||||||
FixedTurret,
|
|
||||||
RotatingTurret,
|
|
||||||
RadialTurret,
|
|
||||||
Mindflayer,
|
|
||||||
BirdLargeBreathe,
|
BirdLargeBreathe,
|
||||||
BirdLargeFire,
|
BirdLargeFire,
|
||||||
BirdLargeBasic,
|
BirdLargeBasic,
|
||||||
@ -97,13 +105,15 @@ pub enum Tactic {
|
|||||||
ArthropodBasic,
|
ArthropodBasic,
|
||||||
ArthropodRanged,
|
ArthropodRanged,
|
||||||
ArthropodLeap,
|
ArthropodLeap,
|
||||||
|
|
||||||
|
// Specific species tactics
|
||||||
|
Mindflayer,
|
||||||
Minotaur,
|
Minotaur,
|
||||||
ClayGolem,
|
ClayGolem,
|
||||||
TidalWarrior,
|
TidalWarrior,
|
||||||
Yeti,
|
Yeti,
|
||||||
Tornado,
|
|
||||||
Harvester,
|
Harvester,
|
||||||
Backstab,
|
StoneGolem,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
|
Loading…
Reference in New Issue
Block a user