mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Advanced AI for every stance.
This commit is contained in:
parent
9ea70d71e5
commit
34168d9c76
@ -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)),
|
||||
)),
|
||||
),
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user