mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Balanced stance AI
This commit is contained in:
parent
221b0aff5f
commit
4ab3abfc07
@ -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,
|
||||
)
|
@ -1,3 +1,4 @@
|
||||
([
|
||||
Group(Weapon(Sword)),
|
||||
Skill((Sword(BalancedFinisher), 1)),
|
||||
])
|
||||
|
@ -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)),
|
||||
])
|
||||
|
@ -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)),
|
||||
])
|
||||
|
@ -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)),
|
||||
])
|
||||
|
@ -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)),
|
||||
])
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user