mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Addressed review comments
This commit is contained in:
parent
a44c36432e
commit
b5682c4682
@ -2123,7 +2123,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
// If ability is a stance, enter the stance without beginning a strike, otherwise
|
||||
// immediately begin the strike
|
||||
stage_section: if *is_stance {
|
||||
Some(StageSection::Charge)
|
||||
Some(StageSection::Ready)
|
||||
} else {
|
||||
Some(StageSection::Buildup)
|
||||
},
|
||||
@ -2611,7 +2611,7 @@ bitflags::bitflags! {
|
||||
const ROLL_INTERRUPT = 0b00000001;
|
||||
// Allows blocking to interrupt the ability at any point
|
||||
const BLOCK_INTERRUPT = 0b00000010;
|
||||
// When the ability is in the buyildup section, it counts as a parry
|
||||
// When the ability is in the buildup section, it counts as a parry
|
||||
const BUILDUP_PARRIES = 0b00000100;
|
||||
// When in the ability, an entity only receives half as much poise damage
|
||||
const POISE_RESISTANT = 0b00001000;
|
||||
|
@ -647,6 +647,10 @@ impl CharacterState {
|
||||
buildup: Some(strike.buildup_duration),
|
||||
action: Some(strike.swing_duration),
|
||||
recover: Some(strike.recover_duration),
|
||||
ready: data
|
||||
.static_data
|
||||
.is_stance
|
||||
.then_some(combo_melee2::STANCE_ENTER_TIME),
|
||||
..Default::default()
|
||||
})
|
||||
},
|
||||
@ -847,6 +851,7 @@ pub struct DurationsInfo {
|
||||
pub recover: Option<Duration>,
|
||||
pub movement: Option<Duration>,
|
||||
pub charge: Option<Duration>,
|
||||
pub ready: Option<Duration>,
|
||||
}
|
||||
|
||||
impl Default for CharacterState {
|
||||
|
@ -337,8 +337,7 @@ pub enum AbilityContext {
|
||||
}
|
||||
|
||||
impl AbilityContext {
|
||||
// TODO: Come up with better name for function
|
||||
pub fn yeet(char_state: Option<&CharacterState>) -> Option<Self> {
|
||||
pub fn try_from(char_state: Option<&CharacterState>) -> Option<Self> {
|
||||
if let Some(AbilityKind::Sword(stance)) = char_state
|
||||
.and_then(|cs| cs.ability_info())
|
||||
.and_then(|info| info.ability_meta)
|
||||
|
@ -27,6 +27,10 @@ pub struct Melee {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum MultiTarget {
|
||||
Normal,
|
||||
/// Applies scaling to the power of the attack based on how many consecutive
|
||||
/// enemies have been hit. First enemy hit will be at a power of 1.0, second
|
||||
/// enemy hit will be at a power of `1.0 + scaling`, nth enemy hit will be
|
||||
/// at a power of `1.0 + (n - 1) * scaling`.
|
||||
Scaling(f32),
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
character_state::OutputEvents, tool::ToolKind, CharacterState, Melee, MeleeConstructor,
|
||||
character_state::OutputEvents, tool::ToolKind, CharacterState, MeleeConstructor,
|
||||
StateUpdate,
|
||||
},
|
||||
states::{
|
||||
@ -121,15 +121,13 @@ impl CharacterBehavior for Data {
|
||||
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
|
||||
reset_state(self, data, output_events, &mut update);
|
||||
} else {
|
||||
end_ability(data, &mut update);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate},
|
||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||
event::LocalEvent,
|
||||
outcome::Outcome,
|
||||
states::{
|
||||
@ -156,16 +156,12 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -344,15 +344,13 @@ impl CharacterBehavior for Data {
|
||||
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
|
||||
reset_state(self, data, output_events, &mut update);
|
||||
} else {
|
||||
end_ability(data, &mut update);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
character_state::OutputEvents, tool::Stats, CharacterState, InputKind, Melee,
|
||||
character_state::OutputEvents, tool::Stats, CharacterState, InputKind,
|
||||
MeleeConstructor, StateUpdate, InputAttr, InventoryAction, slot::{Slot, EquipSlot},
|
||||
},
|
||||
states::{
|
||||
@ -132,7 +132,7 @@ impl CharacterBehavior for Data {
|
||||
let strike_data = self.strike_data();
|
||||
|
||||
match self.stage_section {
|
||||
Some(StageSection::Charge) => {
|
||||
Some(StageSection::Ready) => {
|
||||
// Adds a small duration to entering a stance to discourage spam swapping stances for ability activation benefits of matching stance
|
||||
if self.timer < STANCE_ENTER_TIME {
|
||||
if let CharacterState::ComboMelee2(c) = &mut update.character {
|
||||
@ -225,17 +225,13 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
} else {
|
||||
// Return to wielding
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(_) => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
None => {
|
||||
if self.timer < STANCE_LEAVE_TIME {
|
||||
@ -244,9 +240,7 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure melee component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
|
||||
handle_climb(data, &mut update);
|
||||
@ -273,7 +267,7 @@ impl CharacterBehavior for Data {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
if matches!(data.character, CharacterState::ComboMelee2(data) if data.static_data.ability_info.input == Some(input) && input != InputKind::Primary && data.stage_section.is_none()) {
|
||||
end_ability(data, &mut update);
|
||||
end_melee_ability(data, &mut update);
|
||||
} else {
|
||||
update.queued_inputs.insert(input, InputAttr {
|
||||
select_pos,
|
||||
|
@ -247,16 +247,12 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate},
|
||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::{StageSection, *},
|
||||
@ -112,16 +112,12 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
combat::{CombatBuff, CombatEffect},
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate},
|
||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||
event::ServerEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
@ -131,16 +131,12 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate},
|
||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::{StageSection, *},
|
||||
@ -139,16 +139,12 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, StateUpdate},
|
||||
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
@ -66,8 +66,6 @@ impl CharacterBehavior for Data {
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate},
|
||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
@ -113,16 +113,12 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate},
|
||||
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
@ -94,16 +94,12 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -151,16 +151,12 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
end_ability(data, &mut update);
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
end_melee_ability(data, &mut update);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
quadruped_low, quadruped_medium, quadruped_small,
|
||||
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
|
||||
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
||||
InventoryAction, StateUpdate,
|
||||
InventoryAction, StateUpdate, Melee,
|
||||
},
|
||||
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
@ -975,7 +975,7 @@ pub fn handle_jump(
|
||||
}
|
||||
|
||||
fn handle_ability(data: &JoinData<'_>, update: &mut StateUpdate, input: InputKind) -> bool {
|
||||
let context = AbilityContext::yeet(Some(data.character));
|
||||
let context = AbilityContext::try_from(Some(data.character));
|
||||
if let Some(ability_input) = input.into() {
|
||||
if let Some((ability, from_offhand)) = data
|
||||
.active_abilities
|
||||
@ -1259,6 +1259,7 @@ pub enum StageSection {
|
||||
Charge,
|
||||
Movement,
|
||||
Action,
|
||||
Ready,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -1387,6 +1388,11 @@ pub fn end_ability(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_melee_ability(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||
end_ability(data, update);
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum HandInfo {
|
||||
TwoHanded,
|
||||
|
@ -1,8 +1,4 @@
|
||||
use crate::{
|
||||
consts::MAX_PATH_DIST,
|
||||
data::{AgentData, AttackData, Path, ReadData, TargetData},
|
||||
util::entities_have_line_of_sight,
|
||||
};
|
||||
use crate::{consts::MAX_PATH_DIST, data::*, util::entities_have_line_of_sight};
|
||||
use common::{
|
||||
comp::{
|
||||
ability::{self, Ability, AbilityKind, ActiveAbilities, AuxiliaryAbility, Capability},
|
||||
@ -18,7 +14,7 @@ use common::{
|
||||
util::Dir,
|
||||
vol::ReadVol,
|
||||
};
|
||||
use rand::Rng;
|
||||
use rand::{prelude::SliceRandom, Rng};
|
||||
use specs::saveload::MarkerAllocator;
|
||||
use std::{f32::consts::PI, time::Duration};
|
||||
use vek::*;
|
||||
@ -422,145 +418,16 @@ impl<'a> AgentData<'a> {
|
||||
read_data: &ReadData,
|
||||
rng: &mut impl Rng,
|
||||
) {
|
||||
struct ComboMeleeData {
|
||||
min_range: f32,
|
||||
max_range: f32,
|
||||
angle: f32,
|
||||
energy: f32,
|
||||
}
|
||||
impl ComboMeleeData {
|
||||
fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.max_range.powi(2)
|
||||
&& attack_data.dist_sqrd > self.min_range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() >= self.energy
|
||||
}
|
||||
}
|
||||
struct FinisherMeleeData {
|
||||
range: f32,
|
||||
angle: f32,
|
||||
energy: f32,
|
||||
combo: u32,
|
||||
}
|
||||
impl FinisherMeleeData {
|
||||
fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() >= self.energy
|
||||
&& agent_data
|
||||
.combo
|
||||
.map_or(false, |c| c.counter() >= self.combo)
|
||||
}
|
||||
|
||||
fn use_desirable(&self, tgt_data: &TargetData, agent_data: &AgentData) -> bool {
|
||||
let combo_factor =
|
||||
agent_data.combo.map_or(0, |c| c.counter()) as f32 / self.combo as f32 * 2.0;
|
||||
let tgt_health_factor = tgt_data.health.map_or(0.0, |h| h.current()) / 50.0;
|
||||
let self_health_factor = agent_data.health.map_or(0.0, |h| h.current()) / 50.0;
|
||||
// Use becomes more desirable if either self or target is close to death
|
||||
combo_factor > tgt_health_factor.min(self_health_factor)
|
||||
}
|
||||
}
|
||||
struct SelfBuffData {
|
||||
buff: BuffKind,
|
||||
energy: f32,
|
||||
}
|
||||
impl SelfBuffData {
|
||||
fn could_use(&self, agent_data: &AgentData) -> bool {
|
||||
agent_data.energy.current() >= self.energy
|
||||
}
|
||||
|
||||
fn use_desirable(&self, agent_data: &AgentData) -> bool {
|
||||
agent_data
|
||||
.buffs
|
||||
.map_or(false, |buffs| !buffs.contains(self.buff))
|
||||
}
|
||||
}
|
||||
struct DiveMeleeData {
|
||||
range: f32,
|
||||
angle: f32,
|
||||
energy: f32,
|
||||
}
|
||||
impl DiveMeleeData {
|
||||
fn npc_should_use_hack(&self, agent_data: &AgentData, tgt_data: &TargetData) -> bool {
|
||||
let dist_sqrd_2d = agent_data.pos.0.xy().distance_squared(tgt_data.pos.0.xy());
|
||||
agent_data.energy.current() > self.energy
|
||||
&& agent_data.physics_state.on_ground.is_some()
|
||||
&& agent_data.pos.0.z >= tgt_data.pos.0.z
|
||||
&& dist_sqrd_2d > (self.range / 2.0).powi(2)
|
||||
&& dist_sqrd_2d < (self.range + 5.0).powi(2)
|
||||
}
|
||||
}
|
||||
struct BlockData {
|
||||
angle: f32,
|
||||
// Should probably just always use 5 or so unless riposte melee
|
||||
range: f32,
|
||||
energy: f32,
|
||||
}
|
||||
impl BlockData {
|
||||
fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() >= self.energy
|
||||
}
|
||||
}
|
||||
struct DashMeleeData {
|
||||
range: f32,
|
||||
angle: f32,
|
||||
initial_energy: f32,
|
||||
energy_drain: f32,
|
||||
speed: f32,
|
||||
charge_dur: f32,
|
||||
}
|
||||
impl DashMeleeData {
|
||||
// TODO: Maybe figure out better way of pulling in base accel from body and
|
||||
// accounting for friction?
|
||||
const BASE_SPEED: f32 = 3.0;
|
||||
const ORI_RATE: f32 = 30.0;
|
||||
|
||||
fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
let charge_dur = self.charge_dur(agent_data);
|
||||
let charge_dist = charge_dur * self.speed * Self::BASE_SPEED;
|
||||
let attack_dist = charge_dist + self.range;
|
||||
let ori_gap = Self::ORI_RATE * charge_dur;
|
||||
attack_data.dist_sqrd < attack_dist.powi(2)
|
||||
&& attack_data.angle < self.angle + ori_gap
|
||||
&& agent_data.energy.current() > self.initial_energy
|
||||
}
|
||||
|
||||
fn use_desirable(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
let charge_dist = self.charge_dur(agent_data) * self.speed * Self::BASE_SPEED;
|
||||
attack_data.dist_sqrd / charge_dist.powi(2) > 0.75_f32.powi(2)
|
||||
}
|
||||
|
||||
fn charge_dur(&self, agent_data: &AgentData) -> f32 {
|
||||
((agent_data.energy.current() - self.initial_energy) / self.energy_drain)
|
||||
.clamp(0.0, self.charge_dur)
|
||||
}
|
||||
}
|
||||
struct RapidMeleeData {
|
||||
range: f32,
|
||||
angle: f32,
|
||||
energy: f32,
|
||||
strikes: u32,
|
||||
}
|
||||
impl RapidMeleeData {
|
||||
fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() > self.energy * self.strikes as f32
|
||||
}
|
||||
}
|
||||
use ability::SwordStance;
|
||||
let stance = |stance| match stance {
|
||||
1 => SwordStance::Offensive,
|
||||
2 => SwordStance::Defensive,
|
||||
3 => SwordStance::Mobility,
|
||||
4 => SwordStance::Crippling,
|
||||
5 => SwordStance::Parrying,
|
||||
6 => SwordStance::Heavy,
|
||||
7 => SwordStance::Reaching,
|
||||
8 => SwordStance::Cleaving,
|
||||
5 => SwordStance::Cleaving,
|
||||
6 => SwordStance::Parrying,
|
||||
7 => SwordStance::Heavy,
|
||||
8 => SwordStance::Reaching,
|
||||
_ => SwordStance::Balanced,
|
||||
};
|
||||
if !agent.action_state.initialized {
|
||||
@ -575,7 +442,9 @@ impl<'a> AgentData<'a> {
|
||||
// Hack to make cleaving stance come up less often because agents cannot use it
|
||||
// effectively due to only considering a single target
|
||||
// Remove when agents properly consider multiple targets
|
||||
4 + rng.gen_range(0..13) / 3
|
||||
// (stance, weight)
|
||||
let options = [(4, 3), (5, 1), (6, 3), (7, 3), (8, 3)];
|
||||
options.choose_weighted(rng, |x| x.1).map_or(0, |x| x.0)
|
||||
// rng.gen_range(4..9)
|
||||
} else if self
|
||||
.skill_set
|
||||
@ -952,23 +821,32 @@ impl<'a> AgentData<'a> {
|
||||
};
|
||||
const DESIRED_ENERGY: f32 = 50.0;
|
||||
|
||||
let mut try_roll = || {
|
||||
let mut try_roll = |controller: &mut Controller| {
|
||||
if let Some(char_state) = tgt_data.char_state {
|
||||
matches!(char_state.stage_section(), Some(StageSection::Buildup))
|
||||
&& char_state.is_melee_attack()
|
||||
&& char_state
|
||||
.timer()
|
||||
.map_or(false, |timer| rng.gen::<f32>() < timer.as_secs_f32() * 4.0)
|
||||
&& matches!(
|
||||
self.char_state.stage_section(),
|
||||
Some(StageSection::Recover)
|
||||
)
|
||||
&& self
|
||||
.char_state
|
||||
.ability_info()
|
||||
.and_then(|a| a.ability_meta)
|
||||
.map(|m| m.capabilities)
|
||||
.map_or(false, |c| c.contains(Capability::ROLL_INTERRUPT))
|
||||
let try_roll =
|
||||
matches!(char_state.stage_section(), Some(StageSection::Buildup))
|
||||
&& char_state.is_melee_attack()
|
||||
&& char_state.timer().map_or(false, |timer| {
|
||||
rng.gen::<f32>() < timer.as_secs_f32() * 4.0
|
||||
})
|
||||
&& matches!(
|
||||
self.char_state.stage_section(),
|
||||
Some(StageSection::Recover)
|
||||
)
|
||||
&& self
|
||||
.char_state
|
||||
.ability_info()
|
||||
.and_then(|a| a.ability_meta)
|
||||
.map(|m| m.capabilities)
|
||||
.map_or(false, |c| c.contains(Capability::ROLL_INTERRUPT));
|
||||
if try_roll {
|
||||
controller.inputs.move_dir = (tgt_data.pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.try_normalized()
|
||||
.unwrap_or_default();
|
||||
controller.push_basic_input(InputKind::Roll);
|
||||
}
|
||||
try_roll
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -984,12 +862,8 @@ impl<'a> AgentData<'a> {
|
||||
controller.push_basic_input(InputKind::Ability(0));
|
||||
} else if MOBILITY_AGILITY.could_use(self) && MOBILITY_AGILITY.use_desirable(self) {
|
||||
controller.push_basic_input(InputKind::Ability(2));
|
||||
} else if try_roll() {
|
||||
controller.inputs.move_dir = (tgt_data.pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.try_normalized()
|
||||
.unwrap_or_default();
|
||||
controller.push_basic_input(InputKind::Roll);
|
||||
} else if try_roll(controller) {
|
||||
// Nothing here, rolling handled in try_roll function
|
||||
} else if MOBILITY_FEINT.could_use(attack_data, self) && rng.gen::<f32>() < 0.3 {
|
||||
controller.push_basic_input(InputKind::Ability(1));
|
||||
advance(
|
||||
@ -1641,7 +1515,7 @@ impl<'a> AgentData<'a> {
|
||||
enum ActionStateConditions {
|
||||
ConditionStaffCanShockwave = 0,
|
||||
}
|
||||
let context = AbilityContext::yeet(Some(self.char_state));
|
||||
let context = AbilityContext::try_from(Some(self.char_state));
|
||||
let extract_ability = |input: AbilityInput| {
|
||||
self.active_abilities
|
||||
.activate_ability(
|
||||
|
@ -1,8 +1,10 @@
|
||||
use common::{
|
||||
comp::{
|
||||
buff::Buffs, group, item::MaterialStatManifest, ActiveAbilities, Alignment, Body,
|
||||
CharacterState, Combo, Energy, Health, Inventory, LightEmitter, LootOwner, Ori,
|
||||
PhysicsState, Poise, Pos, Scale, SkillSet, Stats, Vel,
|
||||
buff::{BuffKind, Buffs},
|
||||
group,
|
||||
item::MaterialStatManifest,
|
||||
ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy, Health, Inventory,
|
||||
LightEmitter, LootOwner, Ori, PhysicsState, Poise, Pos, Scale, SkillSet, Stats, Vel,
|
||||
},
|
||||
link::Is,
|
||||
mounting::Mount,
|
||||
@ -180,3 +182,149 @@ pub enum Path {
|
||||
Separate,
|
||||
Partial,
|
||||
}
|
||||
|
||||
pub struct ComboMeleeData {
|
||||
pub min_range: f32,
|
||||
pub max_range: f32,
|
||||
pub angle: f32,
|
||||
pub energy: f32,
|
||||
}
|
||||
|
||||
impl ComboMeleeData {
|
||||
pub fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.max_range.powi(2)
|
||||
&& attack_data.dist_sqrd > self.min_range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() >= self.energy
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FinisherMeleeData {
|
||||
pub range: f32,
|
||||
pub angle: f32,
|
||||
pub energy: f32,
|
||||
pub combo: u32,
|
||||
}
|
||||
|
||||
impl FinisherMeleeData {
|
||||
pub fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() >= self.energy
|
||||
&& agent_data
|
||||
.combo
|
||||
.map_or(false, |c| c.counter() >= self.combo)
|
||||
}
|
||||
|
||||
pub fn use_desirable(&self, tgt_data: &TargetData, agent_data: &AgentData) -> bool {
|
||||
let combo_factor =
|
||||
agent_data.combo.map_or(0, |c| c.counter()) as f32 / self.combo as f32 * 2.0;
|
||||
let tgt_health_factor = tgt_data.health.map_or(0.0, |h| h.current()) / 50.0;
|
||||
let self_health_factor = agent_data.health.map_or(0.0, |h| h.current()) / 50.0;
|
||||
// Use becomes more desirable if either self or target is close to death
|
||||
combo_factor > tgt_health_factor.min(self_health_factor)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SelfBuffData {
|
||||
pub buff: BuffKind,
|
||||
pub energy: f32,
|
||||
}
|
||||
|
||||
impl SelfBuffData {
|
||||
pub fn could_use(&self, agent_data: &AgentData) -> bool {
|
||||
agent_data.energy.current() >= self.energy
|
||||
}
|
||||
|
||||
pub fn use_desirable(&self, agent_data: &AgentData) -> bool {
|
||||
agent_data
|
||||
.buffs
|
||||
.map_or(false, |buffs| !buffs.contains(self.buff))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiveMeleeData {
|
||||
pub range: f32,
|
||||
pub angle: f32,
|
||||
pub energy: f32,
|
||||
}
|
||||
|
||||
impl DiveMeleeData {
|
||||
// Hack here refers to agents using the mildly unintended method of roll jumping
|
||||
// to achieve the required downwards vertical speed to enter dive melee when on
|
||||
// flat ground.
|
||||
pub fn npc_should_use_hack(&self, agent_data: &AgentData, tgt_data: &TargetData) -> bool {
|
||||
let dist_sqrd_2d = agent_data.pos.0.xy().distance_squared(tgt_data.pos.0.xy());
|
||||
agent_data.energy.current() > self.energy
|
||||
&& agent_data.physics_state.on_ground.is_some()
|
||||
&& agent_data.pos.0.z >= tgt_data.pos.0.z
|
||||
&& dist_sqrd_2d > (self.range / 2.0).powi(2)
|
||||
&& dist_sqrd_2d < (self.range + 5.0).powi(2)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockData {
|
||||
pub angle: f32,
|
||||
// Should probably just always use 5 or so unless riposte melee
|
||||
pub range: f32,
|
||||
pub energy: f32,
|
||||
}
|
||||
|
||||
impl BlockData {
|
||||
pub fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() >= self.energy
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DashMeleeData {
|
||||
pub range: f32,
|
||||
pub angle: f32,
|
||||
pub initial_energy: f32,
|
||||
pub energy_drain: f32,
|
||||
pub speed: f32,
|
||||
pub charge_dur: f32,
|
||||
}
|
||||
|
||||
impl DashMeleeData {
|
||||
// TODO: Maybe figure out better way of pulling in base accel from body and
|
||||
// accounting for friction?
|
||||
const BASE_SPEED: f32 = 3.0;
|
||||
const ORI_RATE: f32 = 30.0;
|
||||
|
||||
pub fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
let charge_dur = self.charge_dur(agent_data);
|
||||
let charge_dist = charge_dur * self.speed * Self::BASE_SPEED;
|
||||
let attack_dist = charge_dist + self.range;
|
||||
let ori_gap = Self::ORI_RATE * charge_dur;
|
||||
attack_data.dist_sqrd < attack_dist.powi(2)
|
||||
&& attack_data.angle < self.angle + ori_gap
|
||||
&& agent_data.energy.current() > self.initial_energy
|
||||
}
|
||||
|
||||
pub fn use_desirable(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
let charge_dist = self.charge_dur(agent_data) * self.speed * Self::BASE_SPEED;
|
||||
attack_data.dist_sqrd / charge_dist.powi(2) > 0.75_f32.powi(2)
|
||||
}
|
||||
|
||||
fn charge_dur(&self, agent_data: &AgentData) -> f32 {
|
||||
((agent_data.energy.current() - self.initial_energy) / self.energy_drain)
|
||||
.clamp(0.0, self.charge_dur)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RapidMeleeData {
|
||||
pub range: f32,
|
||||
pub angle: f32,
|
||||
pub energy: f32,
|
||||
pub strikes: u32,
|
||||
}
|
||||
|
||||
impl RapidMeleeData {
|
||||
pub fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool {
|
||||
attack_data.dist_sqrd < self.range.powi(2)
|
||||
&& attack_data.angle < self.angle
|
||||
&& agent_data.energy.current() > self.energy * self.strikes as f32
|
||||
}
|
||||
}
|
||||
|
@ -2885,7 +2885,6 @@ fn skill_strings(skill: Skill) -> SkillStrings<'static> {
|
||||
Skill::General(s) => general_skill_strings(s),
|
||||
Skill::UnlockGroup(s) => unlock_skill_strings(s),
|
||||
// weapon trees
|
||||
Skill::Sword(s) => sword_skill_strings(s),
|
||||
Skill::Axe(s) => axe_skill_strings(s),
|
||||
Skill::Hammer(s) => hammer_skill_strings(s),
|
||||
Skill::Bow(s) => bow_skill_strings(s),
|
||||
@ -2897,6 +2896,7 @@ fn skill_strings(skill: Skill) -> SkillStrings<'static> {
|
||||
Skill::Swim(s) => swim_skill_strings(s),
|
||||
// mining
|
||||
Skill::Pick(s) => mining_skill_strings(s),
|
||||
_ => SkillStrings::plain("", ""),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2954,8 +2954,6 @@ fn unlock_skill_strings(group: SkillGroupKind) -> SkillStrings<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
fn sword_skill_strings(_skill: SwordSkill) -> SkillStrings<'static> { SkillStrings::plain("", "") }
|
||||
|
||||
fn axe_skill_strings(skill: AxeSkill) -> SkillStrings<'static> {
|
||||
let modifiers = SKILL_MODIFIERS.axe_tree;
|
||||
match skill {
|
||||
|
@ -2912,7 +2912,7 @@ impl Hud {
|
||||
skillsets.get(entity),
|
||||
bodies.get(entity),
|
||||
) {
|
||||
let context = AbilityContext::yeet(char_states.get(entity));
|
||||
let context = AbilityContext::try_from(char_states.get(entity));
|
||||
Skillbar::new(
|
||||
client,
|
||||
&info,
|
||||
@ -3343,7 +3343,7 @@ impl Hud {
|
||||
bodies.get(entity),
|
||||
poises.get(entity),
|
||||
) {
|
||||
let context = AbilityContext::yeet(char_states.get(entity));
|
||||
let context = AbilityContext::try_from(char_states.get(entity));
|
||||
for event in Diary::new(
|
||||
&self.show,
|
||||
client,
|
||||
|
@ -917,7 +917,7 @@ impl FigureMgr {
|
||||
let second_tool_spec = second_tool_spec.as_deref();
|
||||
let hands = (active_tool_hand, second_tool_hand);
|
||||
|
||||
let context = AbilityContext::yeet(character);
|
||||
let context = AbilityContext::try_from(character);
|
||||
|
||||
let ability_id = character.and_then(|c| {
|
||||
c.ability_info()
|
||||
|
Loading…
Reference in New Issue
Block a user