Gnarling stalker AI

This commit is contained in:
Sam 2022-01-21 18:28:15 -05:00
parent c15fb2b68f
commit 40bb74c42f
6 changed files with 119 additions and 44 deletions

View File

@ -74,6 +74,11 @@
secondary: "common.abilities.gnarling.dagger.stab",
abilities: [],
),
Custom("Gnarling Blowgun"): (
primary: "common.abilities.gnarling.blowgun.dart",
secondary: "common.abilities.gnarling.blowgun.dart",
abilities: [],
),
Custom("Sword Simple"): (
primary: "common.abilities.swordsimple.doublestrike",
secondary: "common.abilities.swordsimple.dash",
@ -90,12 +95,6 @@
abilities: [
],
),
Tool(Blowgun): (
primary: "common.abilities.blowgun.basic",
secondary: "common.abilities.blowgun.basic",
abilities: [
],
),
Tool(Dagger): (
primary: "common.abilities.dagger.tempbasic",
secondary: "common.abilities.dagger.tempbasic",

View File

@ -3,13 +3,13 @@ BasicRanged(
buildup_duration: 0.5,
recover_duration: 0.3,
projectile: Arrow(
damage: 3.5,
knockback: 5.0,
energy_regen: 4.0,
damage: 4,
knockback: 0,
energy_regen: 0,
),
projectile_body: Object(Arrow),
projectile_light: None,
projectile_speed: 100.0,
projectile_speed: 80.0,
num_projectiles: 1,
projectile_spread: 0.0,
)

View File

@ -17,5 +17,5 @@ ItemDef(
)),
quality: Low,
tags: [],
ability_spec: None,
ability_spec: Some(Custom("Gnarling Blowgun")),
)

View File

@ -1692,7 +1692,7 @@ impl<'a> AgentData<'a> {
ToolKind::Hammer => Tactic::Hammer,
ToolKind::Sword | ToolKind::Spear | ToolKind::Blowgun => Tactic::Sword,
ToolKind::Axe => Tactic::Axe,
_ => Tactic::Melee,
_ => Tactic::SimpleMelee,
};
let tactic = self
@ -1742,21 +1742,22 @@ impl<'a> AgentData<'a> {
"Minotaur" => Tactic::Minotaur,
"Clay Golem" => Tactic::ClayGolem,
"Tidal Warrior" => Tactic::TidalWarrior,
"Tidal Totem" => Tactic::RadialTurret,
"Tidal Totem" | "Tornado" => Tactic::RadialTurret,
"Yeti" => Tactic::Yeti,
"Harvester" => Tactic::Harvester,
"Gnarling Dagger" => Tactic::Backstab,
_ => Tactic::Melee,
"Gnarling Dagger" => Tactic::SimpleBackstab,
"Gnarling Blowgun" => Tactic::ElevatedRanged,
_ => Tactic::SimpleMelee,
},
AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind),
}
} else if let ItemKind::Tool(tool) = &item.kind() {
tool_tactic(tool.kind)
} else {
Tactic::Melee
Tactic::SimpleMelee
}
})
.unwrap_or(Tactic::Melee);
.unwrap_or(Tactic::SimpleMelee);
// Wield the weapon as running towards the target
controller.push_action(ControlAction::Wield);
@ -1770,6 +1771,12 @@ impl<'a> AgentData<'a> {
.look_vec()
.angle_between(tgt_data.pos.0 - self.pos.0)
.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());
@ -1911,13 +1918,14 @@ impl<'a> AgentData<'a> {
min_attack_dist,
dist_sqrd,
angle,
angle_xy,
};
// Match on tactic. Each tactic has different controls
// depending on the distance from the agent to the target
match tactic {
Tactic::Melee => {
self.handle_melee_attack(agent, controller, &attack_data, tgt_data, read_data, rng)
Tactic::SimpleMelee => {
self.handle_simple_melee(agent, controller, &attack_data, tgt_data, read_data, rng)
},
Tactic::Axe => {
self.handle_axe_attack(agent, controller, &attack_data, tgt_data, read_data, rng)
@ -2052,7 +2060,6 @@ impl<'a> AgentData<'a> {
tgt_data,
read_data,
),
Tactic::Tornado => self.handle_tornado_attack(controller),
Tactic::Mindflayer => self.handle_mindflayer_attack(
agent,
controller,
@ -2111,14 +2118,12 @@ impl<'a> AgentData<'a> {
Tactic::Harvester => {
self.handle_harvester_attack(agent, controller, &attack_data, tgt_data, read_data)
},
Tactic::Backstab => self.handle_backstab_attack(
agent,
controller,
&attack_data,
tgt_data,
read_data,
rng,
),
Tactic::SimpleBackstab => {
self.handle_simple_backstab(agent, controller, &attack_data, tgt_data, read_data)
},
Tactic::ElevatedRanged => {
self.handle_elevated_ranged(agent, controller, &attack_data, tgt_data, read_data)
},
}
}

View File

@ -21,7 +21,7 @@ use vek::*;
impl<'a> AgentData<'a> {
// Intended for any agent that has one attack, that attack is a melee attack,
// and the agent is able to freely walk around
pub fn handle_melee_attack(
pub fn handle_simple_melee(
&self,
agent: &mut Agent,
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,
// the agent is able to freely walk around, and the agent is trying to attack
// from behind its target
pub fn handle_backstab_attack(
pub fn handle_simple_backstab(
&self,
agent: &mut Agent,
controller: &mut Controller,
attack_data: &AttackData,
tgt_data: &TargetData,
read_data: &ReadData,
rng: &mut impl Rng,
) {
// Handle attacking of agent
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(
&self,
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(
&self,
agent: &mut Agent,

View File

@ -60,6 +60,7 @@ pub struct AttackData {
pub min_attack_dist: f32,
pub dist_sqrd: f32,
pub angle: f32,
pub angle_xy: f32,
}
impl AttackData {
@ -67,15 +68,27 @@ impl AttackData {
}
#[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 {
Melee,
// General tactics
SimpleMelee,
SimpleBackstab,
ElevatedRanged,
Turret,
FixedTurret,
RotatingTurret,
RadialTurret,
// Tool specific tactics
Axe,
Hammer,
Sword,
Bow,
Staff,
Sceptre,
StoneGolem,
// Broad creature tactics
CircleCharge { radius: u32, circle_time: u32 },
QuadLowRanged,
TailSlap,
@ -85,11 +98,6 @@ pub enum Tactic {
QuadMedJump,
QuadMedBasic,
Theropod,
Turret,
FixedTurret,
RotatingTurret,
RadialTurret,
Mindflayer,
BirdLargeBreathe,
BirdLargeFire,
BirdLargeBasic,
@ -97,13 +105,15 @@ pub enum Tactic {
ArthropodBasic,
ArthropodRanged,
ArthropodLeap,
// Specific species tactics
Mindflayer,
Minotaur,
ClayGolem,
TidalWarrior,
Yeti,
Tornado,
Harvester,
Backstab,
StoneGolem,
}
#[derive(SystemData)]