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,
|
recover_duration: 1.6,
|
||||||
projectile: (
|
projectile: (
|
||||||
kind: Explosive(
|
kind: Explosive(
|
||||||
radius: 7.5,
|
radius: 7.2,
|
||||||
min_falloff: 0.6,
|
min_falloff: 0.6,
|
||||||
reagent: Some(Red),
|
reagent: Some(Red),
|
||||||
terrain: Some((5, Black)),
|
terrain: Some((5, Black)),
|
||||||
|
@ -11,7 +11,7 @@ BasicMelee(
|
|||||||
knockback: 10.0,
|
knockback: 10.0,
|
||||||
energy_regen: 0.0,
|
energy_regen: 0.0,
|
||||||
),
|
),
|
||||||
range: 4.0,
|
range: 4.5,
|
||||||
angle: 60.0,
|
angle: 60.0,
|
||||||
multi_target: Some(Normal),
|
multi_target: Some(Normal),
|
||||||
),
|
),
|
||||||
|
@ -1204,9 +1204,13 @@ impl<'a> AgentData<'a> {
|
|||||||
// Wield the weapon as running towards the target
|
// Wield the weapon as running towards the target
|
||||||
controller.push_action(ControlAction::Wield);
|
controller.push_action(ControlAction::Wield);
|
||||||
|
|
||||||
let min_attack_dist = (self.body.map_or(0.5, |b| b.max_radius()) + DEFAULT_ATTACK_RANGE)
|
let self_radius = self.body.map_or(0.5, |b| b.max_radius()) * self.scale;
|
||||||
* self.scale
|
let self_attack_range =
|
||||||
+ tgt_data.body.map_or(0.5, |b| b.max_radius()) * tgt_data.scale.map_or(1.0, |s| s.0);
|
(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 dist_sqrd = self.pos.0.distance_squared(tgt_data.pos.0);
|
||||||
let angle = self
|
let angle = self
|
||||||
.ori
|
.ori
|
||||||
@ -1358,6 +1362,7 @@ impl<'a> AgentData<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let attack_data = AttackData {
|
let attack_data = AttackData {
|
||||||
|
body_dist,
|
||||||
min_attack_dist,
|
min_attack_dist,
|
||||||
dist_sqrd,
|
dist_sqrd,
|
||||||
angle,
|
angle,
|
||||||
|
@ -4758,40 +4758,34 @@ impl<'a> AgentData<'a> {
|
|||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
) {
|
) {
|
||||||
|
// --- setup ---
|
||||||
|
|
||||||
|
// behaviour parameters
|
||||||
const FIRST_VINE_CREATION_THRESHOLD: f32 = 0.60;
|
const FIRST_VINE_CREATION_THRESHOLD: f32 = 0.60;
|
||||||
const SECOND_VINE_CREATION_THRESHOLD: f32 = 0.30;
|
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 MAX_PUMPKIN_RANGE: f32 = 50.0;
|
||||||
const FIRE_BREATH_RANGE: f32 = 20.0;
|
const FIREBREATH_RANGE: f32 = 20.0; // hard coded from firebreath attack
|
||||||
const FIRE_BREATH_TIME: f32 = 4.0;
|
const FIREBREATH_TIME: f32 = 4.0;
|
||||||
const FIRE_BREATH_SHORT_TIME: f32 = 2.0;
|
const FIREBREATH_SHORT_TIME: f32 = 2.5; // cutoff sooner at close range
|
||||||
const FIRE_BREATH_COOLDOWN: f32 = 3.0;
|
const FIREBREATH_COOLDOWN: f32 = 3.0;
|
||||||
const CLOSE_MIXUP_COOLDOWN: f32 = 6.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 {
|
enum ActionStateConditions {
|
||||||
ConditionHasSummonedFirstVines = 0,
|
HasSummonedFirstVines = 0,
|
||||||
ConditionHasSummonedSecondVines,
|
HasSummonedSecondVines,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// timers
|
||||||
enum ActionStateTimers {
|
enum ActionStateTimers {
|
||||||
TimerFire = 0,
|
SinceFirebreath = 0,
|
||||||
TimerCloseMixup,
|
SinceCloseMixup,
|
||||||
|
SinceFarPumpkin,
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.char_state {
|
// setup line of sight check
|
||||||
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());
|
|
||||||
let line_of_sight_with_target = || {
|
let line_of_sight_with_target = || {
|
||||||
entities_have_line_of_sight(
|
entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
@ -4804,95 +4798,159 @@ impl<'a> AgentData<'a> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (health_fraction < FIRST_VINE_CREATION_THRESHOLD
|
// --- readability macros ---
|
||||||
&& !agent.combat_state.conditions
|
|
||||||
[ActionStateConditions::ConditionHasSummonedFirstVines as usize])
|
// 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
|
|| (health_fraction < SECOND_VINE_CREATION_THRESHOLD
|
||||||
&& !agent.combat_state.conditions
|
&& !conditions!(HasSummonedSecondVines))
|
||||||
[ActionStateConditions::ConditionHasSummonedSecondVines as usize])
|
|
||||||
{
|
{
|
||||||
if health_fraction < SECOND_VINE_CREATION_THRESHOLD {
|
if health_fraction < SECOND_VINE_CREATION_THRESHOLD {
|
||||||
// Summon second vines when reach threshold of health
|
use_second_vines!();
|
||||||
controller.push_basic_input(InputKind::Ability(2));
|
if is_in_vines_recovery!() {
|
||||||
|
conditions!(HasSummonedSecondVines) = true;
|
||||||
if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
|
|
||||||
{
|
|
||||||
agent.combat_state.conditions
|
|
||||||
[ActionStateConditions::ConditionHasSummonedSecondVines as usize] = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Summon first vines when reach threshold of health
|
use_first_vines!();
|
||||||
controller.push_basic_input(InputKind::Ability(1));
|
if is_in_vines_recovery!() {
|
||||||
|
conditions!(HasSummonedFirstVines) = true;
|
||||||
if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
|
|
||||||
{
|
|
||||||
agent.combat_state.conditions
|
|
||||||
[ActionStateConditions::ConditionHasSummonedFirstVines as usize] = 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
|
if timers!(SinceFirebreath) < FIREBREATH_COOLDOWN {
|
||||||
// breathing for more than short limit
|
use_pumpkin!();
|
||||||
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));
|
|
||||||
} else {
|
} else {
|
||||||
let randomise = rng.gen_range(1..=3);
|
let randomise = rng.gen_range(1..=2);
|
||||||
match randomise {
|
match randomise {
|
||||||
1 => controller.push_basic_input(InputKind::Secondary), /* start fire
|
1 => use_fire_breath!(),
|
||||||
* breath */
|
_ => use_pumpkin!(),
|
||||||
_ => controller.push_basic_input(InputKind::Ability(0)), /* close range
|
|
||||||
* pumpkin
|
|
||||||
* _ => controller.push_basic_input(InputKind::Primary), // scythe */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if attack_data.angle < 60.0 {
|
} else if attack_data.angle < 60.0 {
|
||||||
// Scythe them if they're in range and angle
|
use_scythe!();
|
||||||
controller.push_basic_input(InputKind::Primary);
|
|
||||||
}
|
}
|
||||||
} else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) {
|
}
|
||||||
if line_of_sight_with_target() {
|
// mid range (with line of sight)
|
||||||
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs_f32(FIRE_BREATH_TIME))
|
else if attack_data.dist_sqrd < FIREBREATH_RANGE.powi(2) && line_of_sight_with_target() {
|
||||||
{
|
if is_using_firebreath!(FIREBREATH_TIME) {
|
||||||
// Keep breathing fire if close enough, can see target, and have not been
|
use_fire_breath!()
|
||||||
// breathing for more than upper limit
|
} else if attack_data.angle < 30.0 && timers!(SinceFirebreath) > FIREBREATH_COOLDOWN {
|
||||||
controller.push_basic_input(InputKind::Secondary);
|
use_fire_breath!()
|
||||||
} else if attack_data.angle < 30.0
|
} else if timers!(SinceCloseMixup) > CLOSE_MIXUP_COOLDOWN {
|
||||||
&& agent.combat_state.timers[ActionStateTimers::TimerFire as usize]
|
use_pumpkin!();
|
||||||
> 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} 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
|
// long range (with line of sight)
|
||||||
controller.push_basic_input(InputKind::Ability(0));
|
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
|
// closing gap
|
||||||
self.path_toward_target(
|
if !(attack_data.dist_sqrd < (attack_data.body_dist + 0.4 * MELEE_RANGE).powi(2)) {
|
||||||
agent,
|
move_to_target!();
|
||||||
controller,
|
}
|
||||||
tgt_data.pos.0,
|
|
||||||
read_data,
|
|
||||||
Path::Partial,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_frostgigas_attack(
|
pub fn handle_frostgigas_attack(
|
||||||
|
@ -145,6 +145,7 @@ impl<'a> TargetData<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AttackData {
|
pub struct AttackData {
|
||||||
|
pub body_dist: f32,
|
||||||
pub min_attack_dist: f32,
|
pub min_attack_dist: f32,
|
||||||
pub dist_sqrd: f32,
|
pub dist_sqrd: f32,
|
||||||
pub angle: f32,
|
pub angle: f32,
|
||||||
|
Loading…
Reference in New Issue
Block a user