Skip to content

Commit

Permalink
Add action speed multiplier stat
Browse files Browse the repository at this point in the history
  • Loading branch information
haihala committed Nov 4, 2023
1 parent fc40e5f commit 399c959
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 50 deletions.
4 changes: 4 additions & 0 deletions client/characters/src/action_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ impl ActionTracker {

output
}

pub fn last_breakpoint_frame(&self) -> Option<usize> {
self.cancel_breakpoints.last().map(|(_, frame)| *frame)
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion client/characters/src/actions/action_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Requirement {
Self::Condition(condition) => condition(situation),
Self::Time(duration) => {
(situation.frame - situation.tracker.unwrap().current_block_start_frame)
>= *duration
>= ((*duration as f32 / situation.stats.action_speed_multiplier) as usize)
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions client/characters/src/actions/action_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ pub enum ActionEvent {
Animation(Animation),
Consume(ItemId),
RecipientAnimation(Animation),
AnimationAtFrame(Animation, usize),
RecipientAnimationAtFrame(Animation, usize),
Sound(SoundEffect),
Move(ActionId),
Attack(Attack),
Expand Down
13 changes: 5 additions & 8 deletions client/characters/src/characters/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,7 @@ pub fn dummy() -> Character {
dummy_items(),
2.0,
1.0,
Stats {
walk_speed: 3.0,
max_health: 250,
opener_damage_multiplier: 1.5,
opener_meter_gain: 50,
opener_stun_frames: 5,
},
Stats::default(),
vec![
(
ResourceType::Charge,
Expand Down Expand Up @@ -786,7 +780,10 @@ fn dummy_items() -> HashMap<ItemId, Item> {
cost: 100,
category: Consumable,
explanation: "Get yoked".into(),
..default()
effect: Stats {
action_speed_multiplier: 1.1,
..Stats::identity()
},
},
)]
.into_iter()
Expand Down
8 changes: 5 additions & 3 deletions client/characters/src/items/inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ impl Inventory {
}

pub fn get_effects(&self, character: &Character) -> Stats {
self.items.iter().fold(Stats::default(), |accumulator, id| {
accumulator.combine(&get_recursive_effects(id, character))
})
self.items
.iter()
.fold(Stats::identity(), |accumulator, id| {
accumulator.combine(&get_recursive_effects(id, character))
})
}

pub fn count(&self, item: ItemId) -> usize {
Expand Down
2 changes: 2 additions & 0 deletions client/characters/src/situation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bevy::utils::HashMap;
use wag_core::Stats;

use crate::{ActionTracker, Inventory, ResourceType, WAGResource};

Expand All @@ -9,6 +10,7 @@ pub struct Situation {
pub inventory: Inventory,
pub resources: HashMap<ResourceType, WAGResource>,
pub frame: usize,
pub stats: Stats,
// Kept minimal so far, but will grow as needed
}
impl Situation {
Expand Down
14 changes: 10 additions & 4 deletions client/lib/src/assets/animations/animation_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::mem::take;

use bevy::{prelude::*, scene::SceneInstance};

use wag_core::{Animation, Facing};
use wag_core::{Animation, Facing, Stats};

use super::Animations;

Expand All @@ -13,6 +13,7 @@ pub struct AnimationRequest {
pub position_offset: Vec2,
pub invert: bool,
pub looping: bool,
pub ignore_action_speed: bool,
}
impl From<Animation> for AnimationRequest {
fn from(animation: Animation) -> Self {
Expand Down Expand Up @@ -101,11 +102,11 @@ fn find_animation_player_entity(

pub fn update_animation(
animations: Res<Animations>,
mut main: Query<(&mut AnimationHelper, &Facing)>,
mut main: Query<(&mut AnimationHelper, &Facing, &Stats)>,
mut players: Query<&mut AnimationPlayer>,
mut scenes: Query<&mut Transform, With<Handle<Scene>>>,
) {
for (mut helper, facing) in &mut main {
for (mut helper, facing, stats) in &mut main {
let mut player = players.get_mut(helper.player_entity).unwrap();
let mut scene_root = scenes.get_mut(helper.scene_root).unwrap();

Expand All @@ -122,7 +123,12 @@ pub fn update_animation(

player
.start(handle)
.set_elapsed(request.time_offset as f32 / wag_core::FPS);
.set_elapsed(request.time_offset as f32 / wag_core::FPS)
.set_speed(if request.ignore_action_speed {
1.0
} else {
stats.action_speed_multiplier
});

if request.looping {
player.repeat();
Expand Down
29 changes: 12 additions & 17 deletions client/lib/src/player/asset_updater.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bevy::prelude::*;
use characters::{ActionEvent, Character};
use player_state::PlayerState;
use wag_core::{Facing, Players};
use wag_core::{Clock, Facing, Players};

use crate::assets::{AnimationHelper, AnimationRequest, Sounds};

Expand All @@ -19,44 +19,38 @@ pub fn update_animation(
>,
tfs: Query<&Transform>,
players: Res<Players>,
clock: Res<Clock>,
) {
// TODO: This is somewhat faulty as a concept, fix at some point.
for (character, mut state, facing, mut helper, entity) in &mut query {
let base_offset = state
.last_breakpoint_frame()
.map(|frame| clock.frame - frame)
.unwrap_or_default();

let [active, opponent] = tfs
.get_many([entity, players.get_other_entity(entity)])
.unwrap();
let position_offset = (opponent.translation - active.translation).truncate();
if let Some(request) = state
if let Some(req) = state
.drain_matching_actions(|action| match action {
ActionEvent::Animation(animation) => Some(AnimationRequest {
animation: *animation,
..default()
}),
ActionEvent::AnimationAtFrame(animation, frame) => Some(AnimationRequest {
animation: *animation,
time_offset: *frame,
..default()
}),
ActionEvent::RecipientAnimation(animation) => Some(AnimationRequest {
animation: *animation,
position_offset,
invert: true,
..default()
}),
ActionEvent::RecipientAnimationAtFrame(animation, frame) => {
Some(AnimationRequest {
animation: *animation,
time_offset: *frame,
position_offset,
invert: true,
..default()
})
}
_ => None,
})
.last()
{
helper.play(request.to_owned());
let mut request = req.to_owned();
request.time_offset += base_offset;
helper.play(request);
} else if let Some(generic) = state.get_generic_animation(*facing) {
let animation = character
.generic_animations
Expand All @@ -67,6 +61,7 @@ pub fn update_animation(
helper.play_if_new(AnimationRequest {
animation,
looping: true,
ignore_action_speed: true,
..default()
});
}
Expand Down
22 changes: 17 additions & 5 deletions client/lib/src/player/move_activation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use characters::{
};
use input_parsing::InputParser;
use player_state::PlayerState;
use wag_core::{ActionId, Clock, Player};
use wag_core::{ActionId, Clock, Player, Stats};

use crate::{damage::Combo, ui::Notifications};

Expand Down Expand Up @@ -104,17 +104,23 @@ pub(super) fn raw_or_link(
&PlayerState,
&Inventory,
&WAGResources,
&Stats,
)>,
) {
// Set activating move if one in the buffer can start raw or be linked into
for (mut buffer, character, state, inventory, resources) in &mut query {
for (mut buffer, character, state, inventory, resources, stats) in &mut query {
if let Some(freedom_frame) = state.free_since {
// Character has recently been freed

if let Some((stored, id, _)) = buffer
.get_situation_moves(
character,
state.build_situation(inventory.clone(), resources.clone(), clock.frame),
state.build_situation(
inventory.to_owned(),
resources.to_owned(),
stats.to_owned(),
clock.frame,
),
)
.into_iter()
.min_by(|(_, id1, _), (_, id2, _)| id1.cmp(id2))
Expand All @@ -139,18 +145,24 @@ pub(super) fn special_cancel(
&PlayerState,
&Inventory,
&WAGResources,
&Stats,
)>,
) {
// Set activating move if one in the buffer can be cancelled into
for (mut buffer, character, state, inventory, resources) in &mut query {
for (mut buffer, character, state, inventory, resources, stats) in &mut query {
if state.free_since.is_none() {
if let Some(tracker) = state.get_action_tracker() {
// Not free because a move is happening
// Is current move cancellable, if so, since when
if let Some((stored, id, cancellable_since)) = buffer
.get_situation_moves(
character,
state.build_situation(inventory.clone(), resources.clone(), clock.frame),
state.build_situation(
inventory.to_owned(),
resources.to_owned(),
stats.to_owned(),
clock.frame,
),
)
.into_iter()
.filter_map(|(frame, id, action)| {
Expand Down
13 changes: 9 additions & 4 deletions client/lib/src/player/move_advancement.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use bevy::prelude::*;
use characters::{Inventory, WAGResources};
use player_state::PlayerState;
use wag_core::Clock;
use wag_core::{Clock, Stats};

pub(super) fn move_advancement(
clock: Res<Clock>,
mut query: Query<(&mut PlayerState, &Inventory, &WAGResources)>,
mut query: Query<(&mut PlayerState, &Inventory, &WAGResources, &Stats)>,
) {
for (mut state, inventory, resources) in &mut query {
for (mut state, inventory, resources, stats) in &mut query {
if state.action_in_progress() {
state.proceed_move(inventory.clone(), resources.clone(), clock.frame);
state.proceed_move(
inventory.to_owned(),
resources.to_owned(),
stats.to_owned(),
clock.frame,
);
}
}
}
23 changes: 20 additions & 3 deletions client/player_state/src/player_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ impl PlayerState {
.collect()
}

pub fn last_breakpoint_frame(&self) -> Option<usize> {
match self.main {
MainState::Stand(StandState::Move(ref tracker))
| MainState::Crouch(CrouchState::Move(ref tracker))
| MainState::Air(AirState::Move(ref tracker)) => tracker.last_breakpoint_frame(),
_ => None,
}
}

pub fn add_actions(&mut self, mut actions: Vec<ActionEvent>) {
self.unprocessed_events.append(&mut actions);
}
Expand Down Expand Up @@ -89,8 +98,14 @@ impl PlayerState {
};
self.free_since = None;
}
pub fn proceed_move(&mut self, inventory: Inventory, resources: WAGResources, frame: usize) {
let situation = self.build_situation(inventory, resources, frame);
pub fn proceed_move(
&mut self,
inventory: Inventory,
resources: WAGResources,
stats: Stats,
frame: usize,
) {
let situation = self.build_situation(inventory, resources, stats, frame);
let tracker = self.get_action_tracker_mut().unwrap();

if tracker.blocker.fulfilled(situation) {
Expand All @@ -107,11 +122,13 @@ impl PlayerState {
&self,
inventory: Inventory,
resources: WAGResources,
stats: Stats,
frame: usize,
) -> Situation {
Situation {
inventory,
frame,
stats,
resources: resources.0,
grounded: self.is_grounded(),
tracker: self.get_action_tracker().cloned(),
Expand Down Expand Up @@ -279,7 +296,7 @@ impl PlayerState {
}
pub fn combined_status_effects(&self) -> Stats {
// TODO: Cache for later
self.conditions.iter().fold(Stats::default(), |acc, cond| {
self.conditions.iter().fold(Stats::identity(), |acc, cond| {
if let Some(effect) = &cond.effect {
acc.combine(effect)
} else {
Expand Down
21 changes: 18 additions & 3 deletions client/wag_core/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,25 @@ pub struct Stats {
pub opener_damage_multiplier: f32,
pub opener_meter_gain: i32,
pub opener_stun_frames: i32,
// TODO: Add more fields
// Actions
pub action_speed_multiplier: f32,
}

impl Default for Stats {
fn default() -> Self {
Self {
walk_speed: 3.0,
max_health: 250,
opener_damage_multiplier: 1.5,
opener_meter_gain: 50,
opener_stun_frames: 5,
..Self::identity()
}
}
}

impl Stats {
pub fn identity() -> Self {
Self {
// These are meant to be identity values, you should be able to
// combine them with another Stats instance and get the other instance out.
Expand All @@ -22,17 +36,17 @@ impl Default for Stats {
opener_damage_multiplier: 1.0,
opener_meter_gain: 0,
opener_stun_frames: 0,
action_speed_multiplier: 1.0,
}
}
}

impl Stats {
pub fn combine(mut self, rhs: &Self) -> Self {
self.walk_speed += rhs.walk_speed;
self.max_health += rhs.max_health;
self.opener_damage_multiplier *= rhs.opener_damage_multiplier;
self.opener_meter_gain += rhs.opener_meter_gain;
self.opener_stun_frames += rhs.opener_stun_frames;
self.action_speed_multiplier *= rhs.action_speed_multiplier;

self
}
Expand All @@ -44,6 +58,7 @@ impl Stats {
opener_damage_multiplier: 1.5,
opener_meter_gain: 20,
opener_stun_frames: 5,
action_speed_multiplier: 1.0,
}
}
}
Expand Down

0 comments on commit 399c959

Please sign in to comment.