mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Made combo melee more ergonomic to use when it is a stance
This commit is contained in:
parent
21aabb5663
commit
8d3567b6b2
@ -1379,37 +1379,40 @@ impl CharacterAbility {
|
||||
|
||||
#[must_use]
|
||||
pub fn contextualize(mut self, data: &JoinData) -> Self {
|
||||
if let Some(ability_info) = data.character.ability_info() {
|
||||
if let Some(AbilityKind::Sword(old_stance)) = ability_info.ability_meta.kind {
|
||||
if let Some(AbilityKind::Sword(new_stance)) = self.ability_meta().kind {
|
||||
let energy_reduction = if old_stance == new_stance {
|
||||
0.75
|
||||
} else if old_stance == SwordStance::Balanced {
|
||||
1.0
|
||||
} else {
|
||||
1.5
|
||||
};
|
||||
use CharacterAbility::*;
|
||||
match &mut self {
|
||||
BasicMelee { energy_cost, .. }
|
||||
| ComboMelee2 {
|
||||
energy_cost_per_strike: energy_cost,
|
||||
..
|
||||
}
|
||||
| FinisherMelee { energy_cost, .. }
|
||||
| DashMelee { energy_cost, .. }
|
||||
| SpinMelee { energy_cost, .. }
|
||||
| ChargedMelee { energy_cost, .. }
|
||||
| Shockwave { energy_cost, .. }
|
||||
| BasicBlock { energy_cost, .. }
|
||||
| SelfBuff { energy_cost, .. }
|
||||
| DiveMelee { energy_cost, .. }
|
||||
| RiposteMelee { energy_cost, .. }
|
||||
| RapidMelee { energy_cost, .. } => {
|
||||
*energy_cost *= energy_reduction;
|
||||
},
|
||||
_ => {},
|
||||
if let Some(AbilityKind::Sword(old_stance)) = data
|
||||
.character
|
||||
.ability_info()
|
||||
.and_then(|info| info.ability_meta)
|
||||
.and_then(|meta| meta.kind)
|
||||
{
|
||||
if let Some(AbilityKind::Sword(new_stance)) = self.ability_meta().kind {
|
||||
let energy_reduction = if old_stance == new_stance {
|
||||
0.75
|
||||
} else if old_stance == SwordStance::Balanced {
|
||||
1.0
|
||||
} else {
|
||||
1.5
|
||||
};
|
||||
use CharacterAbility::*;
|
||||
match &mut self {
|
||||
BasicMelee { energy_cost, .. }
|
||||
| ComboMelee2 {
|
||||
energy_cost_per_strike: energy_cost,
|
||||
..
|
||||
}
|
||||
| FinisherMelee { energy_cost, .. }
|
||||
| DashMelee { energy_cost, .. }
|
||||
| SpinMelee { energy_cost, .. }
|
||||
| ChargedMelee { energy_cost, .. }
|
||||
| Shockwave { energy_cost, .. }
|
||||
| BasicBlock { energy_cost, .. }
|
||||
| SelfBuff { energy_cost, .. }
|
||||
| DiveMelee { energy_cost, .. }
|
||||
| RiposteMelee { energy_cost, .. }
|
||||
| RapidMelee { energy_cost, .. } => {
|
||||
*energy_cost *= energy_reduction;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,13 +281,16 @@ impl CharacterState {
|
||||
}
|
||||
|
||||
pub fn is_parry(&self) -> bool {
|
||||
let from_capability =
|
||||
if let Some(capabilities) = self.ability_info().map(|a| a.ability_meta.capabilities) {
|
||||
capabilities.contains(Capability::BUILDUP_PARRIES)
|
||||
&& matches!(self.stage_section(), Some(StageSection::Buildup))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let from_capability = if let Some(capabilities) = self
|
||||
.ability_info()
|
||||
.and_then(|a| a.ability_meta)
|
||||
.map(|m| m.capabilities)
|
||||
{
|
||||
capabilities.contains(Capability::BUILDUP_PARRIES)
|
||||
&& matches!(self.stage_section(), Some(StageSection::Buildup))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let from_state = match self {
|
||||
CharacterState::BasicBlock(c) => c.is_parry(),
|
||||
CharacterState::RiposteMelee(c) => matches!(c.stage_section, StageSection::Buildup),
|
||||
@ -503,7 +506,7 @@ impl CharacterState {
|
||||
CharacterState::Skate(_) => None,
|
||||
CharacterState::Glide(_) => None,
|
||||
CharacterState::GlideWield(_) => None,
|
||||
CharacterState::Stunned(_) => None,
|
||||
CharacterState::Stunned(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::Sit => None,
|
||||
CharacterState::Dance => None,
|
||||
CharacterState::BasicBlock(data) => Some(data.static_data.ability_info),
|
||||
@ -528,8 +531,8 @@ impl CharacterState {
|
||||
CharacterState::BasicSummon(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::SelfBuff(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::UseItem(_) => None,
|
||||
CharacterState::SpriteInteract(_) => None,
|
||||
CharacterState::UseItem(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::SpriteInteract(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::Music(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::DiveMelee(data) => Some(data.static_data.ability_info),
|
||||
|
@ -78,7 +78,11 @@ pub enum PoiseState {
|
||||
impl PoiseState {
|
||||
/// Returns the optional stunned character state and duration of stun, and
|
||||
/// optional impulse strength corresponding to a particular poise state
|
||||
pub fn poise_effect(&self, was_wielded: bool) -> (Option<(CharacterState, f64)>, Option<f32>) {
|
||||
pub fn poise_effect(
|
||||
&self,
|
||||
was_wielded: bool,
|
||||
ability_info: states::utils::AbilityInfo,
|
||||
) -> (Option<(CharacterState, f64)>, Option<f32>) {
|
||||
use states::{
|
||||
stunned::{Data, StaticData},
|
||||
utils::StageSection,
|
||||
@ -113,6 +117,7 @@ impl PoiseState {
|
||||
recover_duration,
|
||||
movement_speed,
|
||||
poise_state: *self,
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
@ -262,10 +267,9 @@ impl Poise {
|
||||
let from_char = {
|
||||
let resistant = char_state
|
||||
.and_then(|cs| cs.ability_info())
|
||||
.and_then(|a| a.ability_meta)
|
||||
.map_or(false, |a| {
|
||||
a.ability_meta
|
||||
.capabilities
|
||||
.contains(Capability::POISE_RESISTANT)
|
||||
a.capabilities.contains(Capability::POISE_RESISTANT)
|
||||
});
|
||||
if resistant { 0.5 } else { 0.0 }
|
||||
};
|
||||
|
@ -96,7 +96,7 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
},
|
||||
StageSection::Action => {
|
||||
if input_is_pressed(data, self.static_data.ability_info.input)
|
||||
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
|
||||
&& (self.static_data.energy_drain <= f32::EPSILON
|
||||
|| update.energy.current() > 0.0)
|
||||
{
|
||||
|
@ -76,7 +76,7 @@ impl CharacterBehavior for Data {
|
||||
},
|
||||
StageSection::Action => {
|
||||
if self.static_data.can_hold
|
||||
&& input_is_pressed(data, self.static_data.ability_info.input)
|
||||
&& self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
|
||||
{
|
||||
// Block
|
||||
update.character = CharacterState::BasicBlock(Data {
|
||||
|
@ -118,7 +118,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
if input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
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);
|
||||
@ -146,10 +146,12 @@ fn reset_state(
|
||||
output_events: &mut OutputEvents,
|
||||
update: &mut StateUpdate,
|
||||
) {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
data.static_data.ability_info.input,
|
||||
);
|
||||
if let Some(input) = data.static_data.ability_info.input {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
input,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
if input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
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);
|
||||
@ -149,10 +149,12 @@ fn reset_state(
|
||||
output_events: &mut OutputEvents,
|
||||
update: &mut StateUpdate,
|
||||
) {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
data.static_data.ability_info.input,
|
||||
);
|
||||
if let Some(input) = data.static_data.ability_info.input {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
input,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
if input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
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 {
|
||||
update.vel.0 = update.vel.0.try_normalized().unwrap_or_default()
|
||||
@ -69,10 +69,12 @@ fn reset_state(
|
||||
output_events: &mut OutputEvents,
|
||||
update: &mut StateUpdate,
|
||||
) {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
data.static_data.ability_info.input,
|
||||
);
|
||||
if let Some(input) = data.static_data.ability_info.input {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
input,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ impl CharacterBehavior for Data {
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Charge => {
|
||||
if input_is_pressed(data, self.static_data.ability_info.input)
|
||||
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
|
||||
&& update.energy.current() >= self.static_data.energy_cost
|
||||
&& self.timer < self.static_data.charge_duration
|
||||
{
|
||||
@ -77,7 +77,7 @@ impl CharacterBehavior for Data {
|
||||
update
|
||||
.energy
|
||||
.change_by(-self.static_data.energy_drain * data.dt.0);
|
||||
} else if input_is_pressed(data, self.static_data.ability_info.input)
|
||||
} else if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
|
||||
&& update.energy.current() >= self.static_data.energy_cost
|
||||
{
|
||||
// Maintains charge
|
||||
|
@ -96,7 +96,7 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
},
|
||||
StageSection::Charge => {
|
||||
if !input_is_pressed(data, self.static_data.ability_info.input) && !self.exhausted {
|
||||
if !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) && !self.exhausted {
|
||||
let charge_frac = self.charge_frac();
|
||||
let arrow = ProjectileConstructor::Arrow {
|
||||
damage: self.static_data.initial_damage as f32
|
||||
@ -138,7 +138,7 @@ impl CharacterBehavior for Data {
|
||||
..*self
|
||||
});
|
||||
} else if self.timer < self.static_data.charge_duration
|
||||
&& input_is_pressed(data, self.static_data.ability_info.input)
|
||||
&& self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
|
||||
{
|
||||
// Charges
|
||||
update.character = CharacterState::ChargedRanged(Data {
|
||||
@ -150,7 +150,7 @@ impl CharacterBehavior for Data {
|
||||
update
|
||||
.energy
|
||||
.change_by(-self.static_data.energy_drain * data.dt.0);
|
||||
} else if input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
} else if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
|
||||
// Holds charge
|
||||
update.character = CharacterState::ChargedRanged(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
|
@ -341,7 +341,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
if input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
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);
|
||||
@ -382,14 +382,16 @@ fn reset_state(
|
||||
output_events: &mut OutputEvents,
|
||||
update: &mut StateUpdate,
|
||||
) {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
data.static_data.ability_info.input,
|
||||
);
|
||||
if let Some(input) = data.static_data.ability_info.input {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
input,
|
||||
);
|
||||
|
||||
if let CharacterState::ComboMelee(c) = &mut update.character {
|
||||
c.stage = (data.stage % data.static_data.num_stages) + 1;
|
||||
if let CharacterState::ComboMelee(c) = &mut update.character {
|
||||
c.stage = (data.stage % data.static_data.num_stages) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
character_state::OutputEvents, tool::Stats, CharacterState, InputKind, Melee,
|
||||
MeleeConstructor, StateUpdate, InputAttr
|
||||
MeleeConstructor, StateUpdate, InputAttr, InventoryAction, slot::{Slot, EquipSlot},
|
||||
},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
utils::*, idle, wielding,
|
||||
},
|
||||
uid::Uid,
|
||||
};
|
||||
@ -116,7 +116,7 @@ impl CharacterBehavior for Data {
|
||||
let ability_input = if self.static_data.is_stance {
|
||||
InputKind::Primary
|
||||
} else {
|
||||
self.static_data.ability_info.input
|
||||
self.static_data.ability_info.input.unwrap_or(InputKind::Primary)
|
||||
};
|
||||
|
||||
handle_orientation(data, &mut update, 1.0, None);
|
||||
@ -237,7 +237,7 @@ impl CharacterBehavior for Data {
|
||||
|
||||
if input_is_pressed(data, ability_input) {
|
||||
next_strike(&mut update)
|
||||
} else if !input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
} else if !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
|
||||
attempt_input(data, output_events, &mut update);
|
||||
}
|
||||
},
|
||||
@ -255,7 +255,7 @@ impl CharacterBehavior for Data {
|
||||
) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
if matches!(data.character, CharacterState::ComboMelee2(data) if data.static_data.ability_info.input == input && input != InputKind::Primary && data.stage_section.is_none()) {
|
||||
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);
|
||||
} else {
|
||||
update.queued_inputs.insert(input, InputAttr {
|
||||
@ -265,6 +265,97 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
update
|
||||
}
|
||||
|
||||
fn swap_equipped_weapons(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
if let CharacterState::ComboMelee2(c) = data.character {
|
||||
if c.stage_section.is_none() {
|
||||
update.character =
|
||||
CharacterState::Wielding(wielding::Data { is_sneaking: data.character.is_stealthy() });
|
||||
attempt_swap_equipped_weapons(data, &mut update);
|
||||
}
|
||||
}
|
||||
update
|
||||
}
|
||||
|
||||
fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
if let CharacterState::ComboMelee2(c) = data.character {
|
||||
if c.stage_section.is_none() {
|
||||
update.character = CharacterState::Idle(idle::Data {
|
||||
is_sneaking: data.character.is_stealthy(),
|
||||
footwear: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
update
|
||||
}
|
||||
|
||||
fn manipulate_loadout(
|
||||
&self,
|
||||
data: &JoinData,
|
||||
output_events: &mut OutputEvents,
|
||||
inv_action: InventoryAction,
|
||||
) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
if let CharacterState::ComboMelee2(c) = data.character {
|
||||
if c.stage_section.is_none() {
|
||||
match inv_action {
|
||||
InventoryAction::Drop(slot)
|
||||
| InventoryAction::Swap(slot, _)
|
||||
| InventoryAction::Swap(_, Slot::Equip(slot)) if matches!(slot, EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand) => {
|
||||
update.character = CharacterState::Idle(idle::Data {
|
||||
is_sneaking: data.character.is_stealthy(),
|
||||
footwear: None,
|
||||
});
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
handle_manipulate_loadout(data, output_events, &mut update, inv_action);
|
||||
}
|
||||
}
|
||||
update
|
||||
}
|
||||
|
||||
fn glide_wield(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
if let CharacterState::ComboMelee2(c) = data.character {
|
||||
if c.stage_section.is_none() {
|
||||
attempt_glide_wield(data, &mut update, output_events);
|
||||
}
|
||||
}
|
||||
update
|
||||
}
|
||||
|
||||
fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
if let CharacterState::ComboMelee2(c) = data.character {
|
||||
if c.stage_section.is_none() {
|
||||
attempt_sit(data, &mut update);
|
||||
}
|
||||
}
|
||||
update
|
||||
}
|
||||
|
||||
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
if let CharacterState::ComboMelee2(c) = data.character {
|
||||
if c.stage_section.is_none() {
|
||||
attempt_dance(data, &mut update);
|
||||
}
|
||||
}
|
||||
update
|
||||
}
|
||||
|
||||
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
if let CharacterState::ComboMelee2(c) = data.character {
|
||||
if c.stage_section.is_none() && data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
||||
update.character = CharacterState::Wielding(wielding::Data { is_sneaking: true });
|
||||
}
|
||||
}
|
||||
update
|
||||
}
|
||||
}
|
||||
|
||||
impl Data {
|
||||
|
@ -81,7 +81,7 @@ impl CharacterBehavior for Data {
|
||||
} else {
|
||||
// Transitions to charge section of stage
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
auto_charge: !input_is_pressed(data, self.static_data.ability_info.input),
|
||||
auto_charge: !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)),
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Charge,
|
||||
..*self
|
||||
@ -90,7 +90,7 @@ impl CharacterBehavior for Data {
|
||||
},
|
||||
StageSection::Charge => {
|
||||
if self.timer < self.charge_end_timer
|
||||
&& (input_is_pressed(data, self.static_data.ability_info.input)
|
||||
&& (self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
|
||||
|| (self.auto_charge && self.timer < self.static_data.charge_duration))
|
||||
&& update.energy.current() > 0.0
|
||||
{
|
||||
|
@ -56,7 +56,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
if input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
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);
|
||||
@ -72,7 +72,7 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
|
||||
// At end of state logic so an interrupt isn't overwritten
|
||||
if !input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
if !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
|
||||
handle_dodge_input(data, &mut update);
|
||||
}
|
||||
|
||||
@ -86,10 +86,12 @@ fn reset_state(
|
||||
output_events: &mut OutputEvents,
|
||||
update: &mut StateUpdate,
|
||||
) {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
data.static_data.ability_info.input,
|
||||
);
|
||||
if let Some(input) = data.static_data.ability_info.input {
|
||||
handle_input(
|
||||
join,
|
||||
output_events,
|
||||
update,
|
||||
input,
|
||||
);
|
||||
}
|
||||
}
|
@ -84,7 +84,7 @@ impl CharacterBehavior for Data {
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else if input_is_pressed(data, self.static_data.ability_info.input)
|
||||
} else if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
|
||||
&& update.energy.current() >= self.static_data.energy_cost
|
||||
{
|
||||
// Fire if input is pressed still
|
||||
|
@ -121,7 +121,7 @@ impl CharacterBehavior for Data {
|
||||
} else if update.energy.current() as f32 >= self.static_data.energy_cost
|
||||
&& (self.consecutive_spins < self.static_data.num_spins
|
||||
|| (self.static_data.is_infinite
|
||||
&& input_is_pressed(data, self.static_data.ability_info.input)))
|
||||
&& self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))))
|
||||
{
|
||||
update.character = CharacterState::SpinMelee(Data {
|
||||
timer: Duration::default(),
|
||||
|
@ -29,6 +29,8 @@ pub struct StaticData {
|
||||
pub was_wielded: bool,
|
||||
/// Was sneaking
|
||||
pub was_sneak: bool,
|
||||
/// Miscellaneous information about the ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
@ -19,6 +19,8 @@ pub struct StaticData {
|
||||
pub movement_speed: f32,
|
||||
/// Poise state (used for determining animation in the client)
|
||||
pub poise_state: PoiseState,
|
||||
/// Miscellaneous information about the ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -36,6 +36,8 @@ pub struct StaticData {
|
||||
pub was_wielded: bool,
|
||||
/// Was sneaking
|
||||
pub was_sneak: bool,
|
||||
/// Miscellaneous information about the ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
@ -806,8 +806,9 @@ pub fn handle_manipulate_loadout(
|
||||
inv_slot,
|
||||
item_kind,
|
||||
item_hash: item.item_hash(),
|
||||
was_wielded: matches!(data.character, CharacterState::Wielding(_)),
|
||||
was_wielded: data.character.is_wield(),
|
||||
was_sneak: data.character.is_stealthy(),
|
||||
ability_info: AbilityInfo::from_forced_state_change(data.character),
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
@ -910,8 +911,9 @@ pub fn handle_manipulate_loadout(
|
||||
recover_duration,
|
||||
sprite_pos,
|
||||
sprite_kind: sprite_interact,
|
||||
was_wielded: matches!(data.character, CharacterState::Wielding(_)),
|
||||
was_wielded: data.character.is_wield(),
|
||||
was_sneak: data.character.is_stealthy(),
|
||||
ability_info: AbilityInfo::from_forced_state_change(data.character),
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
@ -1060,7 +1062,7 @@ pub fn handle_dodge_input(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||
));
|
||||
if let CharacterState::Roll(roll) = &mut update.character {
|
||||
if let CharacterState::ComboMelee(c) = data.character {
|
||||
roll.was_combo = Some((c.static_data.ability_info.input, c.stage));
|
||||
roll.was_combo = c.static_data.ability_info.input.map(|input| (input, c.stage));
|
||||
roll.was_wielded = true;
|
||||
} else {
|
||||
if data.character.is_wield() {
|
||||
@ -1085,7 +1087,7 @@ pub fn handle_interrupts(
|
||||
// Check that the input used to enter current character state (if there was one)
|
||||
// is not pressed
|
||||
if input_override
|
||||
.or_else(|| data.character.ability_info().map(|a| a.input))
|
||||
.or_else(|| data.character.ability_info().and_then(|a| a.input))
|
||||
.map_or(true, |input| !input_is_pressed(data, input))
|
||||
{
|
||||
let can_dodge = {
|
||||
@ -1095,16 +1097,14 @@ pub fn handle_interrupts(
|
||||
.map_or(true, |stage_section| {
|
||||
matches!(stage_section, StageSection::Buildup)
|
||||
});
|
||||
let interruptible = data.character.ability_info().map_or(false, |info| {
|
||||
info.ability_meta
|
||||
.capabilities
|
||||
let interruptible = data.character.ability_info().and_then(|info| info.ability_meta).map_or(false, |meta| {
|
||||
meta.capabilities
|
||||
.contains(Capability::ROLL_INTERRUPT)
|
||||
});
|
||||
in_buildup || interruptible
|
||||
};
|
||||
let can_block = data.character.ability_info().map_or(false, |info| {
|
||||
info.ability_meta
|
||||
.capabilities
|
||||
let can_block = data.character.ability_info().and_then(|info| info.ability_meta).map_or(false, |meta| {
|
||||
meta.capabilities
|
||||
.contains(Capability::BLOCK_INTERRUPT)
|
||||
});
|
||||
if can_dodge {
|
||||
@ -1281,12 +1281,13 @@ impl MovementDirection {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct AbilityInfo {
|
||||
pub tool: Option<ToolKind>,
|
||||
pub hand: Option<HandInfo>,
|
||||
pub input: InputKind,
|
||||
pub input: Option<InputKind>,
|
||||
pub input_attr: Option<InputAttr>,
|
||||
pub ability_meta: AbilityMeta,
|
||||
pub ability_meta: Option<AbilityMeta>,
|
||||
pub ability: Option<Ability>,
|
||||
pub return_ability: Option<InputKind>,
|
||||
}
|
||||
@ -1314,7 +1315,7 @@ impl AbilityInfo {
|
||||
.map(|(i, a)| a.get_ability(i, data.inventory, Some(data.skill_set)));
|
||||
|
||||
let return_ability = if data.character.should_be_returned_to() {
|
||||
data.character.ability_info().map(|info| info.input)
|
||||
data.character.ability_info().and_then(|info| info.input)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -1322,13 +1323,31 @@ impl AbilityInfo {
|
||||
Self {
|
||||
tool,
|
||||
hand,
|
||||
input,
|
||||
input: Some(input),
|
||||
input_attr: data.controller.queued_inputs.get(&input).copied(),
|
||||
ability_meta,
|
||||
ability_meta: Some(ability_meta),
|
||||
ability,
|
||||
return_ability,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_forced_state_change(char_state: &CharacterState) -> Self {
|
||||
let return_ability = if char_state.should_be_returned_to() {
|
||||
char_state.ability_info().and_then(|info| info.input)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
tool: None,
|
||||
hand: None,
|
||||
input: None,
|
||||
input_attr: None,
|
||||
ability_meta: None,
|
||||
ability: None,
|
||||
return_ability,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_ability(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||
|
@ -17,7 +17,7 @@ use common::{
|
||||
resources::{DeltaTime, Time},
|
||||
states::{
|
||||
behavior::{JoinData, JoinStruct},
|
||||
idle,
|
||||
idle, utils,
|
||||
},
|
||||
terrain::TerrainGrid,
|
||||
uid::Uid,
|
||||
@ -148,10 +148,11 @@ impl<'a> System<'a> for Sys {
|
||||
// Enter stunned state if poise damage is enough
|
||||
if let Some(mut poise) = poises.get_mut(entity) {
|
||||
let was_wielded = char_state.is_wield();
|
||||
let ability_info = utils::AbilityInfo::from_forced_state_change(&char_state);
|
||||
let poise_state = poise.poise_state();
|
||||
let pos = pos.0;
|
||||
if let (Some((stunned_state, stunned_duration)), impulse_strength) =
|
||||
poise_state.poise_effect(was_wielded)
|
||||
poise_state.poise_effect(was_wielded, ability_info)
|
||||
{
|
||||
// Reset poise if there is some stunned state to apply
|
||||
poise.reset(*read_data.time, stunned_duration);
|
||||
|
@ -26,7 +26,7 @@ use common::{
|
||||
outcome::{HealthChangeInfo, Outcome},
|
||||
resources::Time,
|
||||
rtsim::RtSimEntity,
|
||||
states::utils::StageSection,
|
||||
states::utils::{AbilityInfo, StageSection},
|
||||
terrain::{Block, BlockKind, TerrainGrid},
|
||||
uid::{Uid, UidAllocator},
|
||||
util::Dir,
|
||||
@ -1324,8 +1324,9 @@ pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) {
|
||||
) {
|
||||
let poise_state = comp::poise::PoiseState::Interrupted;
|
||||
let was_wielded = char_state.is_wield();
|
||||
let ability_info = AbilityInfo::from_forced_state_change(&char_state);
|
||||
if let (Some((stunned_state, stunned_duration)), impulse_strength) =
|
||||
poise_state.poise_effect(was_wielded)
|
||||
poise_state.poise_effect(was_wielded, ability_info)
|
||||
{
|
||||
// Reset poise if there is some stunned state to apply
|
||||
poise.reset(*time, stunned_duration);
|
||||
|
@ -250,7 +250,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
{
|
||||
match buff.kind {
|
||||
BuffIconKind::Buff { kind, .. } => event.push(Event::RemoveBuff(kind)),
|
||||
BuffIconKind::Ability { .. } => todo!(),
|
||||
BuffIconKind::Ability { .. } => {},
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -413,7 +413,7 @@ impl<'a> Widget for BuffsBar<'a> {
|
||||
{
|
||||
match buff.kind {
|
||||
BuffIconKind::Buff { kind, .. } => event.push(Event::RemoveBuff(kind)),
|
||||
BuffIconKind::Ability { .. } => todo!(),
|
||||
BuffIconKind::Ability { .. } => {},
|
||||
}
|
||||
}
|
||||
Text::new(&remaining_time)
|
||||
|
Loading…
Reference in New Issue
Block a user