mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/block-parry' into 'master'
Blocking/Parrying Closes #646 and #416 See merge request veloren/veloren!2153
This commit is contained in:
commit
ccc22d4969
CHANGELOG.md
assets
common/abilities
voxygen
audio
i18n/en
shaders
common
server/src/events
voxygen
anim/src/character
src
@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Missing translations can be displayed in English.
|
||||
- New large birds npcs
|
||||
- Day period dependant wildlife spawns
|
||||
- You can now block and parry with melee weapons
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -1 +1,7 @@
|
||||
BasicBlock
|
||||
BasicBlock(
|
||||
buildup_duration: 0.1,
|
||||
recover_duration: 0.1,
|
||||
max_angle: 90.0,
|
||||
block_strength: 0.8,
|
||||
energy_cost: 0.0,
|
||||
)
|
@ -1,5 +1,5 @@
|
||||
SpinMelee(
|
||||
buildup_duration: 0.6,
|
||||
buildup_duration: 0.35,
|
||||
swing_duration: 0.4,
|
||||
recover_duration: 0.5,
|
||||
base_damage: 160,
|
||||
|
BIN
assets/voxygen/audio/ambient/wind.ogg
(Stored with Git LFS)
BIN
assets/voxygen/audio/ambient/wind.ogg
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/audio/sfx/character/block_1.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/character/block_1.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/character/block_2.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/character/block_2.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/character/block_3.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/character/block_3.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/character/parry_1.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/character/parry_1.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/character/parry_2.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/character/parry_2.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -4,7 +4,8 @@
|
||||
(
|
||||
string_map: {
|
||||
"gameinput.primary": "Basic Attack",
|
||||
"gameinput.secondary": "Secondary Attack/Block/Aim",
|
||||
"gameinput.secondary": "Secondary Attack",
|
||||
"gameinput.block": "Block",
|
||||
"gameinput.slot1": "Hotbar Slot 1",
|
||||
"gameinput.slot2": "Hotbar Slot 2",
|
||||
"gameinput.slot3": "Hotbar Slot 3",
|
||||
|
@ -202,7 +202,7 @@ void main() {
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
normalize(vec3(rand0, rand1, rand3)) * 0.3,
|
||||
normalize(vec3(rand4, rand5, rand6)) * 2.0 + grav_vel(earth_gravity)
|
||||
normalize(vec3(rand4, rand5, rand6)) * 4.0 + grav_vel(earth_gravity)
|
||||
),
|
||||
vec3(1.0),
|
||||
vec4(3.5, 3 + rand7, 0, 1),
|
||||
|
@ -12,11 +12,12 @@ use crate::{
|
||||
},
|
||||
poise::PoiseChange,
|
||||
skills::SkillGroupKind,
|
||||
Body, Combo, Energy, EnergyChange, EnergySource, Health, HealthChange, HealthSource,
|
||||
Inventory, SkillSet, Stats,
|
||||
Body, CharacterState, Combo, Energy, EnergyChange, EnergySource, Health, HealthChange,
|
||||
HealthSource, Inventory, Ori, SkillSet, Stats,
|
||||
},
|
||||
event::ServerEvent,
|
||||
outcome::Outcome,
|
||||
states::utils::StageSection,
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
@ -39,6 +40,15 @@ pub enum GroupTarget {
|
||||
OutOfGroup,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum AttackSource {
|
||||
Melee,
|
||||
Projectile,
|
||||
Beam,
|
||||
Shockwave,
|
||||
Explosion,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct AttackerInfo<'a> {
|
||||
@ -51,10 +61,13 @@ pub struct AttackerInfo<'a> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub struct TargetInfo<'a> {
|
||||
pub entity: EcsEntity,
|
||||
pub uid: Uid,
|
||||
pub inventory: Option<&'a Inventory>,
|
||||
pub stats: Option<&'a Stats>,
|
||||
pub health: Option<&'a Health>,
|
||||
pub pos: Vec3<f32>,
|
||||
pub ori: Option<&'a Ori>,
|
||||
pub char_state: Option<&'a CharacterState>,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -105,6 +118,43 @@ impl Attack {
|
||||
|
||||
pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> { self.effects.iter() }
|
||||
|
||||
pub fn compute_damage_reduction(
|
||||
target: &TargetInfo,
|
||||
source: AttackSource,
|
||||
dir: Dir,
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
) -> f32 {
|
||||
let damage_reduction = Damage::compute_damage_reduction(target.inventory, target.stats);
|
||||
let block_reduction = match source {
|
||||
AttackSource::Melee => {
|
||||
if let (Some(CharacterState::BasicBlock(data)), Some(ori)) =
|
||||
(target.char_state, target.ori)
|
||||
{
|
||||
if ori.look_vec().angle_between(-*dir) < data.static_data.max_angle.to_radians()
|
||||
{
|
||||
let parry = matches!(data.stage_section, StageSection::Buildup);
|
||||
emit_outcome(Outcome::Block {
|
||||
parry,
|
||||
pos: target.pos,
|
||||
uid: target.uid,
|
||||
});
|
||||
if parry {
|
||||
1.0
|
||||
} else {
|
||||
data.static_data.block_strength
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
1.0 - (1.0 - damage_reduction) * (1.0 - block_reduction)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn apply_attack(
|
||||
&self,
|
||||
@ -115,6 +165,7 @@ impl Attack {
|
||||
target_dodging: bool,
|
||||
// Currently just modifies damage, maybe look into modifying strength of other effects?
|
||||
strength_modifier: f32,
|
||||
attack_source: AttackSource,
|
||||
mut emit: impl FnMut(ServerEvent),
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
) {
|
||||
@ -126,7 +177,8 @@ impl Attack {
|
||||
.filter(|d| d.target.map_or(true, |t| t == target_group))
|
||||
.filter(|d| !(matches!(d.target, Some(GroupTarget::OutOfGroup)) && target_dodging))
|
||||
{
|
||||
let damage_reduction = Damage::compute_damage_reduction(target.inventory, target.stats);
|
||||
let damage_reduction =
|
||||
Attack::compute_damage_reduction(&target, attack_source, dir, |o| emit_outcome(o));
|
||||
let change = damage.damage.calculate_health_change(
|
||||
damage_reduction,
|
||||
attacker.map(|a| a.uid),
|
||||
|
@ -39,7 +39,7 @@ impl From<&CharacterState> for CharacterAbilityType {
|
||||
CharacterState::BasicRanged(_) => Self::BasicRanged,
|
||||
CharacterState::Boost(_) => Self::Boost,
|
||||
CharacterState::DashMelee(data) => Self::DashMelee(data.stage_section),
|
||||
CharacterState::BasicBlock => Self::BasicBlock,
|
||||
CharacterState::BasicBlock(_) => Self::BasicBlock,
|
||||
CharacterState::LeapMelee(data) => Self::LeapMelee(data.stage_section),
|
||||
CharacterState::ComboMelee(data) => Self::ComboMelee(data.stage_section, data.stage),
|
||||
CharacterState::SpinMelee(data) => Self::SpinMelee(data.stage_section),
|
||||
@ -114,7 +114,13 @@ pub enum CharacterAbility {
|
||||
charge_through: bool,
|
||||
is_interruptible: bool,
|
||||
},
|
||||
BasicBlock,
|
||||
BasicBlock {
|
||||
buildup_duration: f32,
|
||||
recover_duration: f32,
|
||||
max_angle: f32,
|
||||
block_strength: f32,
|
||||
energy_cost: f32,
|
||||
},
|
||||
Roll {
|
||||
energy_cost: f32,
|
||||
buildup_duration: f32,
|
||||
@ -305,7 +311,8 @@ impl CharacterAbility {
|
||||
| CharacterAbility::ChargedRanged { energy_cost, .. }
|
||||
| CharacterAbility::ChargedMelee { energy_cost, .. }
|
||||
| CharacterAbility::Shockwave { energy_cost, .. }
|
||||
| CharacterAbility::BasicAura { energy_cost, .. } => update
|
||||
| CharacterAbility::BasicAura { energy_cost, .. }
|
||||
| CharacterAbility::BasicBlock { energy_cost, .. } => update
|
||||
.energy
|
||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||
.is_ok(),
|
||||
@ -341,6 +348,16 @@ impl CharacterAbility {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_block() -> CharacterAbility {
|
||||
CharacterAbility::BasicBlock {
|
||||
buildup_duration: 0.35,
|
||||
recover_duration: 0.3,
|
||||
max_angle: 60.0,
|
||||
block_strength: 0.5,
|
||||
energy_cost: 50.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adjusted_by_stats(mut self, power: f32, poise_strength: f32, speed: f32) -> Self {
|
||||
use CharacterAbility::*;
|
||||
match self {
|
||||
@ -408,7 +425,15 @@ impl CharacterAbility {
|
||||
*swing_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
BasicBlock => {},
|
||||
BasicBlock {
|
||||
ref mut buildup_duration,
|
||||
ref mut recover_duration,
|
||||
// Block strength explicitly not modified by power, that will be a separate stat
|
||||
..
|
||||
} => {
|
||||
*buildup_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
Roll {
|
||||
ref mut buildup_duration,
|
||||
ref mut movement_duration,
|
||||
@ -578,7 +603,8 @@ impl CharacterAbility {
|
||||
| ChargedRanged { energy_cost, .. }
|
||||
| Shockwave { energy_cost, .. }
|
||||
| HealingBeam { energy_cost, .. }
|
||||
| BasicAura { energy_cost, .. } => *energy_cost as u32,
|
||||
| BasicAura { energy_cost, .. }
|
||||
| BasicBlock { energy_cost, .. } => *energy_cost as u32,
|
||||
BasicBeam { energy_drain, .. } => {
|
||||
if *energy_drain > f32::EPSILON {
|
||||
1
|
||||
@ -586,7 +612,7 @@ impl CharacterAbility {
|
||||
0
|
||||
}
|
||||
},
|
||||
BasicBlock | Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0,
|
||||
Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1235,7 +1261,23 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
stage_section: StageSection::Buildup,
|
||||
exhausted: false,
|
||||
}),
|
||||
CharacterAbility::BasicBlock => CharacterState::BasicBlock,
|
||||
CharacterAbility::BasicBlock {
|
||||
buildup_duration,
|
||||
recover_duration,
|
||||
max_angle,
|
||||
block_strength,
|
||||
energy_cost: _,
|
||||
} => CharacterState::BasicBlock(basic_block::Data {
|
||||
static_data: basic_block::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
max_angle: *max_angle,
|
||||
block_strength: *block_strength,
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::Roll {
|
||||
energy_cost: _,
|
||||
buildup_duration,
|
||||
|
@ -55,7 +55,7 @@ pub enum CharacterState {
|
||||
/// A stunned state
|
||||
Stunned(stunned::Data),
|
||||
/// A basic blocking state
|
||||
BasicBlock,
|
||||
BasicBlock(basic_block::Data),
|
||||
/// Player is busy equipping or unequipping weapons
|
||||
Equipping(equipping::Data),
|
||||
/// Player is holding a weapon and can perform other actions
|
||||
@ -110,7 +110,7 @@ impl CharacterState {
|
||||
| CharacterState::BasicRanged(_)
|
||||
| CharacterState::DashMelee(_)
|
||||
| CharacterState::ComboMelee(_)
|
||||
| CharacterState::BasicBlock
|
||||
| CharacterState::BasicBlock(_)
|
||||
| CharacterState::LeapMelee(_)
|
||||
| CharacterState::SpinMelee(_)
|
||||
| CharacterState::ChargedMelee(_)
|
||||
@ -153,7 +153,7 @@ impl CharacterState {
|
||||
| CharacterState::BasicRanged(_)
|
||||
| CharacterState::DashMelee(_)
|
||||
| CharacterState::ComboMelee(_)
|
||||
| CharacterState::BasicBlock
|
||||
| CharacterState::BasicBlock(_)
|
||||
| CharacterState::LeapMelee(_)
|
||||
| CharacterState::ChargedMelee(_)
|
||||
| CharacterState::ChargedRanged(_)
|
||||
@ -180,7 +180,7 @@ impl CharacterState {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_block(&self) -> bool { matches!(self, CharacterState::BasicBlock) }
|
||||
pub fn is_block(&self) -> bool { matches!(self, CharacterState::BasicBlock(_)) }
|
||||
|
||||
pub fn is_dodge(&self) -> bool { matches!(self, CharacterState::Roll(_)) }
|
||||
|
||||
|
@ -149,15 +149,19 @@ impl ControlAction {
|
||||
pub enum InputKind {
|
||||
Primary = 0,
|
||||
Secondary = 1,
|
||||
Ability(usize) = 2,
|
||||
Roll = 3,
|
||||
Jump = 4,
|
||||
Fly = 5,
|
||||
Block = 2,
|
||||
Ability(usize) = 3,
|
||||
Roll = 4,
|
||||
Jump = 5,
|
||||
Fly = 6,
|
||||
}
|
||||
|
||||
impl InputKind {
|
||||
pub fn is_ability(self) -> bool {
|
||||
matches!(self, Self::Primary | Self::Secondary | Self::Ability(_))
|
||||
matches!(
|
||||
self,
|
||||
Self::Primary | Self::Secondary | Self::Ability(_) | Self::Block
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,6 +326,17 @@ impl Tool {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_block(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
ToolKind::Sword
|
||||
| ToolKind::Axe
|
||||
| ToolKind::Hammer
|
||||
| ToolKind::Shield
|
||||
| ToolKind::Dagger
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
@ -59,6 +59,11 @@ pub enum Outcome {
|
||||
Damage {
|
||||
pos: Vec3<f32>,
|
||||
},
|
||||
Block {
|
||||
pos: Vec3<f32>,
|
||||
parry: bool,
|
||||
uid: Uid,
|
||||
},
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -70,7 +75,8 @@ impl Outcome {
|
||||
| Outcome::Beam { pos, .. }
|
||||
| Outcome::SkillPointGain { pos, .. }
|
||||
| Outcome::SummonedCreature { pos, .. }
|
||||
| Outcome::Damage { pos, .. } => Some(*pos),
|
||||
| Outcome::Damage { pos, .. }
|
||||
| Outcome::Block { pos, .. } => Some(*pos),
|
||||
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
|
||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
|
||||
}
|
||||
|
@ -1,15 +1,36 @@
|
||||
use super::utils::*;
|
||||
use crate::{
|
||||
comp::StateUpdate,
|
||||
comp::{CharacterState, InputKind, StateUpdate},
|
||||
states::behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
// const BLOCK_ACCEL: f32 = 30.0;
|
||||
// const BLOCK_SPEED: f32 = 75.0;
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StaticData {
|
||||
/// How long until state should deal damage
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state has until exiting
|
||||
pub recover_duration: Duration,
|
||||
/// Max angle (45.0 will give you a 90.0 angle window)
|
||||
pub max_angle: f32,
|
||||
/// What percentage incoming damage is reduced by
|
||||
pub block_strength: f32,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||
pub struct Data;
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Struct containing data that does not change over the course of the
|
||||
/// character state
|
||||
pub static_data: StaticData,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
@ -17,6 +38,71 @@ impl CharacterBehavior for Data {
|
||||
|
||||
handle_move(&data, &mut update, 0.4);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::BasicBlock(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Transitions to swing section of stage
|
||||
update.character = CharacterState::BasicBlock(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Swing,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Swing => {
|
||||
if input_is_pressed(data, InputKind::Block) {
|
||||
// Block
|
||||
update.character = CharacterState::BasicBlock(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::BasicBlock(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
// Recovery
|
||||
update.character = CharacterState::BasicBlock(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
},
|
||||
}
|
||||
|
||||
// At end of state logic so an interrupt isn't overwritten
|
||||
if !input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
handle_state_interrupt(data, &mut update, false);
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
||||
|
@ -538,21 +538,17 @@ pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) ->
|
||||
}
|
||||
|
||||
fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||
let hands = |equip_slot| match data.inventory.equipped(equip_slot).map(|i| i.kind()) {
|
||||
Some(ItemKind::Tool(tool)) => Some(tool.hands),
|
||||
_ => None,
|
||||
};
|
||||
let hands = get_hands(data);
|
||||
|
||||
// Mouse1 and Skill1 always use the MainHand slot
|
||||
let always_main_hand = matches!(input, InputKind::Primary | InputKind::Ability(0));
|
||||
let no_main_hand = hands(EquipSlot::Mainhand).is_none();
|
||||
let no_main_hand = hands.0.is_none();
|
||||
// skill_index used to select ability for the AbilityKey::Skill2 input
|
||||
let (equip_slot, skill_index) = if no_main_hand {
|
||||
(Some(EquipSlot::Offhand), 1)
|
||||
} else if always_main_hand {
|
||||
(Some(EquipSlot::Mainhand), 0)
|
||||
} else {
|
||||
let hands = (hands(EquipSlot::Mainhand), hands(EquipSlot::Offhand));
|
||||
match hands {
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(_, Some(Hands::One)) => (Some(EquipSlot::Offhand), 0),
|
||||
@ -579,7 +575,7 @@ fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||
.get(skill_index)
|
||||
.cloned()
|
||||
.and_then(unlocked),
|
||||
InputKind::Roll | InputKind::Jump | InputKind::Fly => None,
|
||||
InputKind::Roll | InputKind::Jump | InputKind::Fly | InputKind::Block => None,
|
||||
})
|
||||
.map(|a| {
|
||||
let tool = unwrap_tool_data(data, equip_slot).map(|t| t.kind);
|
||||
@ -615,6 +611,7 @@ pub fn handle_input(data: &JoinData, update: &mut StateUpdate, input: InputKind)
|
||||
InputKind::Jump => {
|
||||
handle_jump(data, update, 1.0);
|
||||
},
|
||||
InputKind::Block => handle_block_input(data, update),
|
||||
InputKind::Fly => {},
|
||||
}
|
||||
}
|
||||
@ -626,6 +623,24 @@ pub fn attempt_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that player can block, then attempts to block
|
||||
pub fn handle_block_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
let can_block =
|
||||
|equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some(tool) if tool.can_block());
|
||||
let hands = get_hands(data);
|
||||
if input_is_pressed(data, InputKind::Block)
|
||||
&& (can_block(EquipSlot::Mainhand) || (hands.0.is_none() && can_block(EquipSlot::Offhand)))
|
||||
{
|
||||
let ability = CharacterAbility::default_block();
|
||||
if ability.requirements_paid(data, update) {
|
||||
update.character = CharacterState::from((
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, false, InputKind::Roll),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that player can perform a dodge, then
|
||||
/// attempts to perform their dodge ability
|
||||
pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
@ -662,6 +677,17 @@ pub fn unwrap_tool_data<'a>(data: &'a JoinData, equip_slot: EquipSlot) -> Option
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_hands(data: &JoinData) -> (Option<Hands>, Option<Hands>) {
|
||||
let hand = |slot| {
|
||||
if let Some(ItemKind::Tool(tool)) = data.inventory.equipped(slot).map(|i| i.kind()) {
|
||||
Some(tool.hands)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
(hand(EquipSlot::Mainhand), hand(EquipSlot::Offhand))
|
||||
}
|
||||
|
||||
pub fn get_crit_data(data: &JoinData, ai: AbilityInfo) -> (f32, f32) {
|
||||
const DEFAULT_CRIT_DATA: (f32, f32) = (0.5, 1.3);
|
||||
use HandInfo::*;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use common::{
|
||||
combat::{AttackerInfo, TargetInfo},
|
||||
combat::{AttackSource, AttackerInfo, TargetInfo},
|
||||
comp::{
|
||||
Beam, BeamSegment, Body, Combo, Energy, Group, Health, HealthSource, Inventory, Ori, Pos,
|
||||
Scale, Stats,
|
||||
Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource,
|
||||
Inventory, Ori, Pos, Scale, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
outcome::Outcome,
|
||||
@ -40,6 +40,7 @@ pub struct ReadData<'a> {
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
combos: ReadStorage<'a, Combo>,
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling beams that heal or do damage
|
||||
@ -184,10 +185,13 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let target_info = TargetInfo {
|
||||
entity: target,
|
||||
uid: *uid_b,
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
||||
beam_segment.properties.attack.apply_attack(
|
||||
@ -197,6 +201,7 @@ impl<'a> System<'a> for Sys {
|
||||
ori.look_dir(),
|
||||
false,
|
||||
1.0,
|
||||
AttackSource::Beam,
|
||||
|e| server_events.push(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
|
@ -301,9 +301,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Sneak => {
|
||||
states::sneak::Data::handle_event(&states::sneak::Data, &j, action)
|
||||
},
|
||||
CharacterState::BasicBlock => {
|
||||
states::basic_block::Data.handle_event(&j, action)
|
||||
},
|
||||
CharacterState::BasicBlock(data) => data.handle_event(&j, action),
|
||||
CharacterState::Roll(data) => data.handle_event(&j, action),
|
||||
CharacterState::Wielding => states::wielding::Data.handle_event(&j, action),
|
||||
CharacterState::Equipping(data) => data.handle_event(&j, action),
|
||||
@ -357,7 +355,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, &j),
|
||||
CharacterState::Dance => states::dance::Data::behavior(&states::dance::Data, &j),
|
||||
CharacterState::Sneak => states::sneak::Data::behavior(&states::sneak::Data, &j),
|
||||
CharacterState::BasicBlock => states::basic_block::Data.behavior(&j),
|
||||
CharacterState::BasicBlock(data) => data.behavior(&j),
|
||||
CharacterState::Roll(data) => data.behavior(&j),
|
||||
CharacterState::Wielding => states::wielding::Data.behavior(&j),
|
||||
CharacterState::Equipping(data) => data.behavior(&j),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use common::{
|
||||
combat::{AttackerInfo, TargetInfo},
|
||||
combat::{AttackSource, AttackerInfo, TargetInfo},
|
||||
comp::{
|
||||
Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, Pos, Scale,
|
||||
Stats,
|
||||
@ -87,11 +87,12 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Go through all other entities
|
||||
for (target, pos_b, health_b, body_b) in (
|
||||
for (target, pos_b, health_b, body_b, uid_b) in (
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.healths,
|
||||
&read_data.bodies,
|
||||
&read_data.uids,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -143,10 +144,13 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let target_info = TargetInfo {
|
||||
entity: target,
|
||||
uid: *uid_b,
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.char_states.get(target),
|
||||
};
|
||||
|
||||
melee_attack.attack.apply_attack(
|
||||
@ -156,6 +160,7 @@ impl<'a> System<'a> for Sys {
|
||||
dir,
|
||||
is_dodge,
|
||||
1.0,
|
||||
AttackSource::Melee,
|
||||
|e| server_emitter.emit(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
|
@ -1,8 +1,8 @@
|
||||
use common::{
|
||||
combat::{AttackerInfo, TargetInfo},
|
||||
combat::{AttackSource, AttackerInfo, TargetInfo},
|
||||
comp::{
|
||||
projectile, Body, Combo, Energy, Group, Health, HealthSource, Inventory, Ori, PhysicsState,
|
||||
Pos, Projectile, Stats, Vel,
|
||||
projectile, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory,
|
||||
Ori, PhysicsState, Pos, Projectile, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
outcome::Outcome,
|
||||
@ -36,6 +36,7 @@ pub struct ReadData<'a> {
|
||||
combos: ReadStorage<'a, Combo>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling projectile effect triggers
|
||||
@ -60,11 +61,11 @@ impl<'a> System<'a> for Sys {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
|
||||
// Attacks
|
||||
'projectile_loop: for (entity, pos, physics, ori, mut projectile) in (
|
||||
'projectile_loop: for (entity, pos, physics, vel, mut projectile) in (
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.physics_states,
|
||||
&mut orientations,
|
||||
&read_data.velocities,
|
||||
&mut projectiles,
|
||||
)
|
||||
.join()
|
||||
@ -110,51 +111,60 @@ impl<'a> System<'a> for Sys {
|
||||
.uid_allocator
|
||||
.retrieve_entity_internal(other.into())
|
||||
{
|
||||
let owner_entity = projectile.owner.and_then(|u| {
|
||||
read_data.uid_allocator.retrieve_entity_internal(u.into())
|
||||
});
|
||||
|
||||
let attacker_info =
|
||||
owner_entity.zip(projectile.owner).map(|(entity, uid)| {
|
||||
AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
energy: read_data.energies.get(entity),
|
||||
combo: read_data.combos.get(entity),
|
||||
}
|
||||
if let (Some(pos), Some(dir)) = (
|
||||
read_data.positions.get(target),
|
||||
Dir::from_unnormalized(vel.0),
|
||||
) {
|
||||
let owner_entity = projectile.owner.and_then(|u| {
|
||||
read_data.uid_allocator.retrieve_entity_internal(u.into())
|
||||
});
|
||||
|
||||
let target_info = TargetInfo {
|
||||
entity: target,
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
};
|
||||
let attacker_info =
|
||||
owner_entity.zip(projectile.owner).map(|(entity, uid)| {
|
||||
AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
energy: read_data.energies.get(entity),
|
||||
combo: read_data.combos.get(entity),
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(&body) = read_data.bodies.get(entity) {
|
||||
outcomes.push(Outcome::ProjectileHit {
|
||||
let target_info = TargetInfo {
|
||||
entity: target,
|
||||
uid: other,
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
body,
|
||||
vel: read_data
|
||||
.velocities
|
||||
.get(entity)
|
||||
.map_or(Vec3::zero(), |v| v.0),
|
||||
source: projectile.owner,
|
||||
target: read_data.uids.get(target).copied(),
|
||||
});
|
||||
}
|
||||
ori: orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
||||
attack.apply_attack(
|
||||
target_group,
|
||||
attacker_info,
|
||||
target_info,
|
||||
ori.look_dir(),
|
||||
false,
|
||||
1.0,
|
||||
|e| server_emitter.emit(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
if let Some(&body) = read_data.bodies.get(entity) {
|
||||
outcomes.push(Outcome::ProjectileHit {
|
||||
pos: pos.0,
|
||||
body,
|
||||
vel: read_data
|
||||
.velocities
|
||||
.get(entity)
|
||||
.map_or(Vec3::zero(), |v| v.0),
|
||||
source: projectile.owner,
|
||||
target: read_data.uids.get(target).copied(),
|
||||
});
|
||||
}
|
||||
|
||||
attack.apply_attack(
|
||||
target_group,
|
||||
attacker_info,
|
||||
target_info,
|
||||
dir,
|
||||
false,
|
||||
1.0,
|
||||
AttackSource::Projectile,
|
||||
|e| server_emitter.emit(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
projectile::Effect::Explode(e) => {
|
||||
@ -213,12 +223,10 @@ impl<'a> System<'a> for Sys {
|
||||
if projectile_vanished {
|
||||
continue 'projectile_loop;
|
||||
}
|
||||
} else if let Some(dir) = read_data
|
||||
.velocities
|
||||
.get(entity)
|
||||
.and_then(|vel| Dir::from_unnormalized(vel.0))
|
||||
{
|
||||
*ori = dir.into();
|
||||
} else if let Some(ori) = orientations.get_mut(entity) {
|
||||
if let Some(dir) = Dir::from_unnormalized(vel.0) {
|
||||
*ori = dir.into();
|
||||
}
|
||||
}
|
||||
|
||||
if projectile.time_left == Duration::default() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use common::{
|
||||
combat::{AttackerInfo, TargetInfo},
|
||||
combat::{AttackSource, AttackerInfo, TargetInfo},
|
||||
comp::{
|
||||
Body, Combo, Energy, Group, Health, HealthSource, Inventory, Ori, PhysicsState, Pos, Scale,
|
||||
Shockwave, ShockwaveHitEntities, Stats,
|
||||
Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, Ori,
|
||||
PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
outcome::Outcome,
|
||||
@ -37,6 +37,7 @@ pub struct ReadData<'a> {
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
combos: ReadStorage<'a, Combo>,
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling accepted inputs like moving or
|
||||
@ -189,10 +190,13 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let target_info = TargetInfo {
|
||||
entity: target,
|
||||
uid: *uid_b,
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
||||
shockwave.properties.attack.apply_attack(
|
||||
@ -202,6 +206,7 @@ impl<'a> System<'a> for Sys {
|
||||
dir,
|
||||
false,
|
||||
1.0,
|
||||
AttackSource::Shockwave,
|
||||
|e| server_emitter.emit(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
|
@ -253,26 +253,11 @@ impl<'a> System<'a> for Sys {
|
||||
energy.get_mut_unchecked().regen_rate = 0.0
|
||||
}
|
||||
},
|
||||
// recover small amount of passive energy from blocking, and bonus energy from
|
||||
// blocking attacks?
|
||||
CharacterState::BasicBlock => {
|
||||
let res = {
|
||||
let energy = energy.get_unchecked();
|
||||
energy.current() < energy.maximum()
|
||||
};
|
||||
|
||||
if res {
|
||||
energy.get_mut_unchecked().change_by(EnergyChange {
|
||||
amount: -3,
|
||||
source: EnergySource::Regen,
|
||||
});
|
||||
}
|
||||
},
|
||||
// Non-combat abilities that consume energy;
|
||||
// temporarily stall energy gain, but preserve regen_rate.
|
||||
// Abilities that temporarily stall energy gain, but preserve regen_rate.
|
||||
CharacterState::Roll { .. }
|
||||
| CharacterState::Climb { .. }
|
||||
| CharacterState::Stunned { .. } => {},
|
||||
| CharacterState::Stunned { .. }
|
||||
| CharacterState::BasicBlock { .. } => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,16 +672,33 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
||||
RadiusEffect::Attack(attack) => {
|
||||
let energies = &ecs.read_storage::<comp::Energy>();
|
||||
let combos = &ecs.read_storage::<comp::Combo>();
|
||||
for (entity_b, pos_b, health_b, inventory_b_maybe, stats_b_maybe, body_b_maybe) in (
|
||||
for (
|
||||
entity_b,
|
||||
pos_b,
|
||||
health_b,
|
||||
(
|
||||
body_b_maybe,
|
||||
inventory_b_maybe,
|
||||
stats_b_maybe,
|
||||
ori_b_maybe,
|
||||
char_state_b_maybe,
|
||||
uid_b,
|
||||
),
|
||||
) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::Pos>(),
|
||||
&ecs.read_storage::<comp::Health>(),
|
||||
ecs.read_storage::<comp::Inventory>().maybe(),
|
||||
ecs.read_storage::<comp::Stats>().maybe(),
|
||||
ecs.read_storage::<comp::Body>().maybe(),
|
||||
(
|
||||
ecs.read_storage::<comp::Body>().maybe(),
|
||||
ecs.read_storage::<comp::Inventory>().maybe(),
|
||||
ecs.read_storage::<comp::Stats>().maybe(),
|
||||
ecs.read_storage::<comp::Ori>().maybe(),
|
||||
ecs.read_storage::<comp::CharacterState>().maybe(),
|
||||
&ecs.read_storage::<Uid>(),
|
||||
),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, _, h, _, _, _)| !h.is_dead)
|
||||
.filter(|(_, _, h, _)| !h.is_dead)
|
||||
{
|
||||
// Check if it is a hit
|
||||
let strength = if let Some(body) = body_b_maybe {
|
||||
@ -721,10 +738,13 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
||||
|
||||
let target_info = combat::TargetInfo {
|
||||
entity: entity_b,
|
||||
uid: *uid_b,
|
||||
inventory: inventory_b_maybe,
|
||||
stats: stats_b_maybe,
|
||||
health: Some(health_b),
|
||||
pos,
|
||||
pos: pos_b.0,
|
||||
ori: ori_b_maybe,
|
||||
char_state: char_state_b_maybe,
|
||||
};
|
||||
|
||||
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
|
||||
@ -736,6 +756,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
||||
dir,
|
||||
false,
|
||||
strength,
|
||||
combat::AttackSource::Explosion,
|
||||
|e| server_eventbus.emit_now(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
|
@ -166,11 +166,13 @@ impl Animation for AlphaAnimation {
|
||||
let moveret2 = move2 * pullback;
|
||||
next.hand_l.position =
|
||||
Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2 + moveret2 * -7.0);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3)
|
||||
* Quaternion::rotation_y(s_a.hhl.4)
|
||||
* Quaternion::rotation_z(s_a.hhl.5);
|
||||
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.hhr.3) * Quaternion::rotation_y(s_a.hhr.4);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3)
|
||||
* Quaternion::rotation_y(s_a.hhr.4)
|
||||
* Quaternion::rotation_z(s_a.hhr.5);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
s_a.hc.0 + moveret1 * -13.0 + moveret2 * 3.0,
|
||||
|
@ -2,12 +2,24 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
CharacterSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::comp::item::ToolKind;
|
||||
use common::{
|
||||
comp::item::{Hands, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct BlockAnimation;
|
||||
|
||||
type BlockAnimationDependency = (
|
||||
(Option<Hands>, Option<Hands>),
|
||||
Option<ToolKind>,
|
||||
Option<ToolKind>,
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
);
|
||||
impl Animation for BlockAnimation {
|
||||
type Dependency = (Option<ToolKind>, Option<ToolKind>, f32);
|
||||
type Dependency = BlockAnimationDependency;
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
@ -16,24 +28,206 @@ impl Animation for BlockAnimation {
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_block")]
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(_active_tool_kind, _second_tool_kind, _global_time): Self::Dependency,
|
||||
_anim_time: f32,
|
||||
_rate: &mut f32,
|
||||
(hands, active_tool_kind, second_tool_kind,velocity, _global_time, stage_section): Self::Dependency,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
next.head.position = Vec3::new(0.0, -1.0 + s_a.head.0, s_a.head.1 + 19.5);
|
||||
next.head.orientation = Quaternion::rotation_x(-0.25);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.hand.0 - 6.0, s_a.hand.1 + 3.5, s_a.hand.2 + 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(-0.3);
|
||||
next.hand_r.position = Vec3::new(s_a.hand.0 - 6.0, s_a.hand.1 + 3.0, s_a.hand.2 - 2.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(-0.3);
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(-0.3);
|
||||
next.main.orientation = Quaternion::rotation_z(0.0);
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.second.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.torso.position = Vec3::new(0.0, -0.2, 0.1) * s_a.scaler;
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let (movement1base, move2, movement3) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0),
|
||||
Some(StageSection::Swing) => (1.0, (anim_time * 10.0).sin(), 0.0),
|
||||
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(4.0)),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - movement3;
|
||||
let move1 = movement1base * pullback;
|
||||
|
||||
if speed > 0.5 {
|
||||
} else {
|
||||
next.chest.position =
|
||||
Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + move1 * -1.0 + move2 * 0.2);
|
||||
next.chest.orientation = Quaternion::rotation_x(move1 * -0.15);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * 0.25);
|
||||
|
||||
next.belt.position = Vec3::new(0.0, s_a.belt.0 + move1 * 0.5, s_a.belt.1 + move1 * 0.5);
|
||||
next.shorts.position =
|
||||
Vec3::new(0.0, s_a.shorts.0 + move1 * 1.3, s_a.shorts.1 + move1 * 1.0);
|
||||
|
||||
next.belt.orientation = Quaternion::rotation_x(move1 * 0.15);
|
||||
next.shorts.orientation = Quaternion::rotation_x(move1 * 0.25);
|
||||
|
||||
next.foot_l.position = Vec3::new(-s_a.foot.0, s_a.foot.1 + move1 * 2.0, s_a.foot.2);
|
||||
next.foot_l.orientation = Quaternion::rotation_z(move1 * -0.5);
|
||||
|
||||
next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1 + move1 * -2.0, s_a.foot.2);
|
||||
next.foot_r.orientation = Quaternion::rotation_x(move1 * -0.5);
|
||||
};
|
||||
|
||||
match (hands, active_tool_kind, second_tool_kind) {
|
||||
((Some(Hands::Two), _), tool, _) | ((None, Some(Hands::Two)), _, tool) => match tool {
|
||||
Some(ToolKind::Sword) | Some(ToolKind::SwordSimple) => {
|
||||
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4);
|
||||
next.hand_r.position = Vec3::new(
|
||||
s_a.shr.0 + move1 * -2.0,
|
||||
s_a.shr.1,
|
||||
s_a.shr.2 + move1 * 20.0,
|
||||
);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.shr.3)
|
||||
* Quaternion::rotation_y(s_a.shr.4)
|
||||
* Quaternion::rotation_z(move1 * 1.5);
|
||||
|
||||
next.control.position =
|
||||
Vec3::new(s_a.sc.0 + move1 * -3.0, s_a.sc.1, s_a.sc.2 + move1 * 4.0);
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.sc.3)
|
||||
* Quaternion::rotation_y(move1 * 1.1)
|
||||
* Quaternion::rotation_z(move1 * 1.7);
|
||||
},
|
||||
|
||||
Some(ToolKind::Axe) => {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4);
|
||||
next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
s_a.ac.0 + move1 * 13.0,
|
||||
s_a.ac.1 + move1 * -3.0,
|
||||
s_a.ac.2 + move1 * 8.0,
|
||||
);
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.ac.3 + move1 * -2.0)
|
||||
* Quaternion::rotation_y(s_a.ac.4 + move1 * -1.8)
|
||||
* Quaternion::rotation_z(s_a.ac.5 + move1 * 4.0);
|
||||
},
|
||||
Some(ToolKind::Hammer) | Some(ToolKind::HammerSimple) | Some(ToolKind::Pick) => {
|
||||
next.hand_l.position =
|
||||
Vec3::new(s_a.hhl.0, s_a.hhl.1 + move1 * 6.0, s_a.hhl.2 + move1 * 6.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3 + move1 * -0.5)
|
||||
* Quaternion::rotation_y(s_a.hhl.4 + move1 * 1.5)
|
||||
* Quaternion::rotation_z(s_a.hhl.5 + move1 * PI);
|
||||
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3)
|
||||
* Quaternion::rotation_y(s_a.hhr.4)
|
||||
* Quaternion::rotation_z(s_a.hhr.5);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
s_a.hc.0 + move1 * 3.0,
|
||||
s_a.hc.1 + move1 * 3.0,
|
||||
s_a.hc.2 + move1 * 10.0,
|
||||
);
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.hc.3)
|
||||
* Quaternion::rotation_y(s_a.hc.4)
|
||||
* Quaternion::rotation_z(s_a.hc.5 + move1 * -1.0);
|
||||
},
|
||||
Some(ToolKind::Staff) | Some(ToolKind::Sceptre) => {
|
||||
next.hand_r.position = Vec3::new(s_a.sthr.0, s_a.sthr.1, s_a.sthr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.sthr.3) * Quaternion::rotation_y(s_a.sthr.4);
|
||||
|
||||
next.control.position = Vec3::new(s_a.stc.0, s_a.stc.1, s_a.stc.2);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.sthl.0, s_a.sthl.1, s_a.sthl.2);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.sthl.3);
|
||||
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.stc.3)
|
||||
* Quaternion::rotation_y(s_a.stc.4)
|
||||
* Quaternion::rotation_z(s_a.stc.5);
|
||||
},
|
||||
Some(ToolKind::Bow) => {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_l.position = Vec3::new(s_a.bhl.0, s_a.bhl.1, s_a.bhl.2);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.bhl.3);
|
||||
next.hand_r.position = Vec3::new(s_a.bhr.0, s_a.bhr.1, s_a.bhr.2);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.bhr.3);
|
||||
|
||||
next.hold.position = Vec3::new(0.0, -1.0, -5.2);
|
||||
next.hold.orientation = Quaternion::rotation_x(-1.57);
|
||||
next.hold.scale = Vec3::one() * 1.0;
|
||||
|
||||
next.control.position = Vec3::new(s_a.bc.0, s_a.bc.1, s_a.bc.2);
|
||||
next.control.orientation = Quaternion::rotation_x(0.0)
|
||||
* Quaternion::rotation_y(s_a.bc.4)
|
||||
* Quaternion::rotation_z(s_a.bc.5);
|
||||
},
|
||||
Some(ToolKind::Debug) => {
|
||||
next.hand_l.position = Vec3::new(-7.0, 4.0, 3.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(1.27);
|
||||
next.main.position = Vec3::new(-5.0, 5.0, 23.0);
|
||||
next.main.orientation = Quaternion::rotation_x(PI);
|
||||
},
|
||||
Some(ToolKind::Farming) => {
|
||||
next.hand_l.position = Vec3::new(9.0, 1.0, 1.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(1.57);
|
||||
next.hand_r.position = Vec3::new(9.0, 1.0, 11.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(1.57);
|
||||
next.main.position = Vec3::new(7.5, 7.5, 13.2);
|
||||
next.main.orientation = Quaternion::rotation_y(PI);
|
||||
|
||||
next.control.position = Vec3::new(-11.0, 1.8, 4.0);
|
||||
next.control.orientation = Quaternion::rotation_x(0.0)
|
||||
* Quaternion::rotation_y(0.6)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
((_, _), _, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(Some(Hands::One), _) => {
|
||||
next.control_l.position = Vec3::new(-7.0, 8.0 + move1 * 3.0, 2.0 + move1 * 3.0);
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(move1 * 1.0);
|
||||
next.hand_l.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(1.57)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None | Some(Hands::One), Some(Hands::One)) => {
|
||||
next.control_r.position = Vec3::new(7.0, 8.0 + move1 * 3.0, 2.0 + move1 * 3.0);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(move1 * -1.0);
|
||||
next.hand_r.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(1.57)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None, None) | (None, Some(Hands::One)) => {
|
||||
next.hand_l.position = Vec3::new(-4.5, 8.0, 5.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(1.9) * Quaternion::rotation_y(-0.5)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None, None) | (Some(Hands::One), None) => {
|
||||
next.hand_r.position = Vec3::new(4.5, 8.0, 5.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(1.9) * Quaternion::rotation_y(0.5)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
|
||||
if let (None, Some(Hands::Two)) = hands {
|
||||
next.second = next.main;
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
|
@ -87,11 +87,13 @@ impl Animation for ChargeswingAnimation {
|
||||
Some(ToolKind::Hammer) | Some(ToolKind::HammerSimple) => {
|
||||
next.hand_l.position =
|
||||
Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2 + (move2 * -8.0));
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3)
|
||||
* Quaternion::rotation_y(s_a.hhl.4)
|
||||
* Quaternion::rotation_z(s_a.hhl.5);
|
||||
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.hhr.3) * Quaternion::rotation_y(s_a.hhr.4);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3)
|
||||
* Quaternion::rotation_y(s_a.hhr.4)
|
||||
* Quaternion::rotation_z(s_a.hhr.5);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
s_a.hc.0 + (move1 * -2.0 + move2 * -8.0),
|
||||
|
@ -119,9 +119,11 @@ impl Animation for LeapAnimation {
|
||||
match ability_info.and_then(|a| a.tool) {
|
||||
Some(ToolKind::Hammer) => {
|
||||
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_z(s_a.hhl.5);
|
||||
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.hhr.3) * Quaternion::rotation_z(s_a.hhr.5);
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation =
|
||||
Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0);
|
||||
|
@ -291,10 +291,10 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
_ => (-7.0, 7.0, 2.0, -0.1, 0.0, 0.0),
|
||||
},
|
||||
hhl: match (body.species, body.body_type) {
|
||||
_ => (-0.5, -1.0, 10.0, 4.71, 0.0, 0.0),
|
||||
_ => (0.1, 0.0, 11.0, 4.71, 0.0, PI),
|
||||
},
|
||||
hhr: match (body.species, body.body_type) {
|
||||
_ => (0.0, 0.0, 0.0, 4.71, 0.0, 0.0),
|
||||
_ => (0.0, 0.0, 0.0, 4.71, 0.0, PI),
|
||||
},
|
||||
hc: match (body.species, body.body_type) {
|
||||
_ => (6.0, 7.0, 1.0, -0.3, -1.57, 3.64),
|
||||
|
@ -106,10 +106,12 @@ impl Animation for StaggeredAnimation {
|
||||
Some(ToolKind::Hammer | ToolKind::Pick) => {
|
||||
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3)
|
||||
* Quaternion::rotation_y(s_a.hhl.4);
|
||||
* Quaternion::rotation_y(s_a.hhl.4)
|
||||
* Quaternion::rotation_z(s_a.hhl.5);
|
||||
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3)
|
||||
* Quaternion::rotation_y(s_a.hhr.4);
|
||||
* Quaternion::rotation_y(s_a.hhr.4)
|
||||
* Quaternion::rotation_z(s_a.hhr.5);
|
||||
|
||||
next.control.position = Vec3::new(s_a.hc.0, s_a.hc.1, s_a.hc.2);
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.hc.3)
|
||||
|
@ -101,11 +101,13 @@ impl Animation for StunnedAnimation {
|
||||
},
|
||||
Some(ToolKind::Hammer | ToolKind::Pick) => {
|
||||
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3)
|
||||
* Quaternion::rotation_y(s_a.hhl.4)
|
||||
* Quaternion::rotation_z(s_a.hhl.5);
|
||||
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.hhr.3) * Quaternion::rotation_y(s_a.hhr.4);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3)
|
||||
* Quaternion::rotation_y(s_a.hhr.4)
|
||||
* Quaternion::rotation_z(s_a.hhr.5);
|
||||
|
||||
next.control.position = Vec3::new(s_a.hc.0, s_a.hc.1, s_a.hc.2);
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.hc.3)
|
||||
|
@ -166,19 +166,6 @@ impl Animation for WieldAnimation {
|
||||
next.control.orientation = Quaternion::rotation_x(s_a.sc.3 + u_slow * 0.15)
|
||||
* Quaternion::rotation_z(u_slowalt * 0.08);
|
||||
},
|
||||
Some(ToolKind::Dagger) => {
|
||||
next.control.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.control_l.position = Vec3::new(-7.0, 0.0, 0.0);
|
||||
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.control_r.position = Vec3::new(7.0, 0.0, 0.0);
|
||||
},
|
||||
Some(ToolKind::Axe) => {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
@ -215,11 +202,13 @@ impl Animation for WieldAnimation {
|
||||
},
|
||||
Some(ToolKind::Hammer) | Some(ToolKind::HammerSimple) | Some(ToolKind::Pick) => {
|
||||
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3)
|
||||
* Quaternion::rotation_y(s_a.hhl.4)
|
||||
* Quaternion::rotation_z(s_a.hhl.5);
|
||||
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.hhr.3) * Quaternion::rotation_y(s_a.hhr.4);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3)
|
||||
* Quaternion::rotation_y(s_a.hhr.4)
|
||||
* Quaternion::rotation_z(s_a.hhr.5);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
s_a.hc.0,
|
||||
|
@ -407,9 +407,6 @@ impl SfxMgr {
|
||||
let file_ref = "voxygen.audio.sfx.footsteps.stone_step_1";
|
||||
audio.play_sfx(file_ref, pos.map(|e| e as f32 + 0.5), Some(3.0));
|
||||
},
|
||||
Outcome::ExpChange { .. }
|
||||
| Outcome::ComboChange { .. }
|
||||
| Outcome::SummonedCreature { .. } => {},
|
||||
Outcome::Damage { pos, .. } => {
|
||||
let file_ref = vec![
|
||||
"voxygen.audio.sfx.character.hit_1",
|
||||
@ -419,6 +416,27 @@ impl SfxMgr {
|
||||
][rand::thread_rng().gen_range(1..4)];
|
||||
audio.play_sfx(file_ref, *pos, None);
|
||||
},
|
||||
Outcome::Block { pos, parry, .. } => {
|
||||
let block_sfx = vec![
|
||||
"voxygen.audio.sfx.character.block_1",
|
||||
"voxygen.audio.sfx.character.block_2",
|
||||
"voxygen.audio.sfx.character.block_3",
|
||||
];
|
||||
let parry_sfx = vec![
|
||||
"voxygen.audio.sfx.character.parry_1",
|
||||
"voxygen.audio.sfx.character.parry_2",
|
||||
];
|
||||
if *parry {
|
||||
let file_ref = parry_sfx[rand::thread_rng().gen_range(1..parry_sfx.len())];
|
||||
audio.play_sfx(file_ref, *pos, Some(2.0));
|
||||
} else {
|
||||
let file_ref = block_sfx[rand::thread_rng().gen_range(1..block_sfx.len())];
|
||||
audio.play_sfx(file_ref, *pos, Some(2.0));
|
||||
}
|
||||
},
|
||||
Outcome::ExpChange { .. }
|
||||
| Outcome::ComboChange { .. }
|
||||
| Outcome::SummonedCreature { .. } => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,6 +329,11 @@ pub struct ComboFloater {
|
||||
pub timer: f64,
|
||||
}
|
||||
|
||||
pub struct BlockFloater {
|
||||
pub owner: Uid,
|
||||
pub timer: f32,
|
||||
}
|
||||
|
||||
pub struct DebugInfo {
|
||||
pub tps: f64,
|
||||
pub frame_time: Duration,
|
||||
@ -739,6 +744,13 @@ impl PromptDialogSettings {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Floaters {
|
||||
pub exp_floaters: Vec<ExpFloater>,
|
||||
pub skill_point_displays: Vec<SkillPointGain>,
|
||||
pub combo_floaters: VecDeque<ComboFloater>,
|
||||
pub block_floaters: Vec<BlockFloater>,
|
||||
}
|
||||
|
||||
pub struct Hud {
|
||||
ui: Ui,
|
||||
ids: Ids,
|
||||
@ -765,9 +777,7 @@ pub struct Hud {
|
||||
hotbar: hotbar::State,
|
||||
events: Vec<Event>,
|
||||
crosshair_opacity: f32,
|
||||
exp_floaters: Vec<ExpFloater>,
|
||||
skill_point_displays: Vec<SkillPointGain>,
|
||||
combo_floaters: VecDeque<ComboFloater>,
|
||||
floaters: Floaters,
|
||||
}
|
||||
|
||||
impl Hud {
|
||||
@ -878,9 +888,12 @@ impl Hud {
|
||||
hotbar: hotbar_state,
|
||||
events: Vec::new(),
|
||||
crosshair_opacity: 0.0,
|
||||
exp_floaters: Vec::new(),
|
||||
skill_point_displays: Vec::new(),
|
||||
combo_floaters: VecDeque::new(),
|
||||
floaters: Floaters {
|
||||
exp_floaters: Vec::new(),
|
||||
skill_point_displays: Vec::new(),
|
||||
combo_floaters: VecDeque::new(),
|
||||
block_floaters: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1174,9 +1187,18 @@ impl Hud {
|
||||
}
|
||||
}
|
||||
// EXP Numbers
|
||||
self.exp_floaters.retain(|f| f.timer > 0_f32);
|
||||
self.floaters
|
||||
.exp_floaters
|
||||
.iter_mut()
|
||||
.for_each(|f| f.timer -= dt.as_secs_f32());
|
||||
self.floaters.exp_floaters.retain(|f| f.timer > 0_f32);
|
||||
if let Some(uid) = uids.get(me) {
|
||||
for floater in self.exp_floaters.iter_mut().filter(|f| f.owner == *uid) {
|
||||
for floater in self
|
||||
.floaters
|
||||
.exp_floaters
|
||||
.iter_mut()
|
||||
.filter(|f| f.owner == *uid)
|
||||
{
|
||||
let number_speed = 50.0; // Number Speed for Single EXP
|
||||
let player_sct_bg_id = player_sct_bg_id_walker.next(
|
||||
&mut self.ids.player_sct_bgs,
|
||||
@ -1221,13 +1243,19 @@ impl Hud {
|
||||
)
|
||||
.set(player_sct_id, ui_widgets);
|
||||
}
|
||||
floater.timer -= dt.as_secs_f32();
|
||||
}
|
||||
}
|
||||
// Skill points
|
||||
self.skill_point_displays.retain(|d| d.timer > 0_f32);
|
||||
self.floaters
|
||||
.skill_point_displays
|
||||
.iter_mut()
|
||||
.for_each(|f| f.timer -= dt.as_secs_f32());
|
||||
self.floaters
|
||||
.skill_point_displays
|
||||
.retain(|d| d.timer > 0_f32);
|
||||
if let Some(uid) = uids.get(me) {
|
||||
if let Some(display) = self
|
||||
.floaters
|
||||
.skill_point_displays
|
||||
.iter_mut()
|
||||
.find(|d| d.owner == *uid)
|
||||
@ -1311,8 +1339,54 @@ impl Hud {
|
||||
.left_from(self.ids.player_rank_up_txt_1_bg, 5.0)
|
||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||
.set(self.ids.player_rank_up_icon, ui_widgets);
|
||||
}
|
||||
}
|
||||
// Scrolling Combat Text for Parrying an attack
|
||||
self.floaters
|
||||
.block_floaters
|
||||
.iter_mut()
|
||||
.for_each(|f| f.timer -= dt.as_secs_f32());
|
||||
self.floaters.block_floaters.retain(|f| f.timer > 0_f32);
|
||||
if let Some(uid) = uids.get(me) {
|
||||
for floater in self
|
||||
.floaters
|
||||
.block_floaters
|
||||
.iter_mut()
|
||||
.filter(|f| f.owner == *uid)
|
||||
{
|
||||
let number_speed = 50.0;
|
||||
let player_sct_bg_id = player_sct_bg_id_walker.next(
|
||||
&mut self.ids.player_sct_bgs,
|
||||
&mut ui_widgets.widget_id_generator(),
|
||||
);
|
||||
let player_sct_id = player_sct_id_walker.next(
|
||||
&mut self.ids.player_scts,
|
||||
&mut ui_widgets.widget_id_generator(),
|
||||
);
|
||||
let font_size = 30;
|
||||
let y = floater.timer as f64 * number_speed; // Timer sets the widget offset
|
||||
// text transparency
|
||||
let fade = if floater.timer < 0.25 {
|
||||
floater.timer as f32 / 0.25
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
display.timer -= dt.as_secs_f32();
|
||||
Text::new(&i18n.get("hud.sct.block"))
|
||||
.font_size(font_size)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||
.x_y(
|
||||
ui_widgets.win_w * (0.0),
|
||||
ui_widgets.win_h * (-0.3) + y - 3.0,
|
||||
)
|
||||
.set(player_sct_bg_id, ui_widgets);
|
||||
Text::new(&i18n.get("hud.sct.block"))
|
||||
.font_size(font_size)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.color(Color::Rgba(0.69, 0.82, 0.88, fade))
|
||||
.x_y(ui_widgets.win_w * 0.0, ui_widgets.win_h * -0.3 + y)
|
||||
.set(player_sct_id, ui_widgets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2273,12 +2347,14 @@ impl Hud {
|
||||
let ability_map = ecs.fetch::<comp::item::tool::AbilityMap>();
|
||||
let bodies = ecs.read_storage::<comp::Body>();
|
||||
// Combo floater stuffs
|
||||
for combo_floater in self.combo_floaters.iter_mut() {
|
||||
combo_floater.timer -= dt.as_secs_f64();
|
||||
}
|
||||
self.combo_floaters.retain(|f| f.timer > 0_f64);
|
||||
self.floaters
|
||||
.combo_floaters
|
||||
.iter_mut()
|
||||
.for_each(|f| f.timer -= dt.as_secs_f64());
|
||||
self.floaters.combo_floaters.retain(|f| f.timer > 0_f64);
|
||||
let combo = if let Some(uid) = ecs.read_storage::<Uid>().get(entity) {
|
||||
self.combo_floaters
|
||||
self.floaters
|
||||
.combo_floaters
|
||||
.iter()
|
||||
.find(|c| c.owner == *uid)
|
||||
.copied()
|
||||
@ -3425,7 +3501,7 @@ impl Hud {
|
||||
|
||||
pub fn handle_outcome(&mut self, outcome: &Outcome) {
|
||||
match outcome {
|
||||
Outcome::ExpChange { uid, exp } => self.exp_floaters.push(ExpFloater {
|
||||
Outcome::ExpChange { uid, exp } => self.floaters.exp_floaters.push(ExpFloater {
|
||||
owner: *uid,
|
||||
exp_change: *exp,
|
||||
timer: 4.0,
|
||||
@ -3436,17 +3512,25 @@ impl Hud {
|
||||
skill_tree,
|
||||
total_points,
|
||||
..
|
||||
} => self.skill_point_displays.push(SkillPointGain {
|
||||
} => self.floaters.skill_point_displays.push(SkillPointGain {
|
||||
owner: *uid,
|
||||
skill_tree: *skill_tree,
|
||||
total_points: *total_points,
|
||||
timer: 5.0,
|
||||
}),
|
||||
Outcome::ComboChange { uid, combo } => self.combo_floaters.push_front(ComboFloater {
|
||||
owner: *uid,
|
||||
combo: *combo,
|
||||
timer: comp::combo::COMBO_DECAY_START,
|
||||
}),
|
||||
Outcome::ComboChange { uid, combo } => {
|
||||
self.floaters.combo_floaters.push_front(ComboFloater {
|
||||
owner: *uid,
|
||||
combo: *combo,
|
||||
timer: comp::combo::COMBO_DECAY_START,
|
||||
})
|
||||
},
|
||||
Outcome::Block { uid, parry, .. } if *parry => {
|
||||
self.floaters.block_floaters.push(BlockFloater {
|
||||
owner: *uid,
|
||||
timer: 1.0,
|
||||
})
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -3568,8 +3652,8 @@ pub fn get_buff_desc(buff: BuffKind, localized_strings: &Localization) -> &str {
|
||||
|
||||
pub fn get_buff_time(buff: BuffInfo) -> String {
|
||||
if let Some(dur) = buff.dur {
|
||||
format!("Remaining: {:.0}s", dur.as_secs_f32())
|
||||
format!("{:.0}s", dur.as_secs_f32())
|
||||
} else {
|
||||
"Permanent".to_string()
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
|
@ -1385,11 +1385,29 @@ impl FigureMgr {
|
||||
),
|
||||
}
|
||||
},
|
||||
CharacterState::BasicBlock { .. } => {
|
||||
CharacterState::BasicBlock(s) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Buildup => {
|
||||
stage_time / s.static_data.buildup_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Swing => stage_time,
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f32()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
anim::character::BlockAnimation::update_skeleton(
|
||||
&CharacterSkeleton::new(holding_lantern),
|
||||
(active_tool_kind, second_tool_kind, time),
|
||||
state.state_time,
|
||||
&target_base,
|
||||
(
|
||||
hands,
|
||||
active_tool_kind,
|
||||
second_tool_kind,
|
||||
rel_vel,
|
||||
time,
|
||||
Some(s.stage_section),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
|
@ -203,6 +203,18 @@ impl ParticleMgr {
|
||||
});
|
||||
}
|
||||
},
|
||||
Outcome::Block { pos, parry, .. } => {
|
||||
if *parry {
|
||||
self.particles.resize_with(self.particles.len() + 10, || {
|
||||
Particle::new(
|
||||
Duration::from_millis(200),
|
||||
time,
|
||||
ParticleMode::GunPowderSpark,
|
||||
*pos + Vec3::unit_z(),
|
||||
)
|
||||
});
|
||||
}
|
||||
},
|
||||
Outcome::ProjectileShot { .. }
|
||||
| Outcome::Beam { .. }
|
||||
| Outcome::ExpChange { .. }
|
||||
|
@ -418,6 +418,15 @@ impl PlayState for SessionState {
|
||||
);
|
||||
}
|
||||
},
|
||||
GameInput::Block => {
|
||||
let mut client = self.client.borrow_mut();
|
||||
client.handle_input(
|
||||
InputKind::Block,
|
||||
state,
|
||||
select_pos,
|
||||
target_entity.map(|t| t.0),
|
||||
);
|
||||
},
|
||||
GameInput::Roll => {
|
||||
let mut client = self.client.borrow_mut();
|
||||
if can_build {
|
||||
|
@ -111,6 +111,7 @@ impl ControlSettings {
|
||||
match game_input {
|
||||
GameInput::Primary => KeyMouse::Mouse(MouseButton::Left),
|
||||
GameInput::Secondary => KeyMouse::Mouse(MouseButton::Right),
|
||||
GameInput::Block => KeyMouse::Key(VirtualKeyCode::LAlt),
|
||||
GameInput::ToggleCursor => KeyMouse::Key(VirtualKeyCode::Comma),
|
||||
GameInput::Escape => KeyMouse::Key(VirtualKeyCode::Escape),
|
||||
GameInput::Chat => KeyMouse::Key(VirtualKeyCode::Return),
|
||||
|
@ -21,6 +21,7 @@ use winit::monitor::VideoMode;
|
||||
pub enum GameInput {
|
||||
Primary,
|
||||
Secondary,
|
||||
Block,
|
||||
Slot1,
|
||||
Slot2,
|
||||
Slot3,
|
||||
@ -85,6 +86,7 @@ impl GameInput {
|
||||
match *self {
|
||||
GameInput::Primary => "gameinput.primary",
|
||||
GameInput::Secondary => "gameinput.secondary",
|
||||
GameInput::Block => "gameinput.block",
|
||||
GameInput::ToggleCursor => "gameinput.togglecursor",
|
||||
GameInput::MoveForward => "gameinput.moveforward",
|
||||
GameInput::MoveLeft => "gameinput.moveleft",
|
||||
@ -149,6 +151,7 @@ impl GameInput {
|
||||
[
|
||||
GameInput::Primary,
|
||||
GameInput::Secondary,
|
||||
GameInput::Block,
|
||||
GameInput::ToggleCursor,
|
||||
GameInput::MoveForward,
|
||||
GameInput::MoveLeft,
|
||||
|
Loading…
Reference in New Issue
Block a user