This commit is contained in:
Sam 2023-06-03 20:30:10 -04:00
parent dd616f85a9
commit 9fa556b165
5 changed files with 629 additions and 131 deletions

View File

@ -306,9 +306,9 @@ common-abilities-axe-bloodfeast = Bloodfeast
common-abilities-axe-fierce_raze = Fierce Raze
.desc =
A rapid flurry of strikes on your foe
common-abilities-axe-dueal_fierce_raze = Fierce Raze
common-abilities-axe-dual_fierce_raze = Fierce Raze
.desc =
A rapid flurry of strikes on yoru foe using both of your axes
A rapid flurry of strikes on your foe using both of your axes
common-abilities-axe-furor = Furor
.desc =
As your fury rises, your movement and attacks quicken

View File

@ -8,7 +8,7 @@ use common::{
ability::{ActiveAbilities, AuxiliaryAbility, Stance, SwordStance, MAX_ABILITIES},
buff::BuffKind,
item::tool::AbilityContext,
skills::{BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
AbilityInput, Agent, CharacterAbility, CharacterState, ControlAction, ControlEvent,
Controller, InputKind,
},
@ -327,18 +327,6 @@ impl<'a> AgentData<'a> {
}
}
pub fn handle_axe_attack(
&self,
_agent: &mut Agent,
_controller: &mut Controller,
_attack_data: &AttackData,
_tgt_data: &TargetData,
_read_data: &ReadData,
_rng: &mut impl Rng,
) {
// TODO
}
pub fn handle_hammer_attack(
&self,
agent: &mut Agent,
@ -738,7 +726,10 @@ impl<'a> AgentData<'a> {
agent.action_state.int_counters[IntCounters::Tactics as usize],
) {
SwordTactics::Unskilled => {
let desired_energy = 15.0;
let ability_preferences = AbilityPreferences {
desired_energy: 15.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -749,7 +740,7 @@ impl<'a> AgentData<'a> {
next_input = Some(InputKind::Secondary);
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -760,14 +751,17 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::Basic => {
let desired_energy = 25.0;
let ability_preferences = AbilityPreferences {
desired_energy: 25.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
continue_current_input(input, &mut next_input);
} else {
let attempt_ability = InputKind::Ability(rng.gen_range(0..5));
if could_use_input(attempt_ability, desired_energy) {
if could_use_input(attempt_ability, ability_preferences) {
next_input = Some(attempt_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
@ -776,7 +770,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -787,7 +781,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::HeavySimple => {
let desired_energy = 35.0;
let ability_preferences = AbilityPreferences {
desired_energy: 35.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -796,16 +793,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(3..5));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Heavy))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -814,7 +811,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -825,7 +822,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::AgileSimple => {
let desired_energy = 35.0;
let ability_preferences = AbilityPreferences {
desired_energy: 35.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -834,16 +834,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(3..5));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Agile))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -852,7 +852,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -863,7 +863,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::DefensiveSimple => {
let desired_energy = 35.0;
let ability_preferences = AbilityPreferences {
desired_energy: 35.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -872,18 +875,18 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(3..5));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Defensive))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(InputKind::Ability(3), desired_energy) {
} else if could_use_input(InputKind::Ability(3), ability_preferences) {
next_input = Some(InputKind::Ability(3));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -892,7 +895,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -903,7 +906,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::CripplingSimple => {
let desired_energy = 35.0;
let ability_preferences = AbilityPreferences {
desired_energy: 35.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -912,16 +918,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(3..5));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Crippling))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -930,7 +936,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -941,7 +947,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::CleavingSimple => {
let desired_energy = 35.0;
let ability_preferences = AbilityPreferences {
desired_energy: 35.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -950,16 +959,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(3..5));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Cleaving))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -968,7 +977,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -979,7 +988,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::HeavyAdvanced => {
let desired_energy = 50.0;
let ability_preferences = AbilityPreferences {
desired_energy: 50.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -988,16 +1000,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(1..3));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Heavy))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -1006,7 +1018,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -1017,7 +1029,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::AgileAdvanced => {
let desired_energy = 50.0;
let ability_preferences = AbilityPreferences {
desired_energy: 50.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -1026,16 +1041,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(1..3));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Agile))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -1044,7 +1059,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -1055,7 +1070,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::DefensiveAdvanced => {
let desired_energy = 50.0;
let ability_preferences = AbilityPreferences {
desired_energy: 50.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -1064,18 +1082,18 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(1..3));
let random_ability = InputKind::Ability(rng.gen_range(1..4));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Defensive))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else if could_use_input(random_ability, ability_preferences) {
next_input = Some(random_ability);
} else if could_use_input(InputKind::Ability(4), desired_energy)
} else if could_use_input(InputKind::Ability(4), ability_preferences)
&& rng.gen_bool(2.0 * read_data.dt.0 as f64)
{
next_input = Some(InputKind::Ability(4));
@ -1086,7 +1104,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -1097,7 +1115,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::CripplingAdvanced => {
let desired_energy = 50.0;
let ability_preferences = AbilityPreferences {
desired_energy: 50.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -1106,16 +1127,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(1..3));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Crippling))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -1124,7 +1145,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -1135,7 +1156,10 @@ impl<'a> AgentData<'a> {
}
},
SwordTactics::CleavingAdvanced => {
let desired_energy = 50.0;
let ability_preferences = AbilityPreferences {
desired_energy: 50.0,
combo_scaling_buildup: 0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
@ -1144,16 +1168,16 @@ impl<'a> AgentData<'a> {
let stance_ability = InputKind::Ability(rng.gen_range(1..3));
let random_ability = InputKind::Ability(rng.gen_range(1..5));
if !matches!(self.stance, Some(Stance::Sword(SwordStance::Cleaving))) {
if could_use_input(stance_ability, desired_energy) {
if could_use_input(stance_ability, ability_preferences) {
next_input = Some(stance_ability);
} else if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
} else if could_use_input(InputKind::Ability(0), desired_energy) {
} else if could_use_input(InputKind::Ability(0), ability_preferences) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, desired_energy) {
} else 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);
@ -1162,7 +1186,7 @@ impl<'a> AgentData<'a> {
}
};
if let Some(input) = next_input {
if could_use_input(input, desired_energy) {
if could_use_input(input, ability_preferences) {
controller.push_basic_input(input);
false
} else {
@ -1189,6 +1213,322 @@ impl<'a> AgentData<'a> {
}
}
pub fn handle_axe_attack(
&self,
agent: &mut Agent,
controller: &mut Controller,
attack_data: &AttackData,
tgt_data: &TargetData,
read_data: &ReadData,
rng: &mut impl Rng,
) {
if !agent.action_state.initialized {
agent.action_state.initialized = true;
let available_tactics = {
let mut tactics = Vec::new();
let try_tactic = |skill, tactic, tactics: &mut Vec<AxeTactics>| {
if self.skill_set.has_skill(Skill::Axe(skill)) {
tactics.push(tactic);
}
};
try_tactic(AxeSkill::Execute, AxeTactics::SavageAdvanced, &mut tactics);
try_tactic(
AxeSkill::Lacerate,
AxeTactics::MercilessAdvanced,
&mut tactics,
);
try_tactic(AxeSkill::Bulkhead, AxeTactics::RivingAdvanced, &mut tactics);
if tactics.is_empty() {
try_tactic(
AxeSkill::RisingTide,
AxeTactics::SavageIntermediate,
&mut tactics,
);
try_tactic(
AxeSkill::FierceRaze,
AxeTactics::MercilessIntermediate,
&mut tactics,
);
try_tactic(
AxeSkill::Plunder,
AxeTactics::RivingIntermediate,
&mut tactics,
);
}
if tactics.is_empty() {
try_tactic(
AxeSkill::BrutalSwing,
AxeTactics::SavageSimple,
&mut tactics,
);
try_tactic(AxeSkill::Rake, AxeTactics::MercilessSimple, &mut tactics);
try_tactic(AxeSkill::SkullBash, AxeTactics::RivingSimple, &mut tactics);
}
if tactics.is_empty() {
tactics.push(AxeTactics::Unskilled);
}
tactics
};
let tactic = available_tactics
.choose(rng)
.copied()
.unwrap_or(AxeTactics::Unskilled);
agent.action_state.int_counters[IntCounters::Tactic as usize] = tactic as u8;
let auxiliary_key = ActiveAbilities::active_auxiliary_key(Some(self.inventory));
let set_axe_ability = |controller: &mut Controller, slot, skill| {
controller.push_event(ControlEvent::ChangeAbility {
slot,
auxiliary_key,
new_ability: AuxiliaryAbility::MainWeapon(skill),
});
};
match tactic {
AxeTactics::Unskilled => {},
AxeTactics::SavageSimple => {
// Brutal swing
set_axe_ability(controller, 0, 0);
},
AxeTactics::MercilessSimple => {
// Rake
set_axe_ability(controller, 0, 6);
},
AxeTactics::RivingSimple => {
// Skull bash
set_axe_ability(controller, 0, 12);
},
AxeTactics::SavageIntermediate => {
// Brutal swing
set_axe_ability(controller, 0, 0);
// Berserk
set_axe_ability(controller, 1, 1);
// Rising tide
set_axe_ability(controller, 2, 2);
},
AxeTactics::MercilessIntermediate => {
// Rake
set_axe_ability(controller, 0, 6);
// Bloodfeast
set_axe_ability(controller, 1, 7);
// Fierce raze
set_axe_ability(controller, 2, 8);
},
AxeTactics::RivingIntermediate => {
// Skull bash
set_axe_ability(controller, 0, 12);
// Sunder
set_axe_ability(controller, 1, 13);
// Plunder
set_axe_ability(controller, 2, 14);
},
AxeTactics::SavageAdvanced => {
// Berserk
set_axe_ability(controller, 0, 1);
// Rising tide
set_axe_ability(controller, 1, 2);
// Savage sense
set_axe_ability(controller, 2, 3);
// Adrenaline rush
set_axe_ability(controller, 3, 4);
// Execute/maelstrom
set_axe_ability(controller, 4, 5);
},
AxeTactics::MercilessAdvanced => {
// Bloodfeast
set_axe_ability(controller, 0, 7);
// Fierce raze
set_axe_ability(controller, 1, 8);
// Furor
set_axe_ability(controller, 2, 9);
// Fracture
set_axe_ability(controller, 3, 10);
// Lacerate/riptide
set_axe_ability(controller, 4, 11);
},
AxeTactics::RivingAdvanced => {
// Sunder
set_axe_ability(controller, 0, 13);
// Plunder
set_axe_ability(controller, 1, 14);
// Defiance
set_axe_ability(controller, 2, 15);
// Keelhaul
set_axe_ability(controller, 3, 16);
// Bulkhead/capsize
set_axe_ability(controller, 4, 17);
},
}
agent.action_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| {
a.could_use(attack_data, self, tgt_data, read_data, desired_energy)
}),
_ => 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: 15,
};
let mut next_input = None;
if let Some(input) = current_input {
continue_current_input(input, &mut next_input);
} else {
match AxeTactics::from_u8(
agent.action_state.int_counters[IntCounters::Tactic as usize],
) {
AxeTactics::Unskilled => {
if rng.gen_bool(0.5) {
next_input = Some(InputKind::Primary);
} else {
next_input = Some(InputKind::Secondary);
}
},
AxeTactics::SavageSimple
| AxeTactics::MercilessSimple
| AxeTactics::RivingSimple => {
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);
}
},
AxeTactics::SavageIntermediate
| AxeTactics::MercilessIntermediate
| AxeTactics::RivingIntermediate => {
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);
}
},
AxeTactics::SavageAdvanced
| AxeTactics::MercilessAdvanced
| AxeTactics::RivingAdvanced => {
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_bow_attack(
&self,
agent: &mut Agent,
@ -4767,10 +5107,22 @@ impl<'a> AgentData<'a> {
let secondary = self.extract_ability(AbilityInput::Secondary);
let could_use_input = |input| match input {
InputKind::Primary => primary.as_ref().map_or(false, |p| {
p.could_use(attack_data, self, tgt_data, read_data, 0.0)
p.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Secondary => secondary.as_ref().map_or(false, |s| {
s.could_use(attack_data, self, tgt_data, read_data, 0.0)
s.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
_ => false,
};
@ -4823,10 +5175,22 @@ impl<'a> AgentData<'a> {
let secondary = self.extract_ability(AbilityInput::Secondary);
let could_use_input = |input| match input {
InputKind::Primary => primary.as_ref().map_or(false, |p| {
p.could_use(attack_data, self, tgt_data, read_data, 0.0)
p.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Secondary => secondary.as_ref().map_or(false, |s| {
s.could_use(attack_data, self, tgt_data, read_data, 0.0)
s.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
_ => false,
};
@ -4868,7 +5232,13 @@ impl<'a> AgentData<'a> {
let primary = self.extract_ability(AbilityInput::Primary);
let could_use_input = |input| match input {
InputKind::Primary => primary.as_ref().map_or(false, |p| {
p.could_use(attack_data, self, tgt_data, read_data, 0.0)
p.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
_ => false,
};
@ -4916,13 +5286,31 @@ impl<'a> AgentData<'a> {
];
let could_use_input = |input| match input {
InputKind::Primary => primary.as_ref().map_or(false, |p| {
p.could_use(attack_data, self, tgt_data, read_data, 0.0)
p.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Secondary => secondary.as_ref().map_or(false, |s| {
s.could_use(attack_data, self, tgt_data, read_data, 0.0)
s.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| {
a.could_use(attack_data, self, tgt_data, read_data, 0.0)
a.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
_ => false,
};
@ -4984,13 +5372,31 @@ impl<'a> AgentData<'a> {
];
let could_use_input = |input| match input {
InputKind::Primary => primary.as_ref().map_or(false, |p| {
p.could_use(attack_data, self, tgt_data, read_data, 0.0)
p.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Secondary => secondary.as_ref().map_or(false, |s| {
s.could_use(attack_data, self, tgt_data, read_data, 0.0)
s.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| {
a.could_use(attack_data, self, tgt_data, read_data, 0.0)
a.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
_ => false,
};
@ -5077,13 +5483,31 @@ impl<'a> AgentData<'a> {
];
let could_use_input = |input| match input {
InputKind::Primary => primary.as_ref().map_or(false, |p| {
p.could_use(attack_data, self, tgt_data, read_data, 0.0)
p.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Secondary => secondary.as_ref().map_or(false, |s| {
s.could_use(attack_data, self, tgt_data, read_data, 0.0)
s.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| {
a.could_use(attack_data, self, tgt_data, read_data, 0.0)
a.could_use(
attack_data,
self,
tgt_data,
read_data,
AbilityPreferences::default(),
)
}),
_ => false,
};

View File

@ -6,7 +6,10 @@ use common::{
character_state::AttackFilters,
group,
inventory::{
item::{tool::ToolKind, ItemKind, MaterialStatManifest},
item::{
tool::{AbilityMap, ToolKind},
ItemKind, MaterialStatManifest,
},
slot::EquipSlot,
},
ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy, Health, Inventory,
@ -246,7 +249,7 @@ pub enum Tactic {
AdletElder,
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub enum SwordTactics {
Unskilled = 0,
Basic = 1,
@ -283,6 +286,39 @@ impl SwordTactics {
}
}
#[derive(Copy, Clone, Debug)]
pub enum AxeTactics {
Unskilled = 0,
SavageSimple = 1,
MercilessSimple = 2,
RivingSimple = 3,
SavageIntermediate = 4,
MercilessIntermediate = 5,
RivingIntermediate = 6,
SavageAdvanced = 7,
MercilessAdvanced = 8,
RivingAdvanced = 9,
}
impl AxeTactics {
pub fn from_u8(x: u8) -> Self {
use AxeTactics::*;
match x {
0 => Unskilled,
1 => SavageSimple,
2 => MercilessSimple,
3 => RivingSimple,
4 => SavageIntermediate,
5 => MercilessIntermediate,
6 => RivingIntermediate,
7 => SavageAdvanced,
8 => MercilessAdvanced,
9 => RivingAdvanced,
_ => Unskilled,
}
}
}
#[derive(SystemData)]
pub struct ReadData<'a> {
pub entities: Entities<'a>,
@ -323,6 +359,7 @@ pub struct ReadData<'a> {
pub poises: ReadStorage<'a, Poise>,
pub stances: ReadStorage<'a, Stance>,
pub presences: ReadStorage<'a, Presence>,
pub ability_map: ReadExpect<'a, AbilityMap>,
}
impl<'a> ReadData<'a> {
@ -353,10 +390,13 @@ pub enum AbilityData {
angle: f32,
energy: f32,
combo: u32,
combo_scales: bool,
},
SelfBuff {
buff: BuffKind,
energy: f32,
combo: u32,
combo_scales: bool,
},
DiveMelee {
range: f32,
@ -420,6 +460,12 @@ pub enum AbilityData {
},
}
#[derive(Copy, Clone, Debug, Default)]
pub struct AbilityPreferences {
pub desired_energy: f32,
pub combo_scaling_buildup: u32,
}
impl AbilityData {
pub fn from_ability(ability: &CharacterAbility) -> Option<Self> {
use CharacterAbility::*;
@ -456,20 +502,26 @@ impl AbilityData {
energy_cost,
melee_constructor,
minimum_combo,
scaling,
..
} => Self::FinisherMelee {
energy: *energy_cost,
range: melee_constructor.range,
angle: melee_constructor.angle,
combo: *minimum_combo,
combo_scales: scaling.is_some(),
},
SelfBuff {
buff_kind,
energy_cost,
combo_cost,
combo_scaling,
..
} => Self::SelfBuff {
buff: *buff_kind,
energy: *energy_cost,
combo: *combo_cost,
combo_scales: combo_scaling.is_some(),
},
DiveMelee {
energy_cost,
@ -595,7 +647,7 @@ impl AbilityData {
agent_data: &AgentData,
tgt_data: &TargetData,
read_data: &ReadData,
desired_energy: f32,
ability_preferences: AbilityPreferences,
) -> bool {
let melee_check = |range: f32, angle, forced_movement: Option<ForcedMovement>| {
let (range_inc, min_mult) = forced_movement.map_or((0.0, 0.0), |fm| match fm {
@ -621,9 +673,19 @@ impl AbilityData {
};
let energy_check = |energy: f32| {
agent_data.energy.current() >= energy
&& (energy < f32::EPSILON || agent_data.energy.current() >= desired_energy)
&& (energy < f32::EPSILON
|| agent_data.energy.current() >= ability_preferences.desired_energy)
};
let combo_check = |combo, scales| {
let additional_combo = if scales {
ability_preferences.combo_scaling_buildup
} else {
0
};
agent_data
.combo
.map_or(false, |c| c.counter() >= combo + additional_combo)
};
let combo_check = |combo| agent_data.combo.map_or(false, |c| c.counter() >= combo);
let attack_kind_check = |attacks: AttackFilters| {
tgt_data
.char_state
@ -673,9 +735,20 @@ impl AbilityData {
angle,
energy,
combo,
} => melee_check(*range, *angle, None) && energy_check(*energy) && combo_check(*combo),
SelfBuff { buff, energy } => {
combo_scales,
} => {
melee_check(*range, *angle, None)
&& energy_check(*energy)
&& combo_check(*combo, *combo_scales)
},
SelfBuff {
buff,
energy,
combo,
combo_scales,
} => {
energy_check(*energy)
&& combo_check(*combo, *combo_scales)
&& agent_data
.buffs
.map_or(false, |buffs| !buffs.contains(*buff))
@ -716,7 +789,7 @@ impl AbilityData {
} => {
melee_check(*range, *angle, None)
&& energy_check(*energy_per_strike * *strikes as f32)
&& combo_check(*combo)
&& combo_check(*combo, false)
},
ChargedMelee {
range,

View File

@ -1061,43 +1061,6 @@ impl Animation for ComboAnimation {
next.belt.orientation.rotate_z(move2 * -0.6);
next.control.position += Vec3::new(move2 * -6.0, move2 * -20.0, move2 * -4.0);
},
Some("common.abilities.axe.fracture") => {
let (move1, move2) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0),
Some(StageSection::Action) => (1.0, anim_time),
Some(StageSection::Recover) => (1.0, 1.0),
_ => (0.0, 0.0),
};
let move1 = move1 * multi_strike_pullback;
let move2 = move2 * multi_strike_pullback;
next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4);
next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5);
next.control.position = Vec3::new(s_a.ac.0, s_a.ac.1, s_a.ac.2);
next.control.orientation = Quaternion::rotation_x(s_a.ac.3)
* Quaternion::rotation_y(s_a.ac.4)
* Quaternion::rotation_z(s_a.ac.5 + move1 * -PI / 2.0 + move2 * -0.5);
next.control.orientation.rotate_x(move1 * 0.0);
next.chest.orientation.rotate_x(move1 * -0.5);
next.chest.orientation.rotate_z(move1 * 0.7);
next.head.orientation.rotate_z(move1 * -0.3);
next.belt.orientation.rotate_z(move1 * -0.1);
next.shorts.orientation.rotate_z(move1 * -0.4);
next.chest.orientation.rotate_z(move2 * -1.8);
next.head.orientation.rotate_z(move2 * 0.9);
next.shorts.orientation.rotate_z(move2 * 1.3);
next.belt.orientation.rotate_z(move2 * 0.6);
next.control.orientation.rotate_x(move2 * -0.9);
next.control.orientation.rotate_z(move2 * -3.5);
next.control.position += Vec3::new(move2 * 14.0, move2 * 6.0, 0.0);
},
Some("common.abilities.axe.skull_bash") => {
let (move1, move2) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0),

View File

@ -402,6 +402,44 @@ impl Animation for FinisherMeleeAnimation {
next.control.position += Vec3::new(move2 * 12.0, move2 * -6.0, 0.0);
next.torso.orientation.rotate_z(move2_raw * -TAU);
},
Some("common.abilities.axe.fracture") => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
Some(StageSection::Action) => (1.0, anim_time, 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move2 = move2 * pullback;
next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4);
next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5);
next.control.position = Vec3::new(s_a.ac.0, s_a.ac.1, s_a.ac.2);
next.control.orientation = Quaternion::rotation_x(s_a.ac.3)
* Quaternion::rotation_y(s_a.ac.4)
* Quaternion::rotation_z(s_a.ac.5 + move1 * -PI / 2.0 + move2 * -0.5);
next.control.orientation.rotate_x(move1 * 0.0);
next.chest.orientation.rotate_x(move1 * -0.5);
next.chest.orientation.rotate_z(move1 * 0.7);
next.head.orientation.rotate_z(move1 * -0.3);
next.belt.orientation.rotate_z(move1 * -0.1);
next.shorts.orientation.rotate_z(move1 * -0.4);
next.chest.orientation.rotate_z(move2 * -1.8);
next.head.orientation.rotate_z(move2 * 0.9);
next.shorts.orientation.rotate_z(move2 * 1.3);
next.belt.orientation.rotate_z(move2 * 0.6);
next.control.orientation.rotate_x(move2 * -0.9);
next.control.orientation.rotate_z(move2 * -3.5);
next.control.position += Vec3::new(move2 * 14.0, move2 * 6.0, 0.0);
},
_ => {},
}