Readd ranged and debug boost. Add bouncing while running in first person

This commit is contained in:
timokoesters 2020-03-16 12:32:57 +01:00
parent 1e9f081f10
commit 87acc01d48
29 changed files with 336 additions and 105 deletions

View File

@ -393,11 +393,11 @@ impl Client {
{
if last_character_states
.get(entity)
.map(|&l| !client_character_state.equals(&l.0))
.map(|l| !client_character_state.equals(&l.0))
.unwrap_or(true)
{
let _ = last_character_states
.insert(entity, comp::Last(*client_character_state));
.insert(entity, comp::Last(client_character_state.clone()));
}
}
}

View File

@ -1,17 +1,27 @@
use crate::{
comp::{CharacterState, Item, ToolData},
comp::{Body, CharacterState, Item, Projectile, ToolData},
states::*,
};
use specs::{Component, DenseVecStorage, FlaggedStorage, HashMapStorage};
use std::time::Duration;
use vek::vec::Vec3;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum CharacterAbility {
BasicAttack {
BasicMelee {
buildup_duration: Duration,
recover_duration: Duration,
base_damage: u32,
},
BasicRanged {
recover_duration: Duration,
projectile: Projectile,
projectile_body: Body,
},
Boost {
duration: Duration,
only_up: bool,
},
BasicBlock,
Roll,
ChargeAttack,
@ -26,7 +36,7 @@ impl Component for CharacterAbility {
type Storage = DenseVecStorage<Self>;
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct ItemConfig {
pub item: Item,
pub primary_ability: Option<CharacterAbility>,
@ -35,7 +45,7 @@ pub struct ItemConfig {
pub dodge_ability: Option<CharacterAbility>,
}
#[derive(Clone, PartialEq, Eq, Hash, Default, Debug, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)]
pub struct Loadout {
pub active_item: Option<ItemConfig>,
pub second_item: Option<ItemConfig>,
@ -48,39 +58,52 @@ pub struct Loadout {
pub foot: Option<Item>,
}
impl From<CharacterAbility> for CharacterState {
fn from(ability: CharacterAbility) -> Self {
impl From<&CharacterAbility> for CharacterState {
fn from(ability: &CharacterAbility) -> Self {
match ability {
CharacterAbility::BasicAttack {
CharacterAbility::BasicMelee {
buildup_duration,
recover_duration,
base_damage,
} => CharacterState::BasicAttack(basic_attack::Data {
} => CharacterState::BasicMelee(basic_melee::Data {
exhausted: false,
buildup_duration,
recover_duration,
base_damage,
buildup_duration: *buildup_duration,
recover_duration: *recover_duration,
base_damage: *base_damage,
}),
CharacterAbility::BasicBlock { .. } => CharacterState::BasicBlock,
CharacterAbility::Roll { .. } => CharacterState::Roll(roll::Data {
CharacterAbility::BasicRanged {
recover_duration,
projectile,
projectile_body,
} => CharacterState::BasicRanged(basic_ranged::Data {
exhausted: false,
prepare_timer: Duration::default(),
recover_duration: *recover_duration,
projectile: projectile.clone(),
projectile_body: *projectile_body,
}),
CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data {
duration: *duration,
only_up: *only_up,
}),
CharacterAbility::BasicBlock => CharacterState::BasicBlock,
CharacterAbility::Roll => CharacterState::Roll(roll::Data {
remaining_duration: Duration::from_millis(600),
}),
CharacterAbility::ChargeAttack => CharacterState::ChargeAttack(charge_attack::Data {
remaining_duration: Duration::from_millis(600),
}),
CharacterAbility::ChargeAttack { .. } => {
CharacterState::ChargeAttack(charge_attack::Data {
remaining_duration: Duration::from_millis(600),
})
},
CharacterAbility::TimedCombo {
buildup_duration,
recover_duration,
base_damage,
} => CharacterState::TimedCombo(timed_combo::Data {
buildup_duration,
recover_duration,
buildup_duration: *buildup_duration,
recover_duration: *recover_duration,
stage: 0,
stage_exhausted: false,
stage_time_active: Duration::default(),
base_damage,
base_damage: *base_damage,
}),
}
}

View File

@ -18,7 +18,7 @@ pub struct StateUpdate {
pub server_events: VecDeque<ServerEvent>,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum CharacterState {
Idle,
Climb,
@ -34,8 +34,12 @@ pub enum CharacterState {
ChargeAttack(charge_attack::Data),
/// A dodge where player can roll
Roll(roll::Data),
/// A basic attacking state
BasicAttack(basic_attack::Data),
/// A basic melee attack (e.g. sword)
BasicMelee(basic_melee::Data),
/// A basic ranged attack (e.g. bow)
BasicRanged(basic_ranged::Data),
/// A force will boost you into a direction for some duration
Boost(boost::Data),
/// A three-stage attack where play must click at appropriate times
/// to continue attack chain.
TimedCombo(timed_combo::Data),
@ -48,7 +52,7 @@ impl CharacterState {
pub fn is_wield(&self) -> bool {
match self {
CharacterState::Wielding
| CharacterState::BasicAttack(_)
| CharacterState::BasicMelee(_)
| CharacterState::TimedCombo(_)
| CharacterState::BasicBlock => true,
_ => false,
@ -57,7 +61,8 @@ impl CharacterState {
pub fn is_attack(&self) -> bool {
match self {
CharacterState::BasicAttack(_)
CharacterState::BasicMelee(_)
| CharacterState::BasicRanged(_)
| CharacterState::TimedCombo(_)
| CharacterState::ChargeAttack(_) => true,
_ => false,

View File

@ -1,6 +1,9 @@
use crate::{
assets::{self, Asset},
comp::{body::humanoid, CharacterAbility},
comp::{
body::{humanoid, object},
projectile, Body, CharacterAbility, HealthChange, HealthSource, Projectile,
},
effect::Effect,
terrain::{Block, BlockKind},
};
@ -37,35 +40,61 @@ impl ToolData {
use ToolKind::*;
match self.kind {
Sword(_) => vec![BasicAttack {
Sword(_) => vec![BasicMelee {
buildup_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(500),
base_damage: 60,
}],
Axe => vec![BasicAttack {
Axe => vec![BasicMelee {
buildup_duration: Duration::from_millis(700),
recover_duration: Duration::from_millis(100),
base_damage: 80,
}],
Hammer => vec![BasicAttack {
Hammer => vec![BasicMelee {
buildup_duration: Duration::from_millis(700),
recover_duration: Duration::from_millis(300),
base_damage: 100,
}],
Bow => vec![],
Dagger => vec![BasicAttack {
Bow => vec![BasicRanged {
projectile: Projectile {
hit_ground: vec![projectile::Effect::Stick],
hit_wall: vec![projectile::Effect::Stick],
hit_entity: vec![
projectile::Effect::Damage(HealthChange {
// TODO: This should not be fixed (?)
amount: -30,
cause: HealthSource::Item,
}),
projectile::Effect::Vanish,
],
time_left: Duration::from_secs(15),
owner: None,
},
projectile_body: Body::Object(object::Body::Arrow),
recover_duration: Duration::from_millis(300),
}],
Dagger => vec![BasicMelee {
buildup_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(400),
base_damage: 50,
}],
Staff => vec![BasicAttack {
Staff => vec![BasicMelee {
buildup_duration: Duration::from_millis(400),
recover_duration: Duration::from_millis(300),
base_damage: 70,
}],
Shield => vec![],
Debug(kind) => match kind {
Boost => vec![],
DebugKind::Boost => vec![
CharacterAbility::Boost {
duration: Duration::from_millis(100),
only_up: false,
},
CharacterAbility::Boost {
duration: Duration::from_millis(100),
only_up: true,
},
],
Possess => vec![],
},
}

View File

@ -3,7 +3,7 @@ use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
use std::time::Duration;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Effect {
Damage(comp::HealthChange),
Vanish,
@ -11,15 +11,15 @@ pub enum Effect {
Possess,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Projectile {
pub owner: Uid,
// TODO: use SmallVec for these effects
pub hit_ground: Vec<Effect>,
pub hit_wall: Vec<Effect>,
pub hit_entity: Vec<Effect>,
/// Time left until the projectile will despawn
pub time_left: Duration,
pub owner: Option<Uid>,
}
impl Component for Projectile {

View File

@ -18,7 +18,7 @@ impl CharacterBehavior for Data {
vel: *data.vel,
ori: *data.ori,
energy: *data.energy,
character: *data.character,
character: data.character.clone(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
};

View File

@ -1,6 +1,6 @@
use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::{utils::*, wielding},
states::utils::*,
sys::character_behavior::*,
};
use std::{collections::VecDeque, time::Duration};
@ -24,16 +24,16 @@ impl CharacterBehavior for Data {
vel: *data.vel,
ori: *data.ori,
energy: *data.energy,
character: *data.character,
character: data.character.clone(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
};
handle_move(data, &mut update);
// Build up
if self.buildup_duration != Duration::default() {
update.character = CharacterState::BasicAttack(Data {
// Build up
update.character = CharacterState::BasicMelee(Data {
buildup_duration: self
.buildup_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
@ -42,9 +42,8 @@ impl CharacterBehavior for Data {
base_damage: self.base_damage,
exhausted: false,
});
}
// Hit attempt
else if !self.exhausted {
} else if !self.exhausted {
// Hit attempt
if let Some(tool) = unwrap_tool_data(data) {
data.updater.insert(data.entity, Attacking {
base_damage: self.base_damage,
@ -53,16 +52,15 @@ impl CharacterBehavior for Data {
});
}
update.character = CharacterState::BasicAttack(Data {
update.character = CharacterState::BasicMelee(Data {
buildup_duration: self.buildup_duration,
recover_duration: self.recover_duration,
base_damage: self.base_damage,
exhausted: true,
});
}
// Recovery
else if self.recover_duration != Duration::default() {
update.character = CharacterState::BasicAttack(Data {
} else if self.recover_duration != Duration::default() {
// Recovery
update.character = CharacterState::BasicMelee(Data {
buildup_duration: self.buildup_duration,
recover_duration: self
.recover_duration
@ -71,16 +69,11 @@ impl CharacterBehavior for Data {
base_damage: self.base_damage,
exhausted: true,
});
}
// Done
else {
if let Some(tool) = unwrap_tool_data(data) {
update.character = CharacterState::Wielding;
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
} else {
update.character = CharacterState::Idle;
}
} else {
// Done
update.character = CharacterState::Wielding;
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
}
// Grant energy on successful hit

View File

@ -0,0 +1,87 @@
use crate::{
comp::{Attacking, Body, CharacterState, EnergySource, Gravity, Projectile, StateUpdate},
event::ServerEvent,
states::{utils::*, wielding},
sys::character_behavior::*,
};
use std::{collections::VecDeque, time::Duration};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// How long we prepared the weapon already
pub prepare_timer: Duration,
/// How long the state has until exiting
pub recover_duration: Duration,
/// Projectile
pub projectile: Projectile,
/// Projectile
pub projectile_body: Body,
/// Whether the attack fired already
pub exhausted: bool,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate {
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,
energy: *data.energy,
character: data.character.clone(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
};
handle_move(data, &mut update);
handle_jump(data, &mut update);
if !self.exhausted
&& (data.inputs.primary.is_pressed() | data.inputs.secondary.is_pressed())
{
// Prepare (draw the bow)
update.character = CharacterState::BasicRanged(Data {
prepare_timer: self.prepare_timer + Duration::from_secs_f32(data.dt.0),
recover_duration: self.recover_duration,
projectile: self.projectile.clone(),
projectile_body: self.projectile_body,
exhausted: false,
});
} else if !self.exhausted {
// Fire
update.server_events.push_front(ServerEvent::Shoot {
entity: data.entity,
dir: data.inputs.look_dir,
body: self.projectile_body,
light: None,
projectile: self.projectile.clone(),
gravity: Some(Gravity(0.1)),
});
update.character = CharacterState::BasicRanged(Data {
prepare_timer: self.prepare_timer,
recover_duration: self.recover_duration,
projectile: self.projectile.clone(),
projectile_body: self.projectile_body,
exhausted: true,
});
} else if self.recover_duration != Duration::default() {
// Recovery
update.character = CharacterState::BasicRanged(Data {
prepare_timer: self.prepare_timer,
recover_duration: self
.recover_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
projectile: self.projectile.clone(),
projectile_body: self.projectile_body,
exhausted: true,
});
return update;
} else {
// Done
update.character = CharacterState::Wielding;
}
update
}
}

View File

@ -0,0 +1,59 @@
use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::{utils::*, wielding},
sys::character_behavior::*,
};
use std::{collections::VecDeque, time::Duration};
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// How long the state has until exiting
pub duration: Duration,
pub only_up: bool,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate {
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,
energy: *data.energy,
character: data.character.clone(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
};
handle_move(data, &mut update);
// Still going
if self.duration != Duration::default() {
if self.only_up {
update.vel.0.z = 30.0;
} else {
update.vel.0 = data.inputs.look_dir * 30.0;
}
update.character = CharacterState::Boost(Data {
duration: self
.duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
only_up: self.only_up,
});
}
// Done
else {
update.character = CharacterState::Wielding;
}
// Grant energy on successful hit
if let Some(attack) = data.attacking {
if attack.applied && attack.hit_count > 0 {
data.updater.remove::<Attacking>(data.entity);
update.energy.change_by(100, EnergySource::HitEnemy);
}
}
update
}
}

View File

@ -21,7 +21,7 @@ impl CharacterBehavior for Data {
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,
character: *data.character,
character: data.character.clone(),
energy: *data.energy,
local_events: VecDeque::new(),
server_events: VecDeque::new(),

View File

@ -24,7 +24,7 @@ impl CharacterBehavior for Data {
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,
character: *data.character,
character: data.character.clone(),
energy: *data.energy,
local_events: VecDeque::new(),
server_events: VecDeque::new(),

View File

@ -14,7 +14,7 @@ pub struct Data {
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate {
character: *data.character,
character: data.character.clone(),
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,

View File

@ -20,7 +20,7 @@ impl CharacterBehavior for Data {
vel: *data.vel,
ori: *data.ori,
energy: *data.energy,
character: *data.character,
character: data.character.clone(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
};

View File

@ -10,7 +10,7 @@ pub struct Data;
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate {
character: *data.character,
character: data.character.clone(),
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,

View File

@ -1,6 +1,7 @@
// Module declarations
pub mod basic_attack;
pub mod basic_block;
pub mod basic_melee;
pub mod basic_ranged;
pub mod boost;
pub mod charge_attack;
pub mod climb;
pub mod equipping;

View File

@ -15,7 +15,7 @@ pub struct Data {
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate {
character: *data.character,
character: data.character.clone(),
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,

View File

@ -11,7 +11,7 @@ pub struct Data;
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate {
character: *data.character,
character: data.character.clone(),
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,

View File

@ -26,7 +26,7 @@ impl CharacterBehavior for Data {
vel: *data.vel,
ori: *data.ori,
energy: *data.energy,
character: *data.character,
character: data.character.clone(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
};

View File

@ -33,7 +33,7 @@ impl CharacterBehavior for Data {
vel: *data.vel,
ori: *data.ori,
energy: *data.energy,
character: *data.character,
character: data.character.clone(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
};

View File

@ -195,7 +195,7 @@ pub fn attempt_primary_ability(data: &JoinData, update: &mut StateUpdate) {
.loadout
.active_item
.as_ref()
.and_then(|i| i.primary_ability)
.and_then(|i| i.primary_ability.as_ref())
{
update.character = ability.into();
}
@ -217,7 +217,7 @@ pub fn attempt_secondary_ability(data: &JoinData, update: &mut StateUpdate) {
.loadout
.active_item
.as_ref()
.and_then(|i| i.secondary_ability)
.and_then(|i| i.secondary_ability.as_ref())
{
update.character = ability.into();
}
@ -246,7 +246,7 @@ pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) {
.loadout
.active_item
.as_ref()
.and_then(|i| i.dodge_ability)
.and_then(|i| i.dodge_ability.as_ref())
{
update.character = ability.into();
}

View File

@ -10,7 +10,7 @@ pub struct Data;
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate {
character: *data.character,
character: data.character.clone(),
pos: *data.pos,
vel: *data.vel,
ori: *data.ori,

View File

@ -182,7 +182,9 @@ impl<'a> System<'a> for Sys {
CharacterState::Equipping(data) => data.behavior(&j),
CharacterState::ChargeAttack(data) => data.behavior(&j),
CharacterState::TripleStrike(data) => data.behavior(&j),
CharacterState::BasicAttack(data) => data.behavior(&j),
CharacterState::BasicMelee(data) => data.behavior(&j),
CharacterState::BasicRanged(data) => data.behavior(&j),
CharacterState::Boost(data) => data.behavior(&j),
CharacterState::TimedCombo(data) => data.behavior(&j),
// Do not use default match.

View File

@ -77,8 +77,11 @@ impl<'a> System<'a> for Sys {
entity,
cause: HealthSource::World,
}),
projectile::Effect::Possess => server_emitter
.emit(ServerEvent::Possess(projectile.owner.into(), other)),
projectile::Effect::Possess => {
if let Some(owner) = projectile.owner {
server_emitter.emit(ServerEvent::Possess(owner.into(), other));
}
},
_ => {},
}
}

View File

@ -283,17 +283,17 @@ impl<'a> System<'a> for Sys {
}
}
if let Some(&character_state) = character_state {
if let Some(&character_state) = character_state.as_ref() {
if last_character_state
.get(entity)
.map(|&l| !character_state.equals(&l.0))
.map(|l| !character_state.equals(&l.0))
.unwrap_or(true)
{
let _ = last_character_state.insert(entity, Last(character_state));
let _ = last_character_state.insert(entity, Last(character_state.clone()));
send_msg(
ServerMsg::EntityCharacterState {
entity: uid.into(),
character_state,
character_state: character_state.clone(),
},
entity,
pos,
@ -367,7 +367,7 @@ pub fn send_initial_unsynced_components(
if let Some(&ori) = ori {
client.notify(ServerMsg::EntityOri { entity, ori });
}
if let Some(&character_state) = character_state {
if let Some(character_state) = character_state.cloned() {
client.notify(ServerMsg::EntityCharacterState {
entity,
character_state,

View File

@ -112,7 +112,7 @@ impl<'a> TrackedComps<'a> {
.map(|c| comps.push(c.into()));
self.character_state
.get(entity)
.copied()
.cloned()
.map(|c| comps.push(c.into()));
EntityPackage { uid, comps }

View File

@ -543,7 +543,7 @@ impl<'a> Widget for Skillbar<'a> {
// M1 Slot
match self.character_state {
CharacterState::BasicAttack { .. } => {
CharacterState::BasicMelee { .. } => {
if self.controller.primary.is_pressed() {
let fade_pulse = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.6; //Animation timer;
Image::new(self.imgs.skillbar_slot_big)
@ -654,7 +654,7 @@ impl<'a> Widget for Skillbar<'a> {
.set(state.ids.m2_slot, ui);
}
},*/
CharacterState::BasicAttack { .. } => {
CharacterState::BasicMelee { .. } => {
let fade_pulse = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.6; //Animation timer;
if self.controller.secondary.is_pressed() {
Image::new(self.imgs.skillbar_slot_big)

View File

@ -6,7 +6,7 @@ use crate::{
};
use common::{
assets::watch::ReloadIndicator,
comp::{Body, CharacterState, ItemKind, Loadout},
comp::{Body, CharacterState, ItemKind, Loadout, ToolKind},
};
use hashbrown::{hash_map::Entry, HashMap};
use std::{
@ -17,23 +17,26 @@ use std::{
#[derive(PartialEq, Eq, Hash, Clone)]
enum FigureKey {
Simple(Body),
Complex(
Body,
Option<Loadout>,
CameraMode,
Option<CharacterStateCacheKey>,
),
Complex(Body, CameraMode, Option<CharacterCacheKey>),
}
#[derive(PartialEq, Eq, Hash, Clone)]
struct CharacterStateCacheKey {
struct CharacterCacheKey {
state: Discriminant<CharacterState>, // TODO: Can this be simplified?
active_tool: Option<Discriminant<ToolKind>>, // TODO: Can this be simplified?
}
impl From<&CharacterState> for CharacterStateCacheKey {
fn from(cs: &CharacterState) -> Self {
impl CharacterCacheKey {
fn from(cs: &CharacterState, loadout: &Loadout) -> Self {
Self {
state: discriminant(&cs),
active_tool: if let Some(ItemKind::Tool(tool)) =
loadout.active_item.as_ref().map(|i| &i.item.kind)
{
Some(discriminant(&tool.kind))
} else {
None
},
}
}
}
@ -67,12 +70,11 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
for<'a> &'a common::comp::Body: std::convert::TryInto<Skel::Attr>,
Skel::Attr: Default,
{
let key = if loadout.is_some() {
let key = if let Some(loadout) = loadout {
FigureKey::Complex(
body,
loadout.cloned(),
camera_mode,
character_state.map(|cs| CharacterStateCacheKey::from(cs)),
character_state.map(|cs| CharacterCacheKey::from(cs, loadout)),
)
} else {
FigureKey::Simple(body)

View File

@ -468,7 +468,25 @@ impl FigureMgr {
skeleton_attr,
)
},
CharacterState::BasicAttack(_) => {
CharacterState::BasicMelee(_) => {
anim::character::AttackAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::BasicRanged(_) => {
anim::character::AttackAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::Boost(_) => {
anim::character::AttackAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),

View File

@ -188,6 +188,13 @@ impl Scene {
.get(scene_data.player_entity)
.map_or(false, |cs| cs.is_dodge());
let player_running = scene_data
.state
.ecs()
.read_storage::<comp::Vel>()
.get(scene_data.player_entity)
.map_or(false, |v| v.0.magnitude_squared() > 0.5);
let player_scale = match scene_data
.state
.ecs()
@ -211,9 +218,11 @@ impl Scene {
let up = match self.camera.get_mode() {
CameraMode::FirstPerson => {
if player_rolling {
player_scale * 0.8_f32
player_scale * 0.8
} else if player_running {
player_scale * 1.6 + (scene_data.state.get_time() as f32 * 17.0).sin() * 0.05
} else {
player_scale * 1.6_f32
player_scale * 1.6
}
},
CameraMode::ThirdPerson => 1.2,