@@ -1,8 +1,94 @@
// src/game/state.cpp
# include "state.h"
# include <cmath>
# include "../render/player.h"
# include "../timing/speed_controller.h"
# include "../assets/character_run_frames.h"
# include "../assets/character_walk_frames.h"
# include "../assets/character_crawl_frames.h"
# include <cmath>
namespace {
// Frame selector with two regimes:
// 1) Normal (speed <= 6): ping‑ pong with holds at both ends
// 2) High‑ speed (speed >= 7): 0, random(1..last-1), last, random(1..last-1)...
static inline int compute_frame_index ( int frame_count ,
float player_speed ,
int tick_counter ) {
if ( frame_count < = 1 ) return 0 ;
if ( player_speed < = 0.0f ) return 0 ;
const int last = frame_count - 1 ;
// High-speed pattern: 0, rand(1..last-1), last, rand(1..last-1), ...
if ( player_speed > = 7.0f ) {
// Special-case 2-frame animations
if ( frame_count < = 2 ) {
return ( tick_counter & 1 ) ? 1 : 0 ;
}
// Even ticks → extremes (0 or last), alternating each time.
// Odd ticks → pseudo-random mid in [1..last-1].
if ( ( tick_counter & 1 ) = = 0 ) {
// Alternate 0 and last on even ticks
bool choose_zero = ( ( tick_counter > > 1 ) & 1 ) = = 0 ;
return choose_zero ? 0 : last ;
} else {
// Simple deterministic PRNG from tick_counter and speed
unsigned int seed =
static_cast < unsigned int > ( tick_counter ) * 1664525u + 1013904223u ;
seed ^ = static_cast < unsigned int > ( frame_count ) * 2654435761u ;
seed ^ = static_cast < unsigned int > ( player_speed * 997.0f ) ;
int range = last - 1 ; // size of 1..last-1
if ( range < = 0 ) return 0 ; // safety
int mid = 1 + static_cast < int > ( seed % static_cast < unsigned int > ( range ) ) ;
return mid ;
}
}
// Normal-speed regime: discrete ping‑ pong with holds at both ends.
// - speed 1 => 0,1,2,...,last, last, ..., 2,1, 0,0 ...
// - speed 2 => advances 2 steps per tick (skipping every second timeline entry)
// - holds at ends make 0 and last appear more frequently
const int hold = 2 ; // how many extra steps to hold at each end
// One full ping‑ pong cycle length:
// [hold at 0] + [ascend 1..last] + [hold at last] + [descend last-1..0]
const int ascend_len = last ; // produces 1..last (length = last)
const int descend_len = last ; // produces last-1..0 (length = last)
const int cycle_len = hold + ascend_len + hold + descend_len ;
// Advance by an integer number of steps per tick based on speed
int step = static_cast < int > ( floorf ( player_speed + 0.5f ) ) ;
if ( step < 1 ) step = 1 ;
// Use 64-bit for safety, then fold into cycle length
const long long pos64 =
( static_cast < long long > ( tick_counter ) *
static_cast < long long > ( step ) ) %
static_cast < long long > ( cycle_len ) ;
int pos = static_cast < int > ( pos64 ) ;
// Map timeline position to frame index
if ( pos < hold ) {
return 0 ; // hold at 0
}
pos - = hold ;
if ( pos < ascend_len ) {
// ascend: 1..last
return ( pos + 1 ) ;
}
pos - = ascend_len ;
if ( pos < hold ) {
return last ; // hold at last
}
pos - = hold ;
// descend: last-1 .. 0
return last - 1 - pos ;
}
} // namespace
void WalkingState : : update ( ) {
// stop crawling after duration
@@ -19,23 +105,46 @@ void WalkingState::start_crawl() {
state_timer . start ( ) ;
}
// TODO THIS NEEDS REDESIGN ACCORDING TO SPEED
void WalkingState : : toggle_walk_frame ( float player_speed , int tick_counter ) {
constexpr int FRAME_COUNT = 8 ;
float phase_speed = player_speed * 0.1 f ; // tune this scaling for animation tempo
float phase = fmodf ( tick_counter * phase_speed , 1.0f ) ;
int frame = static_cast < int > ( ( 1.0f - fabsf ( sinf ( phase * M_PI ) ) ) * ( FRAME_COUNT - 1 ) ) ;
run_index = frame ;
void WalkingState : : set_state ( PlayerState s ) {
if ( current_state ! = s ) {
walk_index = run_index = crawl_index = 0 ;
phase = 0.0 f ;
}
current_state = s ;
}
void WalkingState : : set_motion_state_for_speed ( int player_speed , int ground_speed ) {
int relative = player_speed - ground_speed ;
PlayerState new_state = ( relative > 1 ) ? PlayerState : : Run : PlayerState : : Walk ;
if ( new_state ! = current_state ) {
run_index = 0 ;
walk_index = 0 ;
}
current_state = new_state ;
}
// TODO THIS NEEDS REDESIGN FOR WALK/RUN
void WalkingState : : toggle_walk_frame ( float player_speed , int tick_counter ) {
switch ( current_state ) {
case PlayerState : : Run :
run_index =
compute_frame_index ( CHARACTER_RUN_FRAME_COUNT , player_speed , tick_counter ) ;
break ;
case PlayerState : : Walk :
walk_index =
compute_frame_index ( CHARACTER_WALK_FRAME_COUNT , player_speed , tick_counter ) ;
break ;
case PlayerState : : Crawl :
crawl_index =
compute_frame_index ( CHARACTER_CRAWL_FRAME_COUNT , player_speed , tick_counter ) ;
break ;
}
}
// void RunningState::toggle_run_frame() {
// if (++run_index >= 7) run_index = 0;
// }
FrameSelection WalkingState : : get_frame_selection ( ) const {
FrameSelection f { MovementType : : Walk , 0 } ;
FrameSelection f { } ;
switch ( current_state ) {
case PlayerState : : Walk :
f . movement = MovementType : : Walk ;
@@ -49,6 +158,10 @@ FrameSelection WalkingState::get_frame_selection() const {
f . movement = MovementType : : Crawl ;
f . frame_index = crawl_index ;
break ;
default :
f . movement = MovementType : : Walk ;
f . frame_index = walk_index ;
break ;
}
return f ;
}