diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron index 975a38f162..0ab9687caa 100644 --- a/assets/common/entity/dungeon/tier-5/cultist.ron +++ b/assets/common/entity/dungeon/tier-5/cultist.ron @@ -8,13 +8,13 @@ loadout: Inline(( inherit: Asset("common.loadout.dungeon.tier-5.cultist"), active_hands: InHands((Choice([ - (2, ModularWeapon(tool: Axe, material: Orichalcum, hands: One)), + // (2, ModularWeapon(tool: Axe, material: Orichalcum, hands: One)), (4, Item("common.items.weapons.sword.cultist")), - (2, Item("common.items.weapons.staff.cultist_staff")), - (2, Item("common.items.weapons.hammer.cultist_purp_2h-0")), - (2, ModularWeapon(tool: Hammer, material: Orichalcum, hands: One)), - (2, Item("common.items.weapons.bow.velorite")), - (1, Item("common.items.weapons.sceptre.sceptre_velorite_0")), + // (2, Item("common.items.weapons.staff.cultist_staff")), + // (2, Item("common.items.weapons.hammer.cultist_purp_2h-0")), + // (2, ModularWeapon(tool: Hammer, material: Orichalcum, hands: One)), + // (2, Item("common.items.weapons.bow.velorite")), + // (1, Item("common.items.weapons.sceptre.sceptre_velorite_0")), ]), None)), )), ), diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 5d6ab99338..da4f06e26c 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -143,6 +143,7 @@ impl ActiveAbilities { inv: Option<&Inventory>, skill_set: &SkillSet, body: Option<&Body>, + char_state: Option<&CharacterState>, context: AbilityContext, // bool is from_offhand ) -> Option<(CharacterAbility, bool)> { @@ -188,7 +189,7 @@ impl ActiveAbilities { .map(|ability| (scale_ability(ability, EquipSlot::ActiveMainhand), false)) }), Ability::SpeciesMovement => matches!(body, Some(Body::Humanoid(_))) - .then(CharacterAbility::default_roll) + .then(|| CharacterAbility::default_roll(char_state)) .map(|ability| (ability.adjusted_by_skills(skill_set, None), false)), Ability::MainWeaponAux(index) => ability_set(EquipSlot::ActiveMainhand) .and_then(|abilities| { @@ -931,9 +932,24 @@ impl CharacterAbility { } } - pub fn default_roll() -> CharacterAbility { + pub fn default_roll(current_state: Option<&CharacterState>) -> CharacterAbility { + let remaining_recover = if let Some(char_state) = current_state { + if matches!(char_state.stage_section(), Some(StageSection::Recover)) { + let timer = char_state.timer().map_or(0.0, |t| t.as_secs_f32()); + let recover_duration = char_state + .durations() + .and_then(|durs| durs.recover) + .map_or(timer, |rec| rec.as_secs_f32()); + recover_duration - timer + } else { + 0.0 + } + } else { + 0.0 + }; CharacterAbility::Roll { - energy_cost: 12.0, + // Energy cost increased by + energy_cost: 12.0 + remaining_recover * 100.0, buildup_duration: 0.05, movement_duration: 0.33, recover_duration: 0.125, @@ -2889,9 +2905,10 @@ pub enum SwordStance { bitflags::bitflags! { #[derive(Default, Serialize, Deserialize)] + // If more are ever needed, first check if any not used anymore, as some were only used in intermediary stages so may be free pub struct Capability: u8 { - // Allows rolls to interrupt the ability at any point, not just during buildup - const ROLL_INTERRUPT = 0b00000001; + // Allows rolls to interrupt the ability at any point for free, not just during buildup + const ROLL_INTERRUPTS_FREE = 0b00000001; // Allows blocking to interrupt the ability at any point const BLOCK_INTERRUPT = 0b00000010; // When the ability is in the buildup section, it counts as a block with 50% DR diff --git a/common/src/states/basic_aura.rs b/common/src/states/basic_aura.rs index dbc5eb0124..a7fea2ae93 100644 --- a/common/src/states/basic_aura.rs +++ b/common/src/states/basic_aura.rs @@ -147,7 +147,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index c710eeca76..06c0bcbc40 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -219,7 +219,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index d901f128b3..386f252941 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -51,7 +51,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); @@ -115,7 +115,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index b134cf737c..4274ec1fb2 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -132,7 +132,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 86c6b5fa61..ad20914e2a 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -141,7 +141,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 00fadd72ae..936ab78afc 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -196,7 +196,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index c625ea1278..785aedb947 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -186,7 +186,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 0ab6bb72fa..84701649ac 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -356,7 +356,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/combo_melee2.rs b/common/src/states/combo_melee2.rs index 5bc9a3066a..cfff041001 100644 --- a/common/src/states/combo_melee2.rs +++ b/common/src/states/combo_melee2.rs @@ -103,12 +103,12 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.7); - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); let strike_data = self.strike_data(); diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index f3c292cb1a..b97586eccb 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -55,7 +55,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_move(data, &mut update, 0.1); @@ -257,7 +257,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/finisher_melee.rs b/common/src/states/finisher_melee.rs index 55212dd575..64c09256fb 100644 --- a/common/src/states/finisher_melee.rs +++ b/common/src/states/finisher_melee.rs @@ -51,7 +51,7 @@ impl CharacterBehavior for Data { handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.7); handle_jump(data, output_events, &mut update, 1.0); - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); match self.stage_section { StageSection::Buildup => { diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index f0d0e0b361..a47c151084 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -1,8 +1,8 @@ use super::utils::*; use crate::{ comp::{ - character_state::OutputEvents, slot::EquipSlot, CharacterState, InventoryAction, Ori, - StateUpdate, + character_state::OutputEvents, controller::InputKind, slot::EquipSlot, CharacterState, + InventoryAction, Ori, StateUpdate, }, event::LocalEvent, outcome::Outcome, @@ -43,7 +43,9 @@ impl CharacterBehavior for Data { handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); handle_jump(data, output_events, &mut update, 1.0); - handle_dodge_input(data, &mut update); + if input_is_pressed(data, InputKind::Roll) { + handle_input(data, output_events, &mut update, InputKind::Roll); + } handle_wield(data, &mut update); // If still in this state, do the things diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs index ae37b42d2a..46df26498f 100644 --- a/common/src/states/idle.rs +++ b/common/src/states/idle.rs @@ -1,8 +1,8 @@ use super::utils::*; use crate::{ comp::{ - character_state::OutputEvents, inventory::item::armor::Friction, CharacterState, - InventoryAction, StateUpdate, + character_state::OutputEvents, controller::InputKind, inventory::item::armor::Friction, + CharacterState, InventoryAction, StateUpdate, }, states::behavior::{CharacterBehavior, JoinData}, }; @@ -26,7 +26,9 @@ impl CharacterBehavior for Data { handle_wield(data, &mut update); handle_climb(data, &mut update); handle_wallrun(data, &mut update); - handle_dodge_input(data, &mut update); + if input_is_pressed(data, InputKind::Roll) { + handle_input(data, output_events, &mut update, InputKind::Roll); + } // Try to Fall/Stand up/Move if self.is_sneaking diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 704f108be3..7fadef7997 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -152,7 +152,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/leap_shockwave.rs b/common/src/states/leap_shockwave.rs index e024eae5f2..61e7fbd74f 100644 --- a/common/src/states/leap_shockwave.rs +++ b/common/src/states/leap_shockwave.rs @@ -216,7 +216,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/music.rs b/common/src/states/music.rs index fceedcc5bc..2f4cc96564 100644 --- a/common/src/states/music.rs +++ b/common/src/states/music.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{character_state::OutputEvents, CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, controller::InputKind, CharacterState, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -71,7 +71,9 @@ 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) { - handle_dodge_input(data, &mut update); + if input_is_pressed(data, InputKind::Roll) { + handle_input(data, output_events, &mut update, InputKind::Roll); + } } update diff --git a/common/src/states/rapid_melee.rs b/common/src/states/rapid_melee.rs index 52b59ab773..e7b015a84c 100644 --- a/common/src/states/rapid_melee.rs +++ b/common/src/states/rapid_melee.rs @@ -1,10 +1,10 @@ use crate::{ comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate}, + event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - event::ServerEvent, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -52,7 +52,7 @@ impl CharacterBehavior for Data { handle_orientation(data, &mut update, self.static_data.ori_modifier, None); handle_move(data, &mut update, self.static_data.move_modifier); - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); match self.stage_section { StageSection::Buildup => { diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index e20362c339..040592a8ed 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -162,7 +162,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/riposte_melee.rs b/common/src/states/riposte_melee.rs index a1c4dcf608..c8cd298ed2 100644 --- a/common/src/states/riposte_melee.rs +++ b/common/src/states/riposte_melee.rs @@ -43,7 +43,7 @@ impl CharacterBehavior for Data { handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.7); handle_jump(data, output_events, &mut update, 1.0); - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); match self.stage_section { StageSection::Buildup => { diff --git a/common/src/states/self_buff.rs b/common/src/states/self_buff.rs index 43710b607b..66e2bbdc4e 100644 --- a/common/src/states/self_buff.rs +++ b/common/src/states/self_buff.rs @@ -119,7 +119,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index d1bb81a3b9..b4f3c0fa03 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -176,7 +176,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs index 3d19ae94f5..aadfd1c0eb 100644 --- a/common/src/states/sit.rs +++ b/common/src/states/sit.rs @@ -1,6 +1,10 @@ use super::utils::*; use crate::{ - comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, + comp::{ + ability::Stance, character_state::OutputEvents, CharacterState, InventoryAction, + StateUpdate, + }, + event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, idle, @@ -15,6 +19,13 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); + if !matches!(data.stance, Some(Stance::None)) { + output_events.emit_server(ServerEvent::ChangeStance { + entity: data.entity, + stance: Stance::None, + }); + } + handle_wield(data, &mut update); handle_jump(data, output_events, &mut update, 1.0); diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 5eadcd2f9b..93efbc3a0a 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -53,7 +53,7 @@ pub struct Data { } impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { + fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); match self.static_data.movement_behavior { @@ -161,7 +161,7 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_interrupts(data, &mut update); + handle_interrupts(data, &mut update, output_events); update } diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index 64e01e076b..4e3d9eb6b5 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -1,8 +1,8 @@ use super::utils::*; use crate::{ comp::{ - character_state::OutputEvents, inventory::slot::InvSlotId, item::ItemDefinitionIdOwned, - CharacterState, InventoryManip, StateUpdate, + character_state::OutputEvents, controller::InputKind, item::ItemDefinitionIdOwned, + slot::InvSlotId, CharacterState, InventoryManip, StateUpdate, }, event::{LocalEvent, ServerEvent}, outcome::Outcome, @@ -141,7 +141,9 @@ impl CharacterBehavior for Data { handle_wield(data, &mut update); // At end of state logic so an interrupt isn't overwritten - handle_dodge_input(data, &mut update); + if input_is_pressed(data, InputKind::Roll) { + handle_input(data, output_events, &mut update, InputKind::Roll); + } update } diff --git a/common/src/states/use_item.rs b/common/src/states/use_item.rs index bd3297d319..1f6f666724 100644 --- a/common/src/states/use_item.rs +++ b/common/src/states/use_item.rs @@ -3,6 +3,7 @@ use crate::{ comp::{ buff::{BuffChange, BuffKind}, character_state::OutputEvents, + controller::InputKind, inventory::{ item::{ConsumableKind, ItemKind}, slot::{InvSlotId, Slot}, @@ -132,7 +133,9 @@ impl CharacterBehavior for Data { } // At end of state logic so an interrupt isn't overwritten - handle_dodge_input(data, &mut update); + if input_is_pressed(data, InputKind::Roll) { + handle_input(data, output_events, &mut update, InputKind::Roll); + } if matches!(update.character, CharacterState::Roll(_)) { // Remove potion/saturation effect if left the use item state early by rolling diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 1b37d9767c..0e8e4322d6 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1080,6 +1080,7 @@ fn handle_ability( data.inventory, data.skill_set, Some(data.body), + Some(data.character), context, ) }) @@ -1104,6 +1105,19 @@ fn handle_ability( return true; } } + 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_wielded = true; + } else { + if data.character.is_wield() { + roll.was_wielded = true; + } + if data.character.is_stealthy() { + roll.is_sneaking = true; + } + } + } false } @@ -1114,12 +1128,9 @@ pub fn handle_input( input: InputKind, ) { match input { - InputKind::Primary | InputKind::Secondary | InputKind::Ability(_) => { + InputKind::Primary | InputKind::Secondary | InputKind::Ability(_) | InputKind::Roll => { handle_ability(data, update, output_events, input); }, - InputKind::Roll => { - handle_dodge_input(data, update); - }, InputKind::Jump => { handle_jump(data, output_events, update, 1.0); }, @@ -1172,58 +1183,16 @@ pub fn handle_block_input(data: &JoinData<'_>, update: &mut StateUpdate) -> bool } } -/// Checks that player can perform a dodge, then -/// attempts to perform their dodge ability -pub fn handle_dodge_input(data: &JoinData<'_>, update: &mut StateUpdate) -> bool { - if input_is_pressed(data, InputKind::Roll) && data.body.is_humanoid() { - let ability = CharacterAbility::default_roll().adjusted_by_skills(data.skill_set, None); - if ability.requirements_paid(data, update) { - update.character = CharacterState::from(( - &ability, - AbilityInfo::from_input(data, false, InputKind::Roll, Default::default()), - data, - )); - update.used_inputs.push(InputKind::Roll); - 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_wielded = true; - } else { - if data.character.is_wield() { - roll.was_wielded = true; - } - if data.character.is_stealthy() { - roll.is_sneaking = true; - } - } - } - true - } else { - false - } - } else { - false - } -} - /// Returns whether an interrupt occurred -pub fn handle_interrupts(data: &JoinData, update: &mut StateUpdate) -> bool { - let can_dodge = { - let in_buildup = data - .character - .stage_section() - .map_or(true, |stage_section| { - matches!(stage_section, StageSection::Buildup) - }); - let interruptible = data - .character - .ability_info() - .map(|info| info.ability_meta) - .map_or(false, |meta| { - meta.capabilities.contains(Capability::ROLL_INTERRUPT) - }); - in_buildup || interruptible - }; +pub fn handle_interrupts( + data: &JoinData, + update: &mut StateUpdate, + output_events: &mut OutputEvents, +) -> bool { + let can_dodge = matches!( + data.character.stage_section(), + Some(StageSection::Buildup | StageSection::Recover) + ); let can_block = data .character .ability_info() @@ -1231,8 +1200,8 @@ pub fn handle_interrupts(data: &JoinData, update: &mut StateUpdate) -> bool { .map_or(false, |meta| { meta.capabilities.contains(Capability::BLOCK_INTERRUPT) }); - if can_dodge { - handle_dodge_input(data, update) + if can_dodge && input_is_pressed(data, InputKind::Roll) { + handle_ability(data, update, output_events, InputKind::Roll) } else if can_block { handle_block_input(data, update) } else { diff --git a/common/systems/src/stats.rs b/common/systems/src/stats.rs index 1dd247ffe8..c5cd629fe9 100644 --- a/common/systems/src/stats.rs +++ b/common/systems/src/stats.rs @@ -2,7 +2,6 @@ use common::{ combat, comp::{ self, - ability::Stance, item::MaterialStatManifest, skills::{GeneralSkill, Skill}, Body, CharacterState, Combo, Energy, Health, Inventory, Poise, Pos, SkillSet, Stats, @@ -141,13 +140,8 @@ impl<'a> System<'a> for Sys { } // Update energies and poises - for (entity, character_state, mut energy, mut poise) in ( - &read_data.entities, - &read_data.char_states, - &mut energies, - &mut poises, - ) - .join() + for (character_state, mut energy, mut poise) in + (&read_data.char_states, &mut energies, &mut poises).join() { match character_state { // Sitting accelerates recharging energy the most diff --git a/server/agent/src/attack.rs b/server/agent/src/attack.rs index 4b115b70ce..f03ae35044 100644 --- a/server/agent/src/attack.rs +++ b/server/agent/src/attack.rs @@ -473,7 +473,145 @@ impl<'a> AgentData<'a> { read_data: &ReadData, rng: &mut impl Rng, ) { - // TODO + #[derive(Copy, Clone)] + enum SwordTactics { + Unskilled = 0, + Basic = 1, + HeavySimple = 2, + AgileSimple = 3, + DefensiveSimple = 4, + CripplingSimple = 5, + CleavingSimple = 6, + HeavyAdvanced = 7, + AgileAdvanced = 8, + DefensiveAdvanced = 9, + CripplingAdvanced = 10, + CleavingAdvanced = 11, + } + + impl SwordTactics { + fn from_u8(x: u8) -> Self { + use SwordTactics::*; + match x { + 0 => Unskilled, + 1 => Basic, + 2 => HeavySimple, + 3 => AgileSimple, + 4 => DefensiveSimple, + 5 => CripplingSimple, + 6 => CleavingSimple, + 7 => HeavyAdvanced, + 8 => AgileAdvanced, + 9 => DefensiveAdvanced, + 10 => CripplingAdvanced, + 11 => CleavingAdvanced, + _ => Unskilled, + } + } + } + + enum IntCounters { + Tactics = 0, + } + + if !agent.action_state.initialized { + let available_tactics = { + let mut tactics = Vec::new(); + let try_tactic = |skill, tactic, tactics: &mut Vec| { + if self.skill_set.has_skill(Skill::Sword(skill)) { + tactics.push(tactic); + } + }; + try_tactic( + SwordSkill::HeavyFortitude, + SwordTactics::HeavyAdvanced, + &mut tactics, + ); + try_tactic( + SwordSkill::AgileDancingEdge, + SwordTactics::AgileAdvanced, + &mut tactics, + ); + try_tactic( + SwordSkill::DefensiveStalwartSword, + SwordTactics::DefensiveAdvanced, + &mut tactics, + ); + try_tactic( + SwordSkill::CripplingEviscerate, + SwordTactics::CripplingAdvanced, + &mut tactics, + ); + try_tactic( + SwordSkill::CleavingBladeFever, + SwordTactics::CleavingAdvanced, + &mut tactics, + ); + if tactics.is_empty() { + try_tactic( + SwordSkill::HeavyWindmillSlash, + SwordTactics::HeavySimple, + &mut tactics, + ); + try_tactic( + SwordSkill::AgileQuickDraw, + SwordTactics::AgileSimple, + &mut tactics, + ); + try_tactic( + SwordSkill::DefensiveDisengage, + SwordTactics::DefensiveSimple, + &mut tactics, + ); + try_tactic( + SwordSkill::CripplingGouge, + SwordTactics::CripplingSimple, + &mut tactics, + ); + try_tactic( + SwordSkill::CleavingWhirlwindSlice, + SwordTactics::CleavingSimple, + &mut tactics, + ); + } + if tactics.is_empty() { + try_tactic(SwordSkill::CrescentSlash, SwordTactics::Basic, &mut tactics); + } + if tactics.is_empty() { + tactics.push(SwordTactics::Unskilled); + } + tactics + }; + + let tactic = available_tactics + .choose(rng) + .copied() + .unwrap_or(SwordTactics::Unskilled); + + agent.action_state.int_counters[IntCounters::Tactics as usize] = tactic as u8; + } + + // Action modes + const RECKLESS: usize = 0; + const GUARDED: usize = 1; + const RETREAT: usize = 2; + + match SwordTactics::from_u8(agent.action_state.int_counters[IntCounters::Tactics as usize]) + { + SwordTactics::Unskilled => {}, + SwordTactics::Basic => {}, + SwordTactics::HeavySimple => {}, + SwordTactics::AgileSimple => {}, + SwordTactics::DefensiveSimple => {}, + SwordTactics::CripplingSimple => {}, + SwordTactics::CleavingSimple => {}, + SwordTactics::HeavyAdvanced => {}, + SwordTactics::AgileAdvanced => {}, + SwordTactics::DefensiveAdvanced => {}, + SwordTactics::CripplingAdvanced => {}, + SwordTactics::CleavingAdvanced => {}, + _ => {}, + } } pub fn handle_bow_attack( @@ -532,7 +670,8 @@ impl<'a> AgentData<'a> { // Use shotgun if target close and have sufficient energy controller.push_basic_input(InputKind::Ability(0)); } else if self.body.map(|b| b.is_humanoid()).unwrap_or(false) - && self.energy.current() > CharacterAbility::default_roll().energy_cost() + && self.energy.current() + > CharacterAbility::default_roll(Some(self.char_state)).energy_cost() && !matches!(self.char_state, CharacterState::BasicRanged(c) if !matches!(c.stage_section, StageSection::Recover)) { // Else roll away if can roll and have enough energy, and not using shotgun @@ -645,6 +784,7 @@ impl<'a> AgentData<'a> { Some(self.inventory), self.skill_set, self.body, + Some(self.char_state), context, ) .unwrap_or_default() @@ -661,7 +801,8 @@ impl<'a> AgentData<'a> { let shockwave_cost = shockwave.energy_cost(); if self.body.map_or(false, |b| b.is_humanoid()) && attack_data.in_min_range() - && self.energy.current() > CharacterAbility::default_roll().energy_cost() + && self.energy.current() + > CharacterAbility::default_roll(Some(self.char_state)).energy_cost() && !matches!(self.char_state, CharacterState::Shockwave(_)) { // if a humanoid, have enough stamina, not in shockwave, and in melee range, @@ -698,7 +839,8 @@ impl<'a> AgentData<'a> { [ActionStateConditions::ConditionStaffCanShockwave as usize] = true; } } else if self.energy.current() - > shockwave_cost + CharacterAbility::default_roll().energy_cost() + > shockwave_cost + + CharacterAbility::default_roll(Some(self.char_state)).energy_cost() && attack_data.dist_sqrd < flamethrower_range.powi(2) { controller.push_basic_input(InputKind::Secondary); @@ -835,7 +977,8 @@ impl<'a> AgentData<'a> { } } else if attack_data.dist_sqrd < (2.0 * attack_data.min_attack_dist).powi(2) { if self.body.map_or(false, |b| b.is_humanoid()) - && self.energy.current() > CharacterAbility::default_roll().energy_cost() + && self.energy.current() + > CharacterAbility::default_roll(Some(self.char_state)).energy_cost() && !matches!(self.char_state, CharacterState::BasicAura(c) if !matches!(c.stage_section, StageSection::Recover)) { // Else roll away if can roll and have enough energy, and not using aura or in @@ -2998,7 +3141,8 @@ impl<'a> AgentData<'a> { } } else if attack_data.dist_sqrd < (2.0 * attack_data.min_attack_dist).powi(2) { if self.body.map_or(false, |b| b.is_humanoid()) - && self.energy.current() > CharacterAbility::default_roll().energy_cost() + && self.energy.current() + > CharacterAbility::default_roll(Some(self.char_state)).energy_cost() && !matches!(self.char_state, CharacterState::BasicAura(c) if !matches!(c.stage_section, StageSection::Recover)) { // Else use steam beam diff --git a/server/agent/src/data.rs b/server/agent/src/data.rs index e6d90eaf2f..7cd74e9a0c 100644 --- a/server/agent/src/data.rs +++ b/server/agent/src/data.rs @@ -194,8 +194,7 @@ pub enum Path { } pub struct ComboMeleeData { - pub min_range: f32, - pub max_range: f32, + pub range: f32, pub angle: f32, pub energy: f32, } @@ -203,9 +202,7 @@ pub struct ComboMeleeData { impl ComboMeleeData { pub fn could_use(&self, attack_data: &AttackData, agent_data: &AgentData) -> bool { attack_data.dist_sqrd - < (self.max_range + agent_data.body.map_or(0.0, |b| b.max_radius())).powi(2) - && attack_data.dist_sqrd - > (self.min_range + agent_data.body.map_or(0.0, |b| b.max_radius())).powi(2) + < (self.range + agent_data.body.map_or(0.0, |b| b.max_radius())).powi(2) && attack_data.angle < self.angle && agent_data.energy.current() >= self.energy } diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index e3f8cc5900..f53817e7d9 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -18,13 +18,6 @@ use crate::{ }, GlobalState, }; -use conrod_core::{ - color, image, - widget::{self, Button, Image, Rectangle, State, Text}, - widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, -}; -use i18n::Localization; -use vek::*; use client::{self, Client}; use common::{ combat, @@ -48,7 +41,14 @@ use common::{ }, consts::{ENERGY_PER_LEVEL, HP_PER_LEVEL}, }; +use conrod_core::{ + color, image, + widget::{self, Button, Image, Rectangle, State, Text}, + widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, +}; +use i18n::Localization; use std::borrow::Cow; +use vek::*; const ART_SIZE: [f64; 2] = [320.0, 320.0]; widget_ids! { @@ -1595,8 +1595,7 @@ impl<'a> Diary<'a> { .set(state.ids.sword_bg, ui); use PositionSpecifier::TopLeftWithMarginsOn; - let skill_buttons - = &[ + let skill_buttons = &[ SkillIcon::Ability { skill: Skill::Sword(SwordSkill::CrescentSlash), ability_id: "veloren.core.pseudo_abilities.sword.crescent_slash", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 0276681922..98d4aa0616 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2976,6 +2976,7 @@ impl Hud { let combos = ecs.read_storage::(); let time = ecs.read_resource::