Balanced stance AI

This commit is contained in:
Sam 2022-09-17 22:11:41 -04:00
parent 221b0aff5f
commit 4ab3abfc07
13 changed files with 219 additions and 12 deletions

View File

@ -55,6 +55,5 @@ ComboMelee(
speed_increase: 0.0,
max_speed_increase: 0.0,
scales_from_combo: 0,
is_interruptible: false,
ori_modifier: 0.7,
)

View File

@ -1,3 +1,4 @@
([
Group(Weapon(Sword)),
Skill((Sword(BalancedFinisher), 1)),
])

View File

@ -1,3 +1,13 @@
([
Group(Weapon(Sword)),
Skill((Sword(BalancedFinisher), 1)),
Skill((Sword(OffensiveCombo), 1)),
Skill((Sword(OffensiveFinisher), 1)),
Skill((Sword(OffensiveAdvance), 1)),
Skill((Sword(DefensiveCombo), 1)),
Skill((Sword(DefensiveBulwark), 1)),
Skill((Sword(DefensiveRetreat), 1)),
Skill((Sword(MobilityCombo), 1)),
Skill((Sword(MobilityFeint), 1)),
Skill((Sword(MobilityAgility), 1)),
])

View File

@ -1,3 +1,13 @@
([
Group(Weapon(Sword)),
Skill((Sword(BalancedFinisher), 1)),
Skill((Sword(OffensiveCombo), 1)),
Skill((Sword(OffensiveFinisher), 1)),
Skill((Sword(OffensiveAdvance), 1)),
Skill((Sword(DefensiveCombo), 1)),
Skill((Sword(DefensiveBulwark), 1)),
Skill((Sword(DefensiveRetreat), 1)),
Skill((Sword(MobilityCombo), 1)),
Skill((Sword(MobilityFeint), 1)),
Skill((Sword(MobilityAgility), 1)),
])

View File

@ -1,3 +1,33 @@
([
Group(Weapon(Sword)),
Skill((Sword(BalancedFinisher), 1)),
Skill((Sword(OffensiveCombo), 1)),
Skill((Sword(OffensiveFinisher), 1)),
Skill((Sword(OffensiveAdvance), 1)),
Skill((Sword(CripplingCombo), 1)),
Skill((Sword(CripplingFinisher), 1)),
Skill((Sword(CripplingStrike), 1)),
Skill((Sword(CripplingGouge), 1)),
Skill((Sword(CleavingCombo), 1)),
Skill((Sword(CleavingFinisher), 1)),
Skill((Sword(CleavingSpin), 1)),
Skill((Sword(CleavingDive), 1)),
Skill((Sword(DefensiveCombo), 1)),
Skill((Sword(DefensiveBulwark), 1)),
Skill((Sword(DefensiveRetreat), 1)),
Skill((Sword(ParryingCombo), 1)),
Skill((Sword(ParryingParry), 1)),
Skill((Sword(ParryingRiposte), 1)),
Skill((Sword(ParryingCounter), 1)),
Skill((Sword(HeavyCombo), 1)),
Skill((Sword(HeavyFinisher), 1)),
Skill((Sword(HeavyPommelStrike), 1)),
Skill((Sword(HeavyFortitude), 1)),
Skill((Sword(MobilityCombo), 1)),
Skill((Sword(MobilityFeint), 1)),
Skill((Sword(MobilityAgility), 1)),
Skill((Sword(ReachingCombo), 1)),
Skill((Sword(ReachingCharge), 1)),
Skill((Sword(ReachingFlurry), 1)),
Skill((Sword(ReachingSkewer), 1)),
])

View File

@ -1,3 +1,33 @@
([
Group(Weapon(Sword)),
Skill((Sword(BalancedFinisher), 1)),
Skill((Sword(OffensiveCombo), 1)),
Skill((Sword(OffensiveFinisher), 1)),
Skill((Sword(OffensiveAdvance), 1)),
Skill((Sword(CripplingCombo), 1)),
Skill((Sword(CripplingFinisher), 1)),
Skill((Sword(CripplingStrike), 1)),
Skill((Sword(CripplingGouge), 1)),
Skill((Sword(CleavingCombo), 1)),
Skill((Sword(CleavingFinisher), 1)),
Skill((Sword(CleavingSpin), 1)),
Skill((Sword(CleavingDive), 1)),
Skill((Sword(DefensiveCombo), 1)),
Skill((Sword(DefensiveBulwark), 1)),
Skill((Sword(DefensiveRetreat), 1)),
Skill((Sword(ParryingCombo), 1)),
Skill((Sword(ParryingParry), 1)),
Skill((Sword(ParryingRiposte), 1)),
Skill((Sword(ParryingCounter), 1)),
Skill((Sword(HeavyCombo), 1)),
Skill((Sword(HeavyFinisher), 1)),
Skill((Sword(HeavyPommelStrike), 1)),
Skill((Sword(HeavyFortitude), 1)),
Skill((Sword(MobilityCombo), 1)),
Skill((Sword(MobilityFeint), 1)),
Skill((Sword(MobilityAgility), 1)),
Skill((Sword(ReachingCombo), 1)),
Skill((Sword(ReachingCharge), 1)),
Skill((Sword(ReachingFlurry), 1)),
Skill((Sword(ReachingSkewer), 1)),
])

View File

@ -13,7 +13,7 @@ use std::time::Duration;
use vek::Vec3;
/// Separated out to condense update portions of character state
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
/// Buildup to sprite interaction
pub buildup_duration: Duration,
@ -33,7 +33,7 @@ pub struct StaticData {
pub ability_info: AbilityInfo,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// Struct containing data that does not change over the course of the
/// character state

View File

@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize};
use std::time::Duration;
/// Separated out to condense update portions of character state
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
/// Buildup to item use
pub buildup_duration: Duration,
@ -40,7 +40,7 @@ pub struct StaticData {
pub ability_info: AbilityInfo,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// Struct containing data that does not change over the course of the
/// character state

View File

@ -1355,6 +1355,7 @@ impl<'a> AgentData<'a> {
tgt_pos,
read_data.bodies.get(target),
read_data.scales.get(target),
read_data.char_states.get(target),
);
if let Some(tgt_name) =
read_data.stats.get(target).map(|stats| stats.name.clone())

View File

@ -7,7 +7,7 @@ use common::{
comp::{
ability::{self, ActiveAbilities, AuxiliaryAbility},
buff::BuffKind,
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill},
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
AbilityInput, Agent, CharacterAbility, CharacterState, ControlAction, ControlEvent,
Controller, InputKind,
},
@ -411,7 +411,6 @@ impl<'a> AgentData<'a> {
}
}
#[allow(unused_variables)]
pub fn handle_sword_attack(
&self,
agent: &mut Agent,
@ -421,6 +420,22 @@ impl<'a> AgentData<'a> {
read_data: &ReadData,
rng: &mut impl Rng,
) {
struct FinisherMeleeData {
range: f32,
angle: f32,
energy: f32,
combo: u32,
}
impl FinisherMeleeData {
fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
attack_data.dist_sqrd < self.range.powi(2)
&& attack_data.angle < self.angle
&& agent_data.energy.current() >= self.energy
&& agent_data
.combo
.map_or(false, |c| c.counter() >= self.combo)
}
}
use ability::SwordStance;
let stance = |stance| match stance {
1 => SwordStance::Offensive,
@ -434,7 +449,23 @@ impl<'a> AgentData<'a> {
_ => SwordStance::Balanced,
};
if !agent.action_state.initialized {
agent.action_state.int_counter = rng.gen_range(0..9);
// TODO: Don't always assume that if they have skill checked for, they have
// entire set of necessary skills to take full advantage of AI. Make sure to
// change this to properly query for all required skills when AI dynamically get
// new skills and don't have pre-created skill sets.
agent.action_state.int_counter = if self
.skill_set
.has_skill(Skill::Sword(SwordSkill::CripplingFinisher))
{
rng.gen_range(4..9)
} else if self
.skill_set
.has_skill(Skill::Sword(SwordSkill::OffensiveFinisher))
{
rng.gen_range(1..4)
} else {
0
};
let auxiliary_key = ActiveAbilities::active_auxiliary_key(Some(self.inventory));
let mut set_sword_ability = |slot, skill| {
controller.push_event(ControlEvent::ChangeAbility {
@ -542,24 +573,101 @@ impl<'a> AgentData<'a> {
agent.action_state.initialized = true;
}
let advance = |agent: &mut Agent, controller: &mut Controller, dist: f32, angle: f32| {
if attack_data.dist_sqrd > dist.powi(2) || attack_data.angle > angle {
self.path_toward_target(
agent,
controller,
tgt_data.pos.0,
read_data,
Path::Separate,
None,
);
}
};
// Called when out of energy, or the situation is not right to use another
// ability. Only contains tactics for using M1 and M2
let fallback_rng_1 = rng.gen::<f32>() < 0.67;
let fallback_tactics = |agent: &mut Agent, controller: &mut Controller| {
const BALANCED_COMBO_RANGE: f32 = 2.5;
const BALANCED_COMBO_ANGLE: f32 = 40.0;
const BALANCED_THRUST_RANGE: f32 = 4.0;
const BALANCED_THRUST_ANGLE: f32 = 8.0;
let balanced_thrust_targetable = attack_data.dist_sqrd < BALANCED_THRUST_RANGE.powi(2)
&& attack_data.angle < BALANCED_THRUST_ANGLE;
if (matches!(self.char_state, CharacterState::ChargedMelee(c) if c.charge_amount < 0.95)
|| matches!(
tgt_data.char_state.and_then(|cs| cs.stage_section()),
Some(StageSection::Buildup)
))
&& balanced_thrust_targetable
{
// If target in buildup (and therefore likely to still be around for an attack
// that needs charging), thrust
// Or if already thrusting, keep thrussting
controller.push_basic_input(InputKind::Secondary);
} else if attack_data.dist_sqrd < BALANCED_COMBO_RANGE.powi(2)
&& attack_data.angle < BALANCED_COMBO_ANGLE
&& fallback_rng_1
{
controller.push_basic_input(InputKind::Primary);
} else if balanced_thrust_targetable {
controller.push_basic_input(InputKind::Secondary);
}
advance(agent, controller, 1.5, BALANCED_COMBO_ANGLE);
};
match stance(agent.action_state.int_counter) {
SwordStance::Balanced => {
const BALANCED_FINISHER: FinisherMeleeData = FinisherMeleeData {
range: 2.5,
angle: 12.5,
energy: 30.0,
combo: 10,
};
if self
.skill_set
.has_skill(Skill::Sword(SwordSkill::BalancedFinisher))
&& BALANCED_FINISHER.could_use(attack_data, self)
{
controller.push_basic_input(InputKind::Ability(0));
advance(
agent,
controller,
BALANCED_FINISHER.range,
BALANCED_FINISHER.angle,
);
} else {
fallback_tactics(agent, controller);
}
},
SwordStance::Offensive => {
fallback_tactics(agent, controller);
},
SwordStance::Defensive => {
fallback_tactics(agent, controller);
},
SwordStance::Mobility => {
fallback_tactics(agent, controller);
},
SwordStance::Crippling => {
fallback_tactics(agent, controller);
},
SwordStance::Cleaving => {
fallback_tactics(agent, controller);
},
SwordStance::Parrying => {
fallback_tactics(agent, controller);
},
SwordStance::Heavy => {
fallback_tactics(agent, controller);
},
SwordStance::Reaching => {
fallback_tactics(agent, controller);
},
}
}

View File

@ -41,6 +41,7 @@ pub struct AgentData<'a> {
pub health: Option<&'a Health>,
pub char_state: &'a CharacterState,
pub active_abilities: &'a ActiveAbilities,
pub combo: Option<&'a Combo>,
pub cached_spatial_grid: &'a common::CachedSpatialGrid,
pub msm: &'a MaterialStatManifest,
}
@ -49,11 +50,22 @@ pub struct TargetData<'a> {
pub pos: &'a Pos,
pub body: Option<&'a Body>,
pub scale: Option<&'a Scale>,
pub char_state: Option<&'a CharacterState>,
}
impl<'a> TargetData<'a> {
pub fn new(pos: &'a Pos, body: Option<&'a Body>, scale: Option<&'a Scale>) -> Self {
Self { pos, body, scale }
pub fn new(
pos: &'a Pos,
body: Option<&'a Body>,
scale: Option<&'a Scale>,
char_state: Option<&'a CharacterState>,
) -> Self {
Self {
pos,
body,
scale,
char_state,
}
}
}

View File

@ -48,7 +48,11 @@ impl<'a> System<'a> for Sys {
(
&read_data.entities,
(&read_data.energies, read_data.healths.maybe()),
(
&read_data.energies,
read_data.healths.maybe(),
read_data.combos.maybe(),
),
(
&read_data.positions,
&read_data.velocities,
@ -78,7 +82,7 @@ impl<'a> System<'a> for Sys {
|_guard,
(
entity,
(energy, health),
(energy, health, combo),
(pos, vel, ori),
body,
inventory,
@ -193,6 +197,7 @@ impl<'a> System<'a> for Sys {
health: read_data.healths.get(entity),
char_state,
active_abilities,
combo,
cached_spatial_grid: &read_data.cached_spatial_grid,
msm: &read_data.msm,
};

View File

@ -619,6 +619,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
tgt_pos,
read_data.bodies.get(target),
read_data.scales.get(target),
read_data.char_states.get(target),
);
let tgt_name = read_data.stats.get(target).map(|stats| stats.name.clone());