Rolls cancel recover at the cost of additional energy, sitting ends active stances.

This commit is contained in:
Sam 2023-01-16 00:34:19 -05:00
parent 6b5ae2b6fe
commit 7365fcb530
35 changed files with 290 additions and 137 deletions

View File

@ -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)),
)),
),

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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();

View File

@ -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
}

View File

@ -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 => {

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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 => {

View File

@ -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
}

View File

@ -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 => {

View File

@ -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
}

View File

@ -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
}

View File

@ -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);

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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",

View File

@ -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)
{

View File

@ -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,
)
})

View File

@ -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,
)
})