mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Hammer AI
This commit is contained in:
parent
0dc261c70a
commit
aeb887963e
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -7151,8 +7151,10 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"specs",
|
"specs",
|
||||||
|
"tracing",
|
||||||
"vek 0.16.1",
|
"vek 0.16.1",
|
||||||
"veloren-common",
|
"veloren-common",
|
||||||
|
"veloren-common-base",
|
||||||
"veloren-common-dynlib",
|
"veloren-common-dynlib",
|
||||||
"veloren-rtsim",
|
"veloren-rtsim",
|
||||||
]
|
]
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
loadout: Inline((
|
loadout: Inline((
|
||||||
inherit: Asset("common.loadout.dungeon.cultist.cultist"),
|
inherit: Asset("common.loadout.dungeon.cultist.cultist"),
|
||||||
active_hands: InHands((Choice([
|
active_hands: InHands((Choice([
|
||||||
(2, ModularWeapon(tool: Axe, material: Orichalcum, hands: One)),
|
// (2, ModularWeapon(tool: Axe, material: Orichalcum, hands: One)),
|
||||||
(4, Item("common.items.weapons.sword.cultist")),
|
// (4, Item("common.items.weapons.sword.cultist")),
|
||||||
(2, Item("common.items.weapons.staff.cultist_staff")),
|
// (2, Item("common.items.weapons.staff.cultist_staff")),
|
||||||
(2, Item("common.items.weapons.hammer.cultist_purp_2h-0")),
|
(2, Item("common.items.weapons.hammer.cultist_purp_2h-0")),
|
||||||
(2, ModularWeapon(tool: Hammer, material: Orichalcum, hands: One)),
|
// (2, ModularWeapon(tool: Hammer, material: Orichalcum, hands: One)),
|
||||||
(2, Item("common.items.weapons.bow.velorite")),
|
// (2, Item("common.items.weapons.bow.velorite")),
|
||||||
(1, Item("common.items.weapons.sceptre.sceptre_velorite_0")),
|
// (1, Item("common.items.weapons.sceptre.sceptre_velorite_0")),
|
||||||
]), None)),
|
]), None)),
|
||||||
)),
|
)),
|
||||||
items: [
|
items: [
|
||||||
|
@ -62,6 +62,7 @@ pub const MAX_MELEE_POISE_PRECISION: f32 = 0.5;
|
|||||||
pub const MAX_BLOCK_POISE_COST: f32 = 25.0;
|
pub const MAX_BLOCK_POISE_COST: f32 = 25.0;
|
||||||
pub const PARRY_BONUS_MULTIPLIER: f32 = 2.0;
|
pub const PARRY_BONUS_MULTIPLIER: f32 = 2.0;
|
||||||
pub const FALLBACK_BLOCK_STRENGTH: f32 = 5.0;
|
pub const FALLBACK_BLOCK_STRENGTH: f32 = 5.0;
|
||||||
|
pub const BEHIND_TARGET_ANGLE: f32 = 45.0;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct AttackerInfo<'a> {
|
pub struct AttackerInfo<'a> {
|
||||||
@ -658,9 +659,8 @@ impl Attack {
|
|||||||
target.char_state.map_or(false, |cs| cs.is_stunned())
|
target.char_state.map_or(false, |cs| cs.is_stunned())
|
||||||
},
|
},
|
||||||
CombatRequirement::BehindTarget => {
|
CombatRequirement::BehindTarget => {
|
||||||
const REQUIRED_ANGLE: f32 = 45.0;
|
|
||||||
if let Some(ori) = target.ori {
|
if let Some(ori) = target.ori {
|
||||||
ori.look_vec().angle_between(dir.with_z(0.0)) < REQUIRED_ANGLE
|
ori.look_vec().angle_between(dir.with_z(0.0)) < BEHIND_TARGET_ANGLE
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ be-dyn-lib = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common = { package = "veloren-common", path = "../../common"}
|
common = { package = "veloren-common", path = "../../common"}
|
||||||
|
common-base = { package = "veloren-common-base", path = "../../common/base" }
|
||||||
common-dynlib = { package = "veloren-common-dynlib", path = "../../common/dynlib", optional = true}
|
common-dynlib = { package = "veloren-common-dynlib", path = "../../common/dynlib", optional = true}
|
||||||
rtsim = { package = "veloren-rtsim", path = "../../rtsim" }
|
rtsim = { package = "veloren-rtsim", path = "../../rtsim" }
|
||||||
|
|
||||||
@ -18,3 +19,4 @@ vek = { workspace = true }
|
|||||||
rand = { workspace = true, features = ["small_rng"] }
|
rand = { workspace = true, features = ["small_rng"] }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
@ -4,11 +4,12 @@ use crate::{
|
|||||||
util::{entities_have_line_of_sight, handle_attack_aggression},
|
util::{entities_have_line_of_sight, handle_attack_aggression},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
|
combat::{self, AttackSource},
|
||||||
comp::{
|
comp::{
|
||||||
ability::{ActiveAbilities, AuxiliaryAbility, Stance, SwordStance, BASE_ABILITY_LIMIT},
|
ability::{ActiveAbilities, AuxiliaryAbility, Stance, SwordStance, BASE_ABILITY_LIMIT},
|
||||||
buff::BuffKind,
|
buff::BuffKind,
|
||||||
item::tool::AbilityContext,
|
item::tool::AbilityContext,
|
||||||
skills::{AxeSkill, BowSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
|
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
|
||||||
Ability, AbilityInput, Agent, CharacterAbility, CharacterState, ControlAction,
|
Ability, AbilityInput, Agent, CharacterAbility, CharacterState, ControlAction,
|
||||||
ControlEvent, Controller, Fluid, InputKind,
|
ControlEvent, Controller, Fluid, InputKind,
|
||||||
},
|
},
|
||||||
@ -271,14 +272,391 @@ impl<'a> AgentData<'a> {
|
|||||||
|
|
||||||
pub fn handle_hammer_attack(
|
pub fn handle_hammer_attack(
|
||||||
&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,
|
rng: &mut impl Rng,
|
||||||
) {
|
) {
|
||||||
// TODO
|
if !agent.combat_state.initialized {
|
||||||
|
agent.combat_state.initialized = true;
|
||||||
|
let available_tactics = {
|
||||||
|
let mut tactics = Vec::new();
|
||||||
|
let try_tactic = |skill, tactic, tactics: &mut Vec<HammerTactics>| {
|
||||||
|
if self.skill_set.has_skill(Skill::Hammer(skill)) {
|
||||||
|
tactics.push(tactic);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::Thunderclap,
|
||||||
|
HammerTactics::AttackExpert,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::Judgement,
|
||||||
|
HammerTactics::SupportExpert,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
if tactics.is_empty() {
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::IronTempest,
|
||||||
|
HammerTactics::AttackAdvanced,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::Rampart,
|
||||||
|
HammerTactics::SupportAdvanced,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if tactics.is_empty() {
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::Retaliate,
|
||||||
|
HammerTactics::AttackIntermediate,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::PileDriver,
|
||||||
|
HammerTactics::SupportIntermediate,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if tactics.is_empty() {
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::Tremor,
|
||||||
|
HammerTactics::AttackSimple,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::HeavyWhorl,
|
||||||
|
HammerTactics::SupportSimple,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if tactics.is_empty() {
|
||||||
|
try_tactic(
|
||||||
|
HammerSkill::ScornfulSwipe,
|
||||||
|
HammerTactics::Simple,
|
||||||
|
&mut tactics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if tactics.is_empty() {
|
||||||
|
tactics.push(HammerTactics::Unskilled);
|
||||||
|
}
|
||||||
|
tactics
|
||||||
|
};
|
||||||
|
|
||||||
|
let tactic = available_tactics
|
||||||
|
.choose(rng)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(HammerTactics::Unskilled);
|
||||||
|
|
||||||
|
agent.combat_state.int_counters[IntCounters::Tactic as usize] = tactic as u8;
|
||||||
|
|
||||||
|
let auxiliary_key = ActiveAbilities::active_auxiliary_key(Some(self.inventory));
|
||||||
|
let set_ability = |controller: &mut Controller, slot, skill| {
|
||||||
|
controller.push_event(ControlEvent::ChangeAbility {
|
||||||
|
slot,
|
||||||
|
auxiliary_key,
|
||||||
|
new_ability: AuxiliaryAbility::MainWeapon(skill),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let mut set_random = |controller: &mut Controller, slot, options: &mut Vec<usize>| {
|
||||||
|
if options.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let i = rng.gen_range(0..options.len());
|
||||||
|
set_ability(controller, slot, options.swap_remove(i));
|
||||||
|
};
|
||||||
|
|
||||||
|
match tactic {
|
||||||
|
HammerTactics::Unskilled => {},
|
||||||
|
HammerTactics::Simple => {
|
||||||
|
// Scornful swipe
|
||||||
|
set_ability(controller, 0, 0);
|
||||||
|
},
|
||||||
|
HammerTactics::AttackSimple => {
|
||||||
|
// Scornful swipe
|
||||||
|
set_ability(controller, 0, 0);
|
||||||
|
// Tremor or vigorous bash
|
||||||
|
set_ability(controller, 1, rng.gen_range(1..3));
|
||||||
|
},
|
||||||
|
HammerTactics::AttackIntermediate => {
|
||||||
|
// Scornful swipe
|
||||||
|
set_ability(controller, 0, 0);
|
||||||
|
// Tremor or vigorous bash
|
||||||
|
set_ability(controller, 1, rng.gen_range(1..3));
|
||||||
|
// Retaliate, spine cracker, or breach
|
||||||
|
set_ability(controller, 2, rng.gen_range(3..6));
|
||||||
|
},
|
||||||
|
HammerTactics::AttackAdvanced => {
|
||||||
|
// Scornful swipe, tremor, vigorous bash, retaliate, spine cracker, or breach
|
||||||
|
let mut options = vec![0, 1, 2, 3, 4, 5];
|
||||||
|
set_random(controller, 0, &mut options);
|
||||||
|
set_random(controller, 1, &mut options);
|
||||||
|
set_random(controller, 2, &mut options);
|
||||||
|
set_ability(controller, 3, rng.gen_range(6..8));
|
||||||
|
},
|
||||||
|
HammerTactics::AttackExpert => {
|
||||||
|
// Scornful swipe, tremor, vigorous bash, retaliate, spine cracker, breach, iron
|
||||||
|
// tempest, or upheaval
|
||||||
|
let mut options = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
set_random(controller, 0, &mut options);
|
||||||
|
set_random(controller, 1, &mut options);
|
||||||
|
set_random(controller, 2, &mut options);
|
||||||
|
set_random(controller, 3, &mut options);
|
||||||
|
set_ability(controller, 4, rng.gen_range(8..10));
|
||||||
|
},
|
||||||
|
HammerTactics::SupportSimple => {
|
||||||
|
// Scornful swipe
|
||||||
|
set_ability(controller, 0, 0);
|
||||||
|
// Heavy whorl or intercept
|
||||||
|
set_ability(controller, 1, rng.gen_range(10..12));
|
||||||
|
},
|
||||||
|
HammerTactics::SupportIntermediate => {
|
||||||
|
// Scornful swipe
|
||||||
|
set_ability(controller, 0, 0);
|
||||||
|
// Heavy whorl or intercept
|
||||||
|
set_ability(controller, 1, rng.gen_range(10..12));
|
||||||
|
// Retaliate, spine cracker, or breach
|
||||||
|
set_ability(controller, 2, rng.gen_range(12..15));
|
||||||
|
},
|
||||||
|
HammerTactics::SupportAdvanced => {
|
||||||
|
// Scornful swipe, heavy whorl, intercept, pile driver, lung pummel, or helm
|
||||||
|
// crusher
|
||||||
|
let mut options = vec![0, 10, 11, 12, 13, 14];
|
||||||
|
set_random(controller, 0, &mut options);
|
||||||
|
set_random(controller, 1, &mut options);
|
||||||
|
set_random(controller, 2, &mut options);
|
||||||
|
set_ability(controller, 3, rng.gen_range(15..17));
|
||||||
|
},
|
||||||
|
HammerTactics::SupportExpert => {
|
||||||
|
// Scornful swipe, heavy whorl, intercept, pile driver, lung pummel, helm
|
||||||
|
// crusher, rampart, or tenacity
|
||||||
|
let mut options = vec![0, 10, 11, 12, 13, 14, 15, 16];
|
||||||
|
set_random(controller, 0, &mut options);
|
||||||
|
set_random(controller, 1, &mut options);
|
||||||
|
set_random(controller, 2, &mut options);
|
||||||
|
set_random(controller, 3, &mut options);
|
||||||
|
set_ability(controller, 4, rng.gen_range(17..19));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
agent.combat_state.int_counters[IntCounters::ActionMode as usize] =
|
||||||
|
ActionMode::Reckless as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IntCounters {
|
||||||
|
Tactic = 0,
|
||||||
|
ActionMode = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Timers {
|
||||||
|
GuardedCycle = 0,
|
||||||
|
PosTimeOut = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Conditions {
|
||||||
|
GuardedDefend = 0,
|
||||||
|
RollingBreakThrough = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FloatCounters {
|
||||||
|
GuardedTimer = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Positions {
|
||||||
|
GuardedCover = 0,
|
||||||
|
Flee = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
let attempt_attack = handle_attack_aggression(
|
||||||
|
self,
|
||||||
|
agent,
|
||||||
|
controller,
|
||||||
|
attack_data,
|
||||||
|
tgt_data,
|
||||||
|
read_data,
|
||||||
|
rng,
|
||||||
|
Timers::PosTimeOut as usize,
|
||||||
|
Timers::GuardedCycle as usize,
|
||||||
|
FloatCounters::GuardedTimer as usize,
|
||||||
|
IntCounters::ActionMode as usize,
|
||||||
|
Conditions::GuardedDefend as usize,
|
||||||
|
Conditions::RollingBreakThrough as usize,
|
||||||
|
Positions::GuardedCover as usize,
|
||||||
|
Positions::Flee as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
let attack_failed = if attempt_attack {
|
||||||
|
let primary = self.extract_ability(AbilityInput::Primary);
|
||||||
|
let secondary = self.extract_ability(AbilityInput::Secondary);
|
||||||
|
let abilities = [
|
||||||
|
self.extract_ability(AbilityInput::Auxiliary(0)),
|
||||||
|
self.extract_ability(AbilityInput::Auxiliary(1)),
|
||||||
|
self.extract_ability(AbilityInput::Auxiliary(2)),
|
||||||
|
self.extract_ability(AbilityInput::Auxiliary(3)),
|
||||||
|
self.extract_ability(AbilityInput::Auxiliary(4)),
|
||||||
|
];
|
||||||
|
let could_use_input = |input, desired_energy| match input {
|
||||||
|
InputKind::Primary => primary.as_ref().map_or(false, |p| {
|
||||||
|
p.could_use(attack_data, self, tgt_data, read_data, desired_energy)
|
||||||
|
}),
|
||||||
|
InputKind::Secondary => secondary.as_ref().map_or(false, |s| {
|
||||||
|
s.could_use(attack_data, self, tgt_data, read_data, desired_energy)
|
||||||
|
}),
|
||||||
|
InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| {
|
||||||
|
let ability = self.active_abilities.get_ability(
|
||||||
|
AbilityInput::Auxiliary(x),
|
||||||
|
Some(self.inventory),
|
||||||
|
Some(self.skill_set),
|
||||||
|
self.stats,
|
||||||
|
);
|
||||||
|
let additional_conditions = match ability {
|
||||||
|
Ability::MainWeaponAux(0) => self
|
||||||
|
.buffs
|
||||||
|
.map_or(false, |buffs| !buffs.contains(BuffKind::ScornfulTaunt)),
|
||||||
|
Ability::MainWeaponAux(2) => {
|
||||||
|
tgt_data.char_state.map_or(false, |cs| cs.is_stunned())
|
||||||
|
},
|
||||||
|
Ability::MainWeaponAux(4) => tgt_data.ori.map_or(false, |ori| {
|
||||||
|
ori.look_vec().angle_between(tgt_data.pos.0 - self.pos.0)
|
||||||
|
< combat::BEHIND_TARGET_ANGLE
|
||||||
|
}),
|
||||||
|
Ability::MainWeaponAux(5) => tgt_data.char_state.map_or(false, |cs| {
|
||||||
|
cs.is_block(AttackSource::Melee) || cs.is_parry(AttackSource::Melee)
|
||||||
|
}),
|
||||||
|
Ability::MainWeaponAux(7) => tgt_data
|
||||||
|
.buffs
|
||||||
|
.map_or(false, |buffs| !buffs.contains(BuffKind::Staggered)),
|
||||||
|
Ability::MainWeaponAux(12) => tgt_data
|
||||||
|
.buffs
|
||||||
|
.map_or(false, |buffs| !buffs.contains(BuffKind::Rooted)),
|
||||||
|
Ability::MainWeaponAux(13) => tgt_data
|
||||||
|
.buffs
|
||||||
|
.map_or(false, |buffs| !buffs.contains(BuffKind::Winded)),
|
||||||
|
Ability::MainWeaponAux(14) => tgt_data
|
||||||
|
.buffs
|
||||||
|
.map_or(false, |buffs| !buffs.contains(BuffKind::Concussion)),
|
||||||
|
Ability::MainWeaponAux(15) => self
|
||||||
|
.buffs
|
||||||
|
.map_or(false, |buffs| !buffs.contains(BuffKind::ProtectingWard)),
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
a.could_use(attack_data, self, tgt_data, read_data, desired_energy)
|
||||||
|
&& additional_conditions
|
||||||
|
}),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
let continue_current_input = |current_input, next_input: &mut Option<InputKind>| {
|
||||||
|
if matches!(current_input, InputKind::Secondary) {
|
||||||
|
let charging =
|
||||||
|
matches!(self.char_state.stage_section(), Some(StageSection::Charge));
|
||||||
|
let charged = self
|
||||||
|
.char_state
|
||||||
|
.durations()
|
||||||
|
.and_then(|durs| durs.charge)
|
||||||
|
.zip(self.char_state.timer())
|
||||||
|
.map_or(false, |(dur, timer)| timer > dur);
|
||||||
|
if !(charging && charged) {
|
||||||
|
*next_input = Some(InputKind::Secondary);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*next_input = Some(current_input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let current_input = self.char_state.ability_info().map(|ai| ai.input);
|
||||||
|
let ability_preferences = AbilityPreferences {
|
||||||
|
desired_energy: 40.0,
|
||||||
|
combo_scaling_buildup: 0,
|
||||||
|
};
|
||||||
|
let mut next_input = None;
|
||||||
|
if let Some(input) = current_input {
|
||||||
|
continue_current_input(input, &mut next_input);
|
||||||
|
} else {
|
||||||
|
match HammerTactics::from_u8(
|
||||||
|
agent.combat_state.int_counters[IntCounters::Tactic as usize],
|
||||||
|
) {
|
||||||
|
HammerTactics::Unskilled => {
|
||||||
|
if rng.gen_bool(0.5) {
|
||||||
|
next_input = Some(InputKind::Primary);
|
||||||
|
} else {
|
||||||
|
next_input = Some(InputKind::Secondary);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HammerTactics::Simple => {
|
||||||
|
if rng.gen_bool(0.5) {
|
||||||
|
next_input = Some(InputKind::Primary);
|
||||||
|
} else {
|
||||||
|
next_input = Some(InputKind::Secondary);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HammerTactics::AttackSimple | HammerTactics::SupportSimple => {
|
||||||
|
if could_use_input(InputKind::Ability(0), ability_preferences) {
|
||||||
|
next_input = Some(InputKind::Ability(0));
|
||||||
|
} else if rng.gen_bool(0.5) {
|
||||||
|
next_input = Some(InputKind::Primary);
|
||||||
|
} else {
|
||||||
|
next_input = Some(InputKind::Secondary);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HammerTactics::AttackIntermediate | HammerTactics::SupportIntermediate => {
|
||||||
|
let random_ability = InputKind::Ability(rng.gen_range(0..3));
|
||||||
|
if could_use_input(random_ability, ability_preferences) {
|
||||||
|
next_input = Some(random_ability);
|
||||||
|
} else if rng.gen_bool(0.5) {
|
||||||
|
next_input = Some(InputKind::Primary);
|
||||||
|
} else {
|
||||||
|
next_input = Some(InputKind::Secondary);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HammerTactics::AttackAdvanced | HammerTactics::SupportAdvanced => {
|
||||||
|
let random_ability = InputKind::Ability(rng.gen_range(0..5));
|
||||||
|
if could_use_input(random_ability, ability_preferences) {
|
||||||
|
next_input = Some(random_ability);
|
||||||
|
} else if rng.gen_bool(0.5) {
|
||||||
|
next_input = Some(InputKind::Primary);
|
||||||
|
} else {
|
||||||
|
next_input = Some(InputKind::Secondary);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HammerTactics::AttackExpert | HammerTactics::SupportExpert => {
|
||||||
|
let random_ability = InputKind::Ability(rng.gen_range(0..5));
|
||||||
|
if could_use_input(random_ability, ability_preferences) {
|
||||||
|
next_input = Some(random_ability);
|
||||||
|
} else if rng.gen_bool(0.5) {
|
||||||
|
next_input = Some(InputKind::Primary);
|
||||||
|
} else {
|
||||||
|
next_input = Some(InputKind::Secondary);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(input) = next_input {
|
||||||
|
if could_use_input(input, ability_preferences) {
|
||||||
|
controller.push_basic_input(input);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if attack_failed && attack_data.dist_sqrd > 1.5_f32.powi(2) {
|
||||||
|
self.path_toward_target(
|
||||||
|
agent,
|
||||||
|
controller,
|
||||||
|
tgt_data.pos.0,
|
||||||
|
read_data,
|
||||||
|
Path::Separate,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_sword_attack(
|
pub fn handle_sword_attack(
|
||||||
|
@ -27,6 +27,7 @@ use common::{
|
|||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::{IdMaps, Uid},
|
uid::{IdMaps, Uid},
|
||||||
};
|
};
|
||||||
|
use common_base::dev_panic;
|
||||||
use specs::{shred, Entities, Entity as EcsEntity, Read, ReadExpect, ReadStorage, SystemData};
|
use specs::{shred, Entities, Entity as EcsEntity, Read, ReadExpect, ReadStorage, SystemData};
|
||||||
|
|
||||||
event_emitters! {
|
event_emitters! {
|
||||||
@ -72,6 +73,7 @@ pub struct AgentData<'a> {
|
|||||||
|
|
||||||
pub struct TargetData<'a> {
|
pub struct TargetData<'a> {
|
||||||
pub pos: &'a Pos,
|
pub pos: &'a Pos,
|
||||||
|
pub ori: Option<&'a Ori>,
|
||||||
pub body: Option<&'a Body>,
|
pub body: Option<&'a Body>,
|
||||||
pub scale: Option<&'a Scale>,
|
pub scale: Option<&'a Scale>,
|
||||||
pub char_state: Option<&'a CharacterState>,
|
pub char_state: Option<&'a CharacterState>,
|
||||||
@ -84,6 +86,7 @@ impl<'a> TargetData<'a> {
|
|||||||
pub fn new(pos: &'a Pos, target: EcsEntity, read_data: &'a ReadData) -> Self {
|
pub fn new(pos: &'a Pos, target: EcsEntity, read_data: &'a ReadData) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pos,
|
pos,
|
||||||
|
ori: read_data.orientations.get(target),
|
||||||
body: read_data.bodies.get(target),
|
body: read_data.bodies.get(target),
|
||||||
scale: read_data.scales.get(target),
|
scale: read_data.scales.get(target),
|
||||||
char_state: read_data.char_states.get(target),
|
char_state: read_data.char_states.get(target),
|
||||||
@ -342,6 +345,39 @@ impl AxeTactics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum HammerTactics {
|
||||||
|
Unskilled = 0,
|
||||||
|
Simple = 1,
|
||||||
|
AttackSimple = 2,
|
||||||
|
SupportSimple = 3,
|
||||||
|
AttackIntermediate = 4,
|
||||||
|
SupportIntermediate = 5,
|
||||||
|
AttackAdvanced = 6,
|
||||||
|
SupportAdvanced = 7,
|
||||||
|
AttackExpert = 8,
|
||||||
|
SupportExpert = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HammerTactics {
|
||||||
|
pub fn from_u8(x: u8) -> Self {
|
||||||
|
use HammerTactics::*;
|
||||||
|
match x {
|
||||||
|
0 => Unskilled,
|
||||||
|
1 => Simple,
|
||||||
|
2 => AttackSimple,
|
||||||
|
3 => SupportSimple,
|
||||||
|
4 => AttackIntermediate,
|
||||||
|
5 => SupportIntermediate,
|
||||||
|
6 => AttackAdvanced,
|
||||||
|
7 => SupportAdvanced,
|
||||||
|
8 => AttackExpert,
|
||||||
|
9 => SupportExpert,
|
||||||
|
_ => Unskilled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
pub struct ReadData<'a> {
|
pub struct ReadData<'a> {
|
||||||
pub entities: Entities<'a>,
|
pub entities: Entities<'a>,
|
||||||
@ -481,6 +517,17 @@ pub enum AbilityData {
|
|||||||
angle: f32,
|
angle: f32,
|
||||||
ori_rate: f32,
|
ori_rate: f32,
|
||||||
},
|
},
|
||||||
|
Shockwave {
|
||||||
|
energy: f32,
|
||||||
|
angle: f32,
|
||||||
|
range: f32,
|
||||||
|
combo: u32,
|
||||||
|
},
|
||||||
|
// Note, buff check not done as auras could be non-buff and auras could target either in or
|
||||||
|
// out of group
|
||||||
|
StaticAura {
|
||||||
|
energy: f32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
@ -659,7 +706,29 @@ impl AbilityData {
|
|||||||
ori_rate: *ori_rate,
|
ori_rate: *ori_rate,
|
||||||
energy_drain: *energy_drain,
|
energy_drain: *energy_drain,
|
||||||
},
|
},
|
||||||
_ => return None,
|
Shockwave {
|
||||||
|
energy_cost,
|
||||||
|
shockwave_angle,
|
||||||
|
shockwave_speed,
|
||||||
|
shockwave_duration,
|
||||||
|
minimum_combo,
|
||||||
|
..
|
||||||
|
} => Self::Shockwave {
|
||||||
|
energy: *energy_cost,
|
||||||
|
angle: *shockwave_angle,
|
||||||
|
range: *shockwave_speed * *shockwave_duration,
|
||||||
|
combo: *minimum_combo,
|
||||||
|
},
|
||||||
|
StaticAura { energy_cost, .. } => Self::StaticAura {
|
||||||
|
energy: *energy_cost,
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
dev_panic!(
|
||||||
|
"Agent tried to use ability with a character state they haven't learned to \
|
||||||
|
understand"
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Some(inner)
|
Some(inner)
|
||||||
}
|
}
|
||||||
@ -888,6 +957,17 @@ impl AbilityData {
|
|||||||
angle,
|
angle,
|
||||||
ori_rate,
|
ori_rate,
|
||||||
} => beam_check(*range, *angle, *ori_rate) && energy_check(*energy_drain * 3.0),
|
} => beam_check(*range, *angle, *ori_rate) && energy_check(*energy_drain * 3.0),
|
||||||
|
Shockwave {
|
||||||
|
energy,
|
||||||
|
range,
|
||||||
|
angle,
|
||||||
|
combo,
|
||||||
|
} => {
|
||||||
|
melee_check(*range, *angle, None)
|
||||||
|
&& energy_check(*energy)
|
||||||
|
&& combo_check(*combo, false)
|
||||||
|
},
|
||||||
|
StaticAura { energy } => energy_check(*energy),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user