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", 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",

View File

@ -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,
) )

View File

@ -17,5 +17,5 @@ ItemDef(
)), )),
quality: Low, quality: Low,
tags: [], 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::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,
),
} }
} }

View File

@ -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,

View File

@ -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)]