Unskilled sword AI

This commit is contained in:
Sam 2023-01-21 23:50:18 -05:00
parent 387ea16598
commit c4154b0160
3 changed files with 167 additions and 71 deletions

View File

@ -517,61 +517,61 @@ impl<'a> AgentData<'a> {
tactics.push(tactic); tactics.push(tactic);
} }
}; };
try_tactic( // try_tactic(
SwordSkill::HeavyFortitude, // SwordSkill::HeavyFortitude,
SwordTactics::HeavyAdvanced, // SwordTactics::HeavyAdvanced,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::AgileDancingEdge, // SwordSkill::AgileDancingEdge,
SwordTactics::AgileAdvanced, // SwordTactics::AgileAdvanced,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::DefensiveStalwartSword, // SwordSkill::DefensiveStalwartSword,
SwordTactics::DefensiveAdvanced, // SwordTactics::DefensiveAdvanced,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::CripplingEviscerate, // SwordSkill::CripplingEviscerate,
SwordTactics::CripplingAdvanced, // SwordTactics::CripplingAdvanced,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::CleavingBladeFever, // SwordSkill::CleavingBladeFever,
SwordTactics::CleavingAdvanced, // SwordTactics::CleavingAdvanced,
&mut tactics, // &mut tactics,
); // );
if tactics.is_empty() { // if tactics.is_empty() {
try_tactic( // try_tactic(
SwordSkill::HeavyWindmillSlash, // SwordSkill::HeavyWindmillSlash,
SwordTactics::HeavySimple, // SwordTactics::HeavySimple,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::AgileQuickDraw, // SwordSkill::AgileQuickDraw,
SwordTactics::AgileSimple, // SwordTactics::AgileSimple,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::DefensiveDisengage, // SwordSkill::DefensiveDisengage,
SwordTactics::DefensiveSimple, // SwordTactics::DefensiveSimple,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::CripplingGouge, // SwordSkill::CripplingGouge,
SwordTactics::CripplingSimple, // SwordTactics::CripplingSimple,
&mut tactics, // &mut tactics,
); // );
try_tactic( // try_tactic(
SwordSkill::CleavingWhirlwindSlice, // SwordSkill::CleavingWhirlwindSlice,
SwordTactics::CleavingSimple, // SwordTactics::CleavingSimple,
&mut tactics, // &mut tactics,
); // );
} // }
if tactics.is_empty() { // if tactics.is_empty() {
try_tactic(SwordSkill::CrescentSlash, SwordTactics::Basic, &mut tactics); // try_tactic(SwordSkill::CrescentSlash, SwordTactics::Basic, &mut tactics);
} // }
if tactics.is_empty() { if tactics.is_empty() {
tactics.push(SwordTactics::Unskilled); tactics.push(SwordTactics::Unskilled);
} }
@ -753,10 +753,10 @@ impl<'a> AgentData<'a> {
if let Some(health) = self.health { if let Some(health) = self.health {
agent.action_state.int_counters[IntCounters::ActionMode as usize] = agent.action_state.int_counters[IntCounters::ActionMode as usize] =
if health.fraction() < 0.25 { if health.fraction() < 0.2 {
agent.action_state.positions[Positions::GuardedCover as usize] = None; agent.action_state.positions[Positions::GuardedCover as usize] = None;
ActionMode::Fleeing as u8 ActionMode::Fleeing as u8
} else if health.fraction() < 0.75 { } else if health.fraction() < 0.9 {
agent.action_state.positions[Positions::Flee as usize] = None; agent.action_state.positions[Positions::Flee as usize] = None;
ActionMode::Guarded as u8 ActionMode::Guarded as u8
} else { } else {
@ -769,6 +769,7 @@ impl<'a> AgentData<'a> {
enum IntCounters { enum IntCounters {
Tactics = 0, Tactics = 0,
ActionMode = 1, ActionMode = 1,
NextInput = 2,
} }
enum Timers { enum Timers {
@ -779,6 +780,7 @@ impl<'a> AgentData<'a> {
enum Conditions { enum Conditions {
GuardedAttack = 0, GuardedAttack = 0,
RollingBreakThrough = 1, RollingBreakThrough = 1,
TacticMisc = 2,
} }
enum FloatCounters { enum FloatCounters {
@ -990,7 +992,7 @@ impl<'a> AgentData<'a> {
- 1.0; - 1.0;
agent.action_state.conditions agent.action_state.conditions
[Conditions::RollingBreakThrough as usize] = true; [Conditions::RollingBreakThrough as usize] = true;
Some(self.pos.0 - rand_dir * actual_dist) Some(self.pos.0 - rand_dir * dist)
} else { } else {
Some(self.pos.0 + rand_dir * actual_dist) Some(self.pos.0 + rand_dir * actual_dist)
} }
@ -1027,23 +1029,85 @@ impl<'a> AgentData<'a> {
extract_ability(AbilityInput::Auxiliary(3)), extract_ability(AbilityInput::Auxiliary(3)),
extract_ability(AbilityInput::Auxiliary(4)), 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| match input {
InputKind::Primary => primary.map_or(false, |p| p.could_use(attack_data, self)),
InputKind::Secondary => secondary.map_or(false, |s| s.could_use(attack_data, self)),
InputKind::Ability(x) => abilities[x]
.as_ref()
.map_or(false, |a| a.could_use(attack_data, self)),
_ => false,
};
match SwordTactics::from_u8( match SwordTactics::from_u8(
agent.action_state.int_counters[IntCounters::Tactics as usize], agent.action_state.int_counters[IntCounters::Tactics as usize],
) { ) {
SwordTactics::Unskilled => {}, SwordTactics::Unskilled => {
SwordTactics::Basic => {}, let current_input = self.char_state.ability_info().map(|ai| ai.input);
SwordTactics::HeavySimple => {}, let mut next_input = None;
SwordTactics::AgileSimple => {}, if let Some(input) = current_input {
SwordTactics::DefensiveSimple => {}, if let input = InputKind::Secondary {
SwordTactics::CripplingSimple => {}, let charging = matches!(
SwordTactics::CleavingSimple => {}, self.char_state.stage_section(),
SwordTactics::HeavyAdvanced => {}, Some(StageSection::Charge)
SwordTactics::AgileAdvanced => {}, );
SwordTactics::DefensiveAdvanced => {}, let charged = self
SwordTactics::CripplingAdvanced => {}, .char_state
SwordTactics::CleavingAdvanced => {}, .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 {
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) {
controller.push_basic_input(input);
false
} else {
true
}
} else {
true
}
},
SwordTactics::Basic => true,
SwordTactics::HeavySimple => true,
SwordTactics::AgileSimple => true,
SwordTactics::DefensiveSimple => true,
SwordTactics::CripplingSimple => true,
SwordTactics::CleavingSimple => true,
SwordTactics::HeavyAdvanced => true,
SwordTactics::AgileAdvanced => true,
SwordTactics::DefensiveAdvanced => true,
SwordTactics::CripplingAdvanced => true,
SwordTactics::CleavingAdvanced => true,
} }
true
} else { } else {
false false
}; };

View File

@ -230,6 +230,13 @@ pub enum AbilityData {
strikes: u32, strikes: u32,
combo: u32, combo: u32,
}, },
ChargedMelee {
range: f32,
angle: f32,
initial_energy: f32,
energy_drain: f32,
charge_dur: f32,
},
} }
impl AbilityData { impl AbilityData {
@ -310,6 +317,19 @@ impl AbilityData {
strikes: max_strikes.unwrap_or(100), strikes: max_strikes.unwrap_or(100),
combo: *minimum_combo, combo: *minimum_combo,
}, },
ChargedMelee {
energy_cost,
energy_drain,
charge_duration,
melee_constructor,
..
} => Self::ChargedMelee {
initial_energy: *energy_cost,
energy_drain: *energy_drain,
charge_dur: *charge_duration,
range: melee_constructor.range,
angle: melee_constructor.angle,
},
_ => return None, _ => return None,
}; };
Some(inner) Some(inner)
@ -379,6 +399,16 @@ impl AbilityData {
&& energy_check(*energy_per_strike * *strikes as f32) && energy_check(*energy_per_strike * *strikes as f32)
&& combo_check(*combo) && combo_check(*combo)
}, },
ChargedMelee {
range,
angle,
initial_energy,
energy_drain,
charge_dur,
} => {
melee_check(*range, *angle)
&& energy_check(*initial_energy + *energy_drain * *charge_dur)
},
} }
} }
} }

View File

@ -1,3 +1,5 @@
#![feature(exclusive_range_pattern)]
#[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))] #[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))]
compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once"); compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once");