Advanced AI for every stance.

This commit is contained in:
Sam 2023-01-28 01:12:11 -05:00
parent 9ea70d71e5
commit 34168d9c76
8 changed files with 539 additions and 199 deletions

View File

@ -8,13 +8,13 @@
loadout: Inline((
inherit: Asset("common.loadout.dungeon.tier-5.cultist"),
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")),
// (2, Item("common.items.weapons.staff.cultist_staff")),
// (2, Item("common.items.weapons.hammer.cultist_purp_2h-0")),
// (2, ModularWeapon(tool: Hammer, material: Orichalcum, hands: One)),
// (2, Item("common.items.weapons.bow.velorite")),
// (1, Item("common.items.weapons.sceptre.sceptre_velorite_0")),
(2, Item("common.items.weapons.staff.cultist_staff")),
(2, Item("common.items.weapons.hammer.cultist_purp_2h-0")),
(2, ModularWeapon(tool: Hammer, material: Orichalcum, hands: One)),
(2, Item("common.items.weapons.bow.velorite")),
(1, Item("common.items.weapons.sceptre.sceptre_velorite_0")),
]), None)),
)),
),

View File

@ -376,20 +376,7 @@ impl CharacterState {
}
pub fn is_melee_attack(&self) -> bool {
matches!(
self,
CharacterState::BasicMelee(_)
| CharacterState::DashMelee(_)
| CharacterState::ComboMelee(_)
| CharacterState::ComboMelee2(_)
| CharacterState::LeapMelee(_)
| CharacterState::SpinMelee(_)
| CharacterState::ChargedMelee(_)
| CharacterState::FinisherMelee(_)
| CharacterState::DiveMelee(_)
| CharacterState::RiposteMelee(_)
| CharacterState::RapidMelee(_)
)
matches!(self.attack_kind(), Some(AttackSource::Melee))
}
pub fn can_perform_mounted(&self) -> bool {
@ -880,6 +867,67 @@ impl CharacterState {
CharacterState::RapidMelee(data) => Some(data.timer),
}
}
pub fn attack_kind(&self) -> Option<AttackSource> {
match self {
CharacterState::Idle(_) => None,
CharacterState::Talk => None,
CharacterState::Climb(_) => None,
CharacterState::Wallrun(_) => None,
CharacterState::Skate(_) => None,
CharacterState::Glide(_) => None,
CharacterState::GlideWield(_) => None,
CharacterState::Stunned(_) => None,
CharacterState::Sit => None,
CharacterState::Dance => None,
CharacterState::BasicBlock(_) => None,
CharacterState::Roll(_) => None,
CharacterState::Wielding(_) => None,
CharacterState::Equipping(_) => None,
CharacterState::ComboMelee(_) => Some(AttackSource::Melee),
CharacterState::ComboMelee2(_) => Some(AttackSource::Melee),
CharacterState::BasicMelee(_) => Some(AttackSource::Melee),
CharacterState::BasicRanged(data) => {
Some(if data.static_data.projectile.is_explosive() {
AttackSource::Explosion
} else {
AttackSource::Projectile
})
},
CharacterState::Boost(_) => None,
CharacterState::DashMelee(_) => Some(AttackSource::Melee),
CharacterState::LeapMelee(_) => Some(AttackSource::Melee),
CharacterState::SpinMelee(_) => Some(AttackSource::Melee),
CharacterState::ChargedMelee(_) => Some(AttackSource::Melee),
// TODO: When charged ranged not only arrow make this check projectile type
CharacterState::ChargedRanged(_) => Some(AttackSource::Projectile),
CharacterState::RepeaterRanged(data) => {
Some(if data.static_data.projectile.is_explosive() {
AttackSource::Explosion
} else {
AttackSource::Projectile
})
},
CharacterState::Shockwave(data) => Some(if data.static_data.requires_ground {
AttackSource::GroundShockwave
} else {
AttackSource::AirShockwave
}),
CharacterState::BasicBeam(_) => Some(AttackSource::Beam),
CharacterState::BasicAura(_) => None,
CharacterState::Blink(_) => None,
CharacterState::BasicSummon(_) => None,
CharacterState::SelfBuff(_) => None,
CharacterState::SpriteSummon(_) => None,
CharacterState::UseItem(_) => None,
CharacterState::SpriteInteract(_) => None,
CharacterState::FinisherMelee(_) => Some(AttackSource::Melee),
CharacterState::Music(_) => None,
CharacterState::DiveMelee(_) => Some(AttackSource::Melee),
CharacterState::RiposteMelee(_) => Some(AttackSource::Melee),
CharacterState::RapidMelee(_) => Some(AttackSource::Melee),
}
}
}
#[derive(Default, Copy, Clone)]

View File

@ -776,4 +776,20 @@ impl ProjectileConstructor {
}
self
}
pub fn is_explosive(&self) -> bool {
use ProjectileConstructor::*;
match self {
Arrow { .. } => false,
Fireball { .. } => true,
Frostball { .. } => true,
Poisonball { .. } => true,
NecroticSphere { .. } => true,
Possess => false,
ClayRocket { .. } => true,
Snowball { .. } => true,
ExplodingPumpkin { .. } => true,
DagonBomb { .. } => true,
}
}
}

View File

@ -518,31 +518,31 @@ impl<'a> AgentData<'a> {
tactics.push(tactic);
}
};
// try_tactic(
// SwordSkill::HeavyFortitude,
// SwordTactics::HeavyAdvanced,
// &mut tactics,
// );
// try_tactic(
// SwordSkill::AgileDancingEdge,
// SwordTactics::AgileAdvanced,
// &mut tactics,
// );
// try_tactic(
// SwordSkill::DefensiveStalwartSword,
// SwordTactics::DefensiveAdvanced,
// &mut tactics,
// );
// try_tactic(
// SwordSkill::CripplingEviscerate,
// SwordTactics::CripplingAdvanced,
// &mut tactics,
// );
// try_tactic(
// SwordSkill::CleavingBladeFever,
// SwordTactics::CleavingAdvanced,
// &mut tactics,
// );
try_tactic(
SwordSkill::HeavyFortitude,
SwordTactics::HeavyAdvanced,
&mut tactics,
);
try_tactic(
SwordSkill::AgileDancingEdge,
SwordTactics::AgileAdvanced,
&mut tactics,
);
try_tactic(
SwordSkill::DefensiveStalwartSword,
SwordTactics::DefensiveAdvanced,
&mut tactics,
);
try_tactic(
SwordSkill::CripplingEviscerate,
SwordTactics::CripplingAdvanced,
&mut tactics,
);
try_tactic(
SwordSkill::CleavingBladeFever,
SwordTactics::CleavingAdvanced,
&mut tactics,
);
if tactics.is_empty() {
try_tactic(
SwordSkill::HeavyWindmillSlash,
@ -587,7 +587,7 @@ impl<'a> AgentData<'a> {
agent.action_state.int_counters[IntCounters::Tactics as usize] = tactic as u8;
let auxiliary_key = ActiveAbilities::active_auxiliary_key(Some(self.inventory));
let mut set_sword_ability = |controller: &mut Controller, slot, skill| {
let set_sword_ability = |controller: &mut Controller, slot, skill| {
controller.push_event(ControlEvent::ChangeAbility {
slot,
auxiliary_key,
@ -770,7 +770,6 @@ impl<'a> AgentData<'a> {
enum IntCounters {
Tactics = 0,
ActionMode = 1,
NextInput = 2,
}
enum Timers {
@ -781,7 +780,6 @@ impl<'a> AgentData<'a> {
enum Conditions {
GuardedDefend = 0,
RollingBreakThrough = 1,
TacticMisc = 2,
}
enum FloatCounters {
@ -876,8 +874,7 @@ impl<'a> AgentData<'a> {
Some(self.pos.0 + rand_dir * actual_dist)
};
}
} else {
if let Some(pos) =
} else if let Some(pos) =
agent.action_state.positions[Positions::GuardedCover as usize]
{
self.path_toward_target(
@ -888,9 +885,7 @@ impl<'a> AgentData<'a> {
Path::Separate,
None,
);
if agent.action_state.conditions
[Conditions::RollingBreakThrough as usize]
{
if agent.action_state.conditions[Conditions::RollingBreakThrough as usize] {
controller.push_basic_input(InputKind::Roll);
agent.action_state.conditions
[Conditions::RollingBreakThrough as usize] = false;
@ -936,7 +931,6 @@ impl<'a> AgentData<'a> {
Some(pos)
}
}
}
false
}
},
@ -1030,23 +1024,6 @@ impl<'a> AgentData<'a> {
extract_ability(AbilityInput::Auxiliary(3)),
extract_ability(AbilityInput::Auxiliary(4)),
];
fn input_from_u8(x: u8) -> Option<InputKind> {
let input = match x {
x @ 0..5 => InputKind::Ability(x as usize),
6 => InputKind::Primary,
7 => InputKind::Secondary,
_ => return None,
};
Some(input)
}
fn input_to_u8(input: InputKind) -> u8 {
match input {
InputKind::Ability(x) => x as u8,
InputKind::Primary => 6,
InputKind::Secondary => 7,
_ => 255,
}
}
let could_use_input = |input, misc_data| match input {
InputKind::Primary => primary.as_ref().map_or(false, |p| {
p.could_use(attack_data, self, misc_data, tgt_data)
@ -1069,7 +1046,7 @@ impl<'a> AgentData<'a> {
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if let input = InputKind::Secondary {
if matches!(input, InputKind::Secondary) {
let charging = matches!(
self.char_state.stage_section(),
Some(StageSection::Charge)
@ -1086,12 +1063,10 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(input);
}
} else {
if rng.gen_bool(0.5) {
} 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, &misc_data) {
@ -1111,7 +1086,7 @@ impl<'a> AgentData<'a> {
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if let input = InputKind::Secondary {
if matches!(input, InputKind::Secondary) {
let charging = matches!(
self.char_state.stage_section(),
Some(StageSection::Charge)
@ -1151,12 +1126,12 @@ impl<'a> AgentData<'a> {
},
SwordTactics::HeavySimple => {
let misc_data = MiscData {
desired_energy: 25.0,
desired_energy: 35.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if let input = InputKind::Secondary {
if matches!(input, InputKind::Secondary) {
let charging = matches!(
self.char_state.stage_section(),
Some(StageSection::Charge)
@ -1184,8 +1159,7 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
} else {
if could_use_input(InputKind::Ability(0), &misc_data) {
} else if could_use_input(InputKind::Ability(0), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
next_input = Some(random_ability);
@ -1194,7 +1168,6 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
}
};
if let Some(input) = next_input {
if could_use_input(input, &misc_data) {
@ -1209,12 +1182,12 @@ impl<'a> AgentData<'a> {
},
SwordTactics::AgileSimple => {
let misc_data = MiscData {
desired_energy: 25.0,
desired_energy: 35.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if let input = InputKind::Secondary {
if matches!(input, InputKind::Secondary) {
let charging = matches!(
self.char_state.stage_section(),
Some(StageSection::Charge)
@ -1242,8 +1215,7 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
} else {
if could_use_input(InputKind::Ability(0), &misc_data) {
} else if could_use_input(InputKind::Ability(0), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
next_input = Some(random_ability);
@ -1252,7 +1224,6 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
}
};
if let Some(input) = next_input {
if could_use_input(input, &misc_data) {
@ -1267,12 +1238,12 @@ impl<'a> AgentData<'a> {
},
SwordTactics::DefensiveSimple => {
let misc_data = MiscData {
desired_energy: 25.0,
desired_energy: 35.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if let input = InputKind::Secondary {
if matches!(input, InputKind::Secondary) {
let charging = matches!(
self.char_state.stage_section(),
Some(StageSection::Charge)
@ -1300,8 +1271,7 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
} else {
if could_use_input(InputKind::Ability(0), &misc_data) {
} else if could_use_input(InputKind::Ability(0), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(InputKind::Ability(3), &misc_data) {
next_input = Some(InputKind::Ability(3));
@ -1312,7 +1282,6 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
}
};
if let Some(input) = next_input {
if could_use_input(input, &misc_data) {
@ -1327,12 +1296,12 @@ impl<'a> AgentData<'a> {
},
SwordTactics::CripplingSimple => {
let misc_data = MiscData {
desired_energy: 25.0,
desired_energy: 35.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if let input = InputKind::Secondary {
if matches!(input, InputKind::Secondary) {
let charging = matches!(
self.char_state.stage_section(),
Some(StageSection::Charge)
@ -1360,8 +1329,7 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
} else {
if could_use_input(InputKind::Ability(0), &misc_data) {
} else if could_use_input(InputKind::Ability(0), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
next_input = Some(random_ability);
@ -1370,7 +1338,6 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
}
};
if let Some(input) = next_input {
if could_use_input(input, &misc_data) {
@ -1385,12 +1352,12 @@ impl<'a> AgentData<'a> {
},
SwordTactics::CleavingSimple => {
let misc_data = MiscData {
desired_energy: 25.0,
desired_energy: 35.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if let input = InputKind::Secondary {
if matches!(input, InputKind::Secondary) {
let charging = matches!(
self.char_state.stage_section(),
Some(StageSection::Charge)
@ -1418,8 +1385,291 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
} else {
if could_use_input(InputKind::Ability(0), &misc_data) {
} else if could_use_input(InputKind::Ability(0), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
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, &misc_data) {
controller.push_basic_input(input);
false
} else {
true
}
} else {
true
}
},
SwordTactics::HeavyAdvanced => {
let misc_data = MiscData {
desired_energy: 50.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if matches!(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(input);
}
} else {
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, &misc_data) {
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), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
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, &misc_data) {
controller.push_basic_input(input);
false
} else {
true
}
} else {
true
}
},
SwordTactics::AgileAdvanced => {
let misc_data = MiscData {
desired_energy: 50.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if matches!(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(input);
}
} else {
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, &misc_data) {
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), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
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, &misc_data) {
controller.push_basic_input(input);
false
} else {
true
}
} else {
true
}
},
SwordTactics::DefensiveAdvanced => {
let misc_data = MiscData {
desired_energy: 50.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if matches!(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(input);
}
} else {
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, &misc_data) {
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), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
next_input = Some(random_ability);
} else if could_use_input(InputKind::Ability(4), &misc_data)
&& rng.gen_bool(2.0 * read_data.dt.0 as f64)
{
next_input = Some(InputKind::Ability(4));
} 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, &misc_data) {
controller.push_basic_input(input);
false
} else {
true
}
} else {
true
}
},
SwordTactics::CripplingAdvanced => {
let misc_data = MiscData {
desired_energy: 50.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if matches!(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(input);
}
} else {
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, &misc_data) {
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), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
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, &misc_data) {
controller.push_basic_input(input);
false
} else {
true
}
} else {
true
}
},
SwordTactics::CleavingAdvanced => {
let misc_data = MiscData {
desired_energy: 50.0,
};
let current_input = self.char_state.ability_info().map(|ai| ai.input);
let mut next_input = None;
if let Some(input) = current_input {
if matches!(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(input);
}
} else {
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, &misc_data) {
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), &misc_data) {
next_input = Some(InputKind::Ability(0));
} else if could_use_input(random_ability, &misc_data) {
next_input = Some(random_ability);
@ -1428,7 +1678,6 @@ impl<'a> AgentData<'a> {
} else {
next_input = Some(InputKind::Secondary);
}
}
};
if let Some(input) = next_input {
if could_use_input(input, &misc_data) {
@ -1441,11 +1690,6 @@ impl<'a> AgentData<'a> {
true
}
},
SwordTactics::HeavyAdvanced => true,
SwordTactics::AgileAdvanced => true,
SwordTactics::DefensiveAdvanced => true,
SwordTactics::CripplingAdvanced => true,
SwordTactics::CleavingAdvanced => true,
}
} else {
false

View File

@ -2,6 +2,7 @@ use common::{
comp::{
ability::CharacterAbility,
buff::{BuffKind, Buffs},
character_state::AttackFilters,
group,
item::MaterialStatManifest,
ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy, Health, Inventory,
@ -245,6 +246,11 @@ pub enum AbilityData {
angle: f32,
energy: f32,
},
BasicBlock {
energy: f32,
blocked_attacks: AttackFilters,
angle: f32,
},
}
pub struct MiscData {
@ -361,6 +367,16 @@ impl AbilityData {
range: melee_constructor.range,
angle: melee_constructor.angle,
},
BasicBlock {
max_angle,
energy_cost,
blocked_attacks,
..
} => Self::BasicBlock {
energy: *energy_cost,
angle: *max_angle,
blocked_attacks: *blocked_attacks,
},
_ => return None,
};
Some(inner)
@ -390,6 +406,12 @@ impl AbilityData {
|| agent_data.energy.current() >= misc_data.desired_energy)
};
let combo_check = |combo| agent_data.combo.map_or(false, |c| c.counter() >= combo);
let attack_kind_check = |attacks: AttackFilters| {
tgt_data
.char_state
.and_then(|cs| cs.attack_kind())
.map_or(false, |ak| attacks.applies(ak))
};
use AbilityData::*;
match self {
ComboMelee {
@ -477,6 +499,19 @@ impl AbilityData {
)
})
},
BasicBlock {
energy,
angle,
blocked_attacks,
} => {
melee_check(25.0, *angle, None)
&& energy_check(*energy)
&& attack_kind_check(*blocked_attacks)
&& tgt_data
.char_state
.and_then(|cs| cs.stage_section())
.map_or(false, |ss| !matches!(ss, StageSection::Recover))
},
}
}
}

View File

@ -6,7 +6,7 @@ use common::{
comp::item::{Hands, ToolKind},
states::utils::{AbilityInfo, StageSection},
};
use core::f32::consts::PI;
use core::f32::consts::{PI, TAU};
pub struct ChargeswingAnimation;
@ -219,7 +219,7 @@ impl Animation for ChargeswingAnimation {
next.control.orientation.rotate_y(move2_pre * -1.6);
next.control.position += Vec3::new(0.0, 0.0, move2_pre * 4.0);
next.torso.orientation.rotate_z(move2_no_pullback * -6.28);
next.torso.orientation.rotate_z(move2_no_pullback * -TAU);
next.chest.orientation.rotate_z(move2 * -2.0);
next.head.orientation.rotate_z(move2 * 1.3);
next.belt.orientation.rotate_z(move2 * 0.6);

View File

@ -27,7 +27,7 @@ impl Animation for DashAnimation {
#[allow(clippy::single_match)] // TODO: Pending review in #587
fn update_skeleton_inner(
skeleton: &Self::Skeleton,
(_hands, ability_id, _global_time, _stage_section, _ability_info): Self::Dependency<'_>,
(_hands, _ability_id, _global_time, _stage_section, _ability_info): Self::Dependency<'_>,
_anim_time: f32,
rate: &mut f32,
_s_a: &SkeletonAttr,
@ -42,10 +42,6 @@ impl Animation for DashAnimation {
next.second.orientation = Quaternion::rotation_z(0.0);
next.off_weapon_trail = true;
match ability_id {
_ => {},
}
next
}
}

View File

@ -3,6 +3,7 @@ use super::{
CharacterSkeleton, SkeletonAttr,
};
use common::states::utils::{AbilityInfo, StageSection};
use core::f32::consts::{PI, TAU};
use std::ops::{Mul, Sub};
pub struct RapidMeleeAnimation;
@ -65,7 +66,7 @@ impl Animation for RapidMeleeAnimation {
next.hand_r.orientation = Quaternion::rotation_x(1.4);
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control.orientation =
Quaternion::rotation_x(s_a.sc.3) * Quaternion::rotation_z(move1 * 3.14);
Quaternion::rotation_x(s_a.sc.3) * Quaternion::rotation_z(move1 * PI);
if move2 < f32::EPSILON {
next.main_weapon_trail = false;
@ -83,7 +84,7 @@ impl Animation for RapidMeleeAnimation {
next.control.orientation.rotate_y(move2_pre * -1.6);
next.control.position += Vec3::new(0.0, 0.0, move2_pre * 4.0);
next.torso.orientation.rotate_z(move2_no_pullback * 6.28);
next.torso.orientation.rotate_z(move2_no_pullback * TAU);
next.chest.orientation.rotate_z(move2 * -2.0);
next.head.orientation.rotate_z(move2 * 1.3);
next.belt.orientation.rotate_z(move2 * 0.6);