mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Rolls cancel recover at the cost of additional energy, sitting ends active stances.
This commit is contained in:
parent
6b5ae2b6fe
commit
7365fcb530
@ -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)),
|
||||
)),
|
||||
),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 => {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 => {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 => {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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<SwordTactics>| {
|
||||
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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -2976,6 +2976,7 @@ impl Hud {
|
||||
let combos = ecs.read_storage::<comp::Combo>();
|
||||
let time = ecs.read_resource::<Time>();
|
||||
let stances = ecs.read_storage::<comp::Stance>();
|
||||
let char_states = ecs.read_storage::<comp::CharacterState>();
|
||||
// Combo floater stuffs
|
||||
self.floaters.combo_floater = self.floaters.combo_floater.map(|mut f| {
|
||||
f.timer -= dt.as_secs_f64();
|
||||
@ -3026,6 +3027,7 @@ impl Hud {
|
||||
self.floaters.combo_floater,
|
||||
context,
|
||||
combos.get(entity),
|
||||
char_states.get(entity),
|
||||
)
|
||||
.set(self.ids.skillbar, ui_widgets)
|
||||
{
|
||||
|
@ -30,7 +30,8 @@ use common::comp::{
|
||||
ItemDesc, MaterialStatManifest,
|
||||
},
|
||||
skillset::SkillGroupKind,
|
||||
Ability, ActiveAbilities, Body, Combo, Energy, Health, Inventory, Poise, PoiseState, SkillSet,
|
||||
Ability, ActiveAbilities, Body, CharacterState, Combo, Energy, Health, Inventory, Poise,
|
||||
PoiseState, SkillSet,
|
||||
};
|
||||
use conrod_core::{
|
||||
color,
|
||||
@ -312,6 +313,7 @@ pub struct Skillbar<'a> {
|
||||
combo_floater: Option<ComboFloater>,
|
||||
context: AbilityContext,
|
||||
combo: Option<&'a Combo>,
|
||||
char_state: Option<&'a CharacterState>,
|
||||
}
|
||||
|
||||
impl<'a> Skillbar<'a> {
|
||||
@ -343,6 +345,7 @@ impl<'a> Skillbar<'a> {
|
||||
combo_floater: Option<ComboFloater>,
|
||||
context: AbilityContext,
|
||||
combo: Option<&'a Combo>,
|
||||
char_state: Option<&'a CharacterState>,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
@ -372,6 +375,7 @@ impl<'a> Skillbar<'a> {
|
||||
combo_floater,
|
||||
context,
|
||||
combo,
|
||||
char_state,
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,6 +925,7 @@ impl<'a> Skillbar<'a> {
|
||||
self.body,
|
||||
self.context,
|
||||
self.combo,
|
||||
self.char_state,
|
||||
);
|
||||
|
||||
let image_source = (self.item_imgs, self.imgs);
|
||||
@ -1002,7 +1007,8 @@ impl<'a> Skillbar<'a> {
|
||||
|
||||
// Helper
|
||||
let tooltip_text = |slot| {
|
||||
let (hotbar, inventory, _, skill_set, active_abilities, _, context, _) = content_source;
|
||||
let (hotbar, inventory, _, skill_set, active_abilities, _, context, _, _) =
|
||||
content_source;
|
||||
hotbar.get(slot).and_then(|content| match content {
|
||||
hotbar::SlotContents::Inventory(i, _) => inventory
|
||||
.get_by_hash(i)
|
||||
@ -1141,6 +1147,7 @@ impl<'a> Skillbar<'a> {
|
||||
Some(self.inventory),
|
||||
self.skillset,
|
||||
Some(self.body),
|
||||
self.char_state,
|
||||
self.context,
|
||||
)
|
||||
})
|
||||
|
@ -10,7 +10,7 @@ use common::{
|
||||
ability::{Ability, AbilityInput, AuxiliaryAbility},
|
||||
item::tool::{AbilityContext, ToolKind},
|
||||
slot::InvSlotId,
|
||||
ActiveAbilities, Body, Combo, Energy, Inventory, Item, ItemKey, SkillSet,
|
||||
ActiveAbilities, Body, CharacterState, Combo, Energy, Inventory, Item, ItemKey, SkillSet,
|
||||
},
|
||||
recipe::ComponentRecipeBook,
|
||||
};
|
||||
@ -130,6 +130,7 @@ type HotbarSource<'a> = (
|
||||
&'a Body,
|
||||
AbilityContext,
|
||||
Option<&'a Combo>,
|
||||
Option<&'a CharacterState>,
|
||||
);
|
||||
type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs);
|
||||
|
||||
@ -138,7 +139,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
|
||||
fn image_key(
|
||||
&self,
|
||||
(hotbar, inventory, energy, skillset, active_abilities, body, context, combo): &HotbarSource<'a>,
|
||||
(hotbar, inventory, energy, skillset, active_abilities, body, context, combo, char_state): &HotbarSource<'a>,
|
||||
) -> Option<(Self::ImageKey, Option<Color>)> {
|
||||
const GREYED_OUT: Color = Color::Rgba(0.3, 0.3, 0.3, 0.8);
|
||||
hotbar.get(*self).and_then(|contents| match contents {
|
||||
@ -168,6 +169,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
Some(inventory),
|
||||
skillset,
|
||||
Some(body),
|
||||
*char_state,
|
||||
*context,
|
||||
)
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user