mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Melee weapons can now block.
This commit is contained in:
parent
3cfc94af45
commit
91c6288213
@ -1 +1,6 @@
|
||||
BasicBlock
|
||||
BasicBlock(
|
||||
buildup_duration: 0.1,
|
||||
recover_duration: 0.1,
|
||||
max_angle: 90.0,
|
||||
block_strength: 0.8,
|
||||
)
|
@ -12,8 +12,8 @@ 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,
|
||||
@ -39,6 +39,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> {
|
||||
@ -55,6 +64,8 @@ pub struct TargetInfo<'a> {
|
||||
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 +116,28 @@ impl Attack {
|
||||
|
||||
pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> { self.effects.iter() }
|
||||
|
||||
pub fn compute_damage_reduction(target: &TargetInfo, source: AttackSource, dir: Dir) -> 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()
|
||||
{
|
||||
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 +148,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 +160,7 @@ 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);
|
||||
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,12 @@ pub enum CharacterAbility {
|
||||
charge_through: bool,
|
||||
is_interruptible: bool,
|
||||
},
|
||||
BasicBlock,
|
||||
BasicBlock {
|
||||
buildup_duration: f32,
|
||||
recover_duration: f32,
|
||||
max_angle: f32,
|
||||
block_strength: f32,
|
||||
},
|
||||
Roll {
|
||||
energy_cost: f32,
|
||||
buildup_duration: f32,
|
||||
@ -341,6 +346,15 @@ impl CharacterAbility {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_block() -> CharacterAbility {
|
||||
CharacterAbility::BasicBlock {
|
||||
buildup_duration: 0.1,
|
||||
recover_duration: 0.1,
|
||||
max_angle: 60.0,
|
||||
block_strength: 0.5,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adjusted_by_stats(mut self, power: f32, poise_strength: f32, speed: f32) -> Self {
|
||||
use CharacterAbility::*;
|
||||
match self {
|
||||
@ -408,7 +422,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,
|
||||
@ -586,7 +608,11 @@ impl CharacterAbility {
|
||||
0
|
||||
}
|
||||
},
|
||||
BasicBlock | Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0,
|
||||
BasicBlock { .. }
|
||||
| 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,
|
||||
} => 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,
|
||||
parry: false,
|
||||
}),
|
||||
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,10 +149,11 @@ 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 {
|
||||
|
@ -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)]
|
||||
|
@ -1,15 +1,38 @@
|
||||
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,
|
||||
/// Whether the block was cancelled early enough to become a parry
|
||||
pub parry: bool,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
@ -17,6 +40,72 @@ 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(),
|
||||
parry: !input_is_pressed(data, InputKind::Block),
|
||||
..*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
|
||||
}
|
||||
}
|
||||
|
@ -579,7 +579,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 +615,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 +627,21 @@ 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());
|
||||
if input_is_pressed(data, InputKind::Block) && can_block(EquipSlot::Mainhand) {
|
||||
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) {
|
||||
|
@ -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
|
||||
@ -188,6 +189,8 @@ impl<'a> System<'a> for Sys {
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
||||
beam_segment.properties.attack.apply_attack(
|
||||
@ -197,6 +200,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,
|
||||
@ -147,6 +147,8 @@ impl<'a> System<'a> for Sys {
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.char_states.get(target),
|
||||
};
|
||||
|
||||
melee_attack.attack.apply_attack(
|
||||
@ -156,6 +158,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
|
||||
@ -130,6 +131,10 @@ impl<'a> System<'a> for Sys {
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
// TODO: Let someone smarter figure this out
|
||||
// ori: orientations.get(target),
|
||||
ori: None,
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
||||
if let Some(&body) = read_data.bodies.get(entity) {
|
||||
@ -152,6 +157,7 @@ impl<'a> System<'a> for Sys {
|
||||
ori.look_dir(),
|
||||
false,
|
||||
1.0,
|
||||
AttackSource::Projectile,
|
||||
|e| server_emitter.emit(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
|
@ -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
|
||||
@ -193,6 +194,8 @@ impl<'a> System<'a> for Sys {
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
||||
shockwave.properties.attack.apply_attack(
|
||||
@ -202,6 +205,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,31 @@ 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,
|
||||
),
|
||||
) 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(),
|
||||
),
|
||||
)
|
||||
.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 {
|
||||
@ -725,6 +740,8 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
||||
stats: stats_b_maybe,
|
||||
health: Some(health_b),
|
||||
pos,
|
||||
ori: ori_b_maybe,
|
||||
char_state: char_state_b_maybe,
|
||||
};
|
||||
|
||||
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
|
||||
@ -736,6 +753,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),
|
||||
);
|
||||
|
@ -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