mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
minor range tweaks; body_dist and macro refactorings of harvester attack code
This commit is contained in:
parent
0fd5548346
commit
4300dd11f4
@ -4,7 +4,7 @@ BasicRanged(
|
||||
recover_duration: 1.6,
|
||||
projectile: (
|
||||
kind: Explosive(
|
||||
radius: 7.5,
|
||||
radius: 7.2,
|
||||
min_falloff: 0.6,
|
||||
reagent: Some(Red),
|
||||
terrain: Some((5, Black)),
|
||||
|
@ -11,7 +11,7 @@ BasicMelee(
|
||||
knockback: 10.0,
|
||||
energy_regen: 0.0,
|
||||
),
|
||||
range: 4.0,
|
||||
range: 4.5,
|
||||
angle: 60.0,
|
||||
multi_target: Some(Normal),
|
||||
),
|
||||
|
@ -1204,9 +1204,13 @@ impl<'a> AgentData<'a> {
|
||||
// Wield the weapon as running towards the target
|
||||
controller.push_action(ControlAction::Wield);
|
||||
|
||||
let min_attack_dist = (self.body.map_or(0.5, |b| b.max_radius()) + DEFAULT_ATTACK_RANGE)
|
||||
* self.scale
|
||||
+ tgt_data.body.map_or(0.5, |b| b.max_radius()) * tgt_data.scale.map_or(1.0, |s| s.0);
|
||||
let self_radius = self.body.map_or(0.5, |b| b.max_radius()) * self.scale;
|
||||
let self_attack_range =
|
||||
(self.body.map_or(0.5, |b| b.max_radius()) + DEFAULT_ATTACK_RANGE) * self.scale;
|
||||
let tgt_radius =
|
||||
tgt_data.body.map_or(0.5, |b| b.max_radius()) * tgt_data.scale.map_or(1.0, |s| s.0);
|
||||
let min_attack_dist = self_attack_range + tgt_radius;
|
||||
let body_dist = self_radius + tgt_radius;
|
||||
let dist_sqrd = self.pos.0.distance_squared(tgt_data.pos.0);
|
||||
let angle = self
|
||||
.ori
|
||||
@ -1358,6 +1362,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
|
||||
let attack_data = AttackData {
|
||||
body_dist,
|
||||
min_attack_dist,
|
||||
dist_sqrd,
|
||||
angle,
|
||||
|
@ -4758,40 +4758,34 @@ impl<'a> AgentData<'a> {
|
||||
read_data: &ReadData,
|
||||
rng: &mut impl Rng,
|
||||
) {
|
||||
// --- setup ---
|
||||
|
||||
// behaviour parameters
|
||||
const FIRST_VINE_CREATION_THRESHOLD: f32 = 0.60;
|
||||
const SECOND_VINE_CREATION_THRESHOLD: f32 = 0.30;
|
||||
const MELEE_RANGE: f32 = 5.0; // hard coded from scythe attack
|
||||
const MAX_PUMPKIN_RANGE: f32 = 50.0;
|
||||
const FIRE_BREATH_RANGE: f32 = 20.0;
|
||||
const FIRE_BREATH_TIME: f32 = 4.0;
|
||||
const FIRE_BREATH_SHORT_TIME: f32 = 2.0;
|
||||
const FIRE_BREATH_COOLDOWN: f32 = 3.0;
|
||||
const CLOSE_MIXUP_COOLDOWN: f32 = 6.0;
|
||||
const FIREBREATH_RANGE: f32 = 20.0; // hard coded from firebreath attack
|
||||
const FIREBREATH_TIME: f32 = 4.0;
|
||||
const FIREBREATH_SHORT_TIME: f32 = 2.5; // cutoff sooner at close range
|
||||
const FIREBREATH_COOLDOWN: f32 = 3.0;
|
||||
const CLOSE_MIXUP_COOLDOWN: f32 = 5.0; // variation in attacks at close range
|
||||
const FAR_PUMPKIN_COOLDOWN: f32 = 1.0; // allows for pathing to player between throws
|
||||
|
||||
// conditions
|
||||
enum ActionStateConditions {
|
||||
ConditionHasSummonedFirstVines = 0,
|
||||
ConditionHasSummonedSecondVines,
|
||||
HasSummonedFirstVines = 0,
|
||||
HasSummonedSecondVines,
|
||||
}
|
||||
|
||||
// timers
|
||||
enum ActionStateTimers {
|
||||
TimerFire = 0,
|
||||
TimerCloseMixup,
|
||||
SinceFirebreath = 0,
|
||||
SinceCloseMixup,
|
||||
SinceFarPumpkin,
|
||||
}
|
||||
|
||||
match self.char_state {
|
||||
CharacterState::BasicBeam(_) => {
|
||||
agent.combat_state.timers[ActionStateTimers::TimerFire as usize] = 0.0;
|
||||
},
|
||||
CharacterState::BasicRanged(_) => {
|
||||
agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize] = 0.0;
|
||||
},
|
||||
_ => {
|
||||
agent.combat_state.timers[ActionStateTimers::TimerFire as usize] += read_data.dt.0;
|
||||
agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize] +=
|
||||
read_data.dt.0;
|
||||
},
|
||||
}
|
||||
|
||||
let health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||
// setup line of sight check
|
||||
let line_of_sight_with_target = || {
|
||||
entities_have_line_of_sight(
|
||||
self.pos,
|
||||
@ -4804,95 +4798,159 @@ impl<'a> AgentData<'a> {
|
||||
)
|
||||
};
|
||||
|
||||
if (health_fraction < FIRST_VINE_CREATION_THRESHOLD
|
||||
&& !agent.combat_state.conditions
|
||||
[ActionStateConditions::ConditionHasSummonedFirstVines as usize])
|
||||
// --- readability macros ---
|
||||
|
||||
// actions
|
||||
macro_rules! reset_timer {
|
||||
($timer:ident) => {
|
||||
agent.combat_state.timers[ActionStateTimers::$timer as usize] = 0.0
|
||||
};
|
||||
}
|
||||
macro_rules! increment_timer {
|
||||
($timer:ident) => {
|
||||
agent.combat_state.timers[ActionStateTimers::$timer as usize] += read_data.dt.0
|
||||
};
|
||||
}
|
||||
macro_rules! use_scythe {
|
||||
() => {
|
||||
controller.push_basic_input(InputKind::Primary)
|
||||
};
|
||||
}
|
||||
macro_rules! use_fire_breath {
|
||||
() => {
|
||||
controller.push_basic_input(InputKind::Secondary)
|
||||
};
|
||||
}
|
||||
macro_rules! use_pumpkin {
|
||||
() => {
|
||||
controller.push_basic_input(InputKind::Ability(0))
|
||||
};
|
||||
}
|
||||
macro_rules! use_first_vines {
|
||||
() => {
|
||||
controller.push_basic_input(InputKind::Ability(1))
|
||||
};
|
||||
}
|
||||
macro_rules! use_second_vines {
|
||||
() => {
|
||||
controller.push_basic_input(InputKind::Ability(2))
|
||||
};
|
||||
}
|
||||
macro_rules! move_to_target {
|
||||
() => {
|
||||
self.path_toward_target(
|
||||
agent,
|
||||
controller,
|
||||
tgt_data.pos.0,
|
||||
read_data,
|
||||
Path::Partial,
|
||||
None,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// shortcuts
|
||||
macro_rules! conditions {
|
||||
($condition:ident) => {
|
||||
agent.combat_state.conditions[ActionStateConditions::$condition as usize]
|
||||
};
|
||||
}
|
||||
macro_rules! timers {
|
||||
($timer:ident) => {
|
||||
agent.combat_state.timers[ActionStateTimers::$timer as usize]
|
||||
};
|
||||
}
|
||||
macro_rules! is_in_vines_recovery {
|
||||
() => (
|
||||
matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
)
|
||||
}
|
||||
macro_rules! is_using_firebreath {
|
||||
// currently using firebreath and under time limit
|
||||
($time_limit:ident) => (
|
||||
matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs_f32($time_limit))
|
||||
)
|
||||
}
|
||||
|
||||
// --- main ---
|
||||
|
||||
// handle timers
|
||||
match self.char_state {
|
||||
CharacterState::BasicBeam(_) => {
|
||||
reset_timer!(SinceFirebreath);
|
||||
},
|
||||
CharacterState::BasicRanged(_) => {
|
||||
reset_timer!(SinceCloseMixup);
|
||||
reset_timer!(SinceFarPumpkin);
|
||||
},
|
||||
_ => {
|
||||
increment_timer!(SinceFirebreath);
|
||||
increment_timer!(SinceCloseMixup);
|
||||
increment_timer!(SinceFarPumpkin);
|
||||
},
|
||||
}
|
||||
|
||||
// vine summoning
|
||||
let health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||
|
||||
if (health_fraction < FIRST_VINE_CREATION_THRESHOLD && !conditions!(HasSummonedFirstVines))
|
||||
|| (health_fraction < SECOND_VINE_CREATION_THRESHOLD
|
||||
&& !agent.combat_state.conditions
|
||||
[ActionStateConditions::ConditionHasSummonedSecondVines as usize])
|
||||
&& !conditions!(HasSummonedSecondVines))
|
||||
{
|
||||
if health_fraction < SECOND_VINE_CREATION_THRESHOLD {
|
||||
// Summon second vines when reach threshold of health
|
||||
controller.push_basic_input(InputKind::Ability(2));
|
||||
|
||||
if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
agent.combat_state.conditions
|
||||
[ActionStateConditions::ConditionHasSummonedSecondVines as usize] = true;
|
||||
use_second_vines!();
|
||||
if is_in_vines_recovery!() {
|
||||
conditions!(HasSummonedSecondVines) = true;
|
||||
}
|
||||
} else {
|
||||
// Summon first vines when reach threshold of health
|
||||
controller.push_basic_input(InputKind::Ability(1));
|
||||
|
||||
if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
agent.combat_state.conditions
|
||||
[ActionStateConditions::ConditionHasSummonedFirstVines as usize] = true;
|
||||
use_first_vines!();
|
||||
if is_in_vines_recovery!() {
|
||||
conditions!(HasSummonedFirstVines) = true;
|
||||
}
|
||||
}
|
||||
} else if attack_data.in_min_range() {
|
||||
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs_f32(FIRE_BREATH_SHORT_TIME))
|
||||
}
|
||||
// close range
|
||||
else if attack_data.dist_sqrd < (attack_data.body_dist + 0.75 * MELEE_RANGE).powi(2) {
|
||||
if is_using_firebreath!(FIREBREATH_SHORT_TIME) {
|
||||
use_fire_breath!()
|
||||
} else if timers!(SinceCloseMixup) > CLOSE_MIXUP_COOLDOWN
|
||||
// for now, no line of sight check for consitency in attacks
|
||||
{
|
||||
// Keep breathing fire if close enough, can see target, and have not been
|
||||
// breathing for more than short limit
|
||||
controller.push_basic_input(InputKind::Secondary);
|
||||
} else if agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize]
|
||||
> CLOSE_MIXUP_COOLDOWN
|
||||
&& line_of_sight_with_target()
|
||||
{
|
||||
if agent.combat_state.timers[ActionStateTimers::TimerFire as usize]
|
||||
< FIRE_BREATH_COOLDOWN
|
||||
{
|
||||
// Throw a close range pumpkin to knock back player
|
||||
controller.push_basic_input(InputKind::Ability(0));
|
||||
if timers!(SinceFirebreath) < FIREBREATH_COOLDOWN {
|
||||
use_pumpkin!();
|
||||
} else {
|
||||
let randomise = rng.gen_range(1..=3);
|
||||
let randomise = rng.gen_range(1..=2);
|
||||
match randomise {
|
||||
1 => controller.push_basic_input(InputKind::Secondary), /* start fire
|
||||
* breath */
|
||||
_ => controller.push_basic_input(InputKind::Ability(0)), /* close range
|
||||
* pumpkin
|
||||
* _ => controller.push_basic_input(InputKind::Primary), // scythe */
|
||||
1 => use_fire_breath!(),
|
||||
_ => use_pumpkin!(),
|
||||
}
|
||||
}
|
||||
} else if attack_data.angle < 60.0 {
|
||||
// Scythe them if they're in range and angle
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
use_scythe!();
|
||||
}
|
||||
} else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) {
|
||||
if line_of_sight_with_target() {
|
||||
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs_f32(FIRE_BREATH_TIME))
|
||||
{
|
||||
// Keep breathing fire if close enough, can see target, and have not been
|
||||
// breathing for more than upper limit
|
||||
controller.push_basic_input(InputKind::Secondary);
|
||||
} else if attack_data.angle < 30.0
|
||||
&& agent.combat_state.timers[ActionStateTimers::TimerFire as usize]
|
||||
> FIRE_BREATH_COOLDOWN
|
||||
{
|
||||
// Start breathing fire at them if close enough, in angle, and can see target
|
||||
controller.push_basic_input(InputKind::Secondary);
|
||||
} else if agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize]
|
||||
> CLOSE_MIXUP_COOLDOWN
|
||||
{
|
||||
// Throw a close range pumpkin to knock back player
|
||||
controller.push_basic_input(InputKind::Ability(0));
|
||||
}
|
||||
}
|
||||
// mid range (with line of sight)
|
||||
else if attack_data.dist_sqrd < FIREBREATH_RANGE.powi(2) && line_of_sight_with_target() {
|
||||
if is_using_firebreath!(FIREBREATH_TIME) {
|
||||
use_fire_breath!()
|
||||
} else if attack_data.angle < 30.0 && timers!(SinceFirebreath) > FIREBREATH_COOLDOWN {
|
||||
use_fire_breath!()
|
||||
} else if timers!(SinceCloseMixup) > CLOSE_MIXUP_COOLDOWN {
|
||||
use_pumpkin!();
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2) && line_of_sight_with_target() {
|
||||
// Throw a pumpkin at them if close enough and can see them
|
||||
controller.push_basic_input(InputKind::Ability(0));
|
||||
}
|
||||
// long range (with line of sight)
|
||||
else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2)
|
||||
&& line_of_sight_with_target()
|
||||
&& timers!(SinceFarPumpkin) > FAR_PUMPKIN_COOLDOWN
|
||||
{
|
||||
use_pumpkin!();
|
||||
}
|
||||
|
||||
// Always attempt to path towards target
|
||||
self.path_toward_target(
|
||||
agent,
|
||||
controller,
|
||||
tgt_data.pos.0,
|
||||
read_data,
|
||||
Path::Partial,
|
||||
None,
|
||||
);
|
||||
// closing gap
|
||||
if !(attack_data.dist_sqrd < (attack_data.body_dist + 0.4 * MELEE_RANGE).powi(2)) {
|
||||
move_to_target!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_frostgigas_attack(
|
||||
|
@ -145,6 +145,7 @@ impl<'a> TargetData<'a> {
|
||||
}
|
||||
|
||||
pub struct AttackData {
|
||||
pub body_dist: f32,
|
||||
pub min_attack_dist: f32,
|
||||
pub dist_sqrd: f32,
|
||||
pub angle: f32,
|
||||
|
Loading…
Reference in New Issue
Block a user