diff --git a/semestralka1/src/game/state.cpp b/semestralka1/src/game/state.cpp index 788bfe3..807ac2f 100644 --- a/semestralka1/src/game/state.cpp +++ b/semestralka1/src/game/state.cpp @@ -8,12 +8,85 @@ #include namespace { -int compute_frame_index(int frame_count, float player_speed, int tick_counter) { - float phase_speed = player_speed * 0.1f; - float phase = fmodf(tick_counter * phase_speed, 1.0f); - int frame = - static_cast((1.0f - fabsf(sinf(phase * M_PI))) * (frame_count - 1)); - return frame; +// 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(tick_counter) * 1664525u + 1013904223u; + seed ^= static_cast(frame_count) * 2654435761u; + seed ^= static_cast(player_speed * 997.0f); + int range = last - 1; // size of 1..last-1 + if (range <= 0) return 0; // safety + int mid = 1 + static_cast(seed % static_cast(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(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(tick_counter) * + static_cast(step)) % + static_cast(cycle_len); + int pos = static_cast(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 @@ -32,58 +105,63 @@ void WalkingState::start_crawl() { state_timer.start(); } -// switch walk/run based on relative speed -void WalkingState::set_motion_state_for_speed(int player_speed, - int ground_speed) { - int relative = player_speed - ground_speed; - if (relative > 1) - current_state = PlayerState::Run; - else - current_state = PlayerState::Walk; +void WalkingState::set_state(PlayerState s) { + if (current_state != s) { + walk_index = run_index = crawl_index = 0; + phase = 0.0f; + } + 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); + 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); + 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; - } - - case PlayerState::Crawl2: - crawl_index = compute_frame_index(CHARACTER_CRAWL2_FRAME_COUNT, player_speed, - tick_counter); + crawl_index = + compute_frame_index(CHARACTER_CRAWL_FRAME_COUNT, player_speed, tick_counter); break; } } FrameSelection WalkingState::get_frame_selection() const { - FrameSelection f{MovementType::Walk, 0}; - - switch (current_state) { - case PlayerState::Walk: - f.movement = MovementType::Walk; - f.frame_index = walk_index; - break; - case PlayerState::Run: - f.movement = MovementType::Run; - f.frame_index = run_index; - break; - case PlayerState::Crawl: - f.movement = MovementType::Crawl; - f.frame_index = crawl_index; - break; - } - return f; + FrameSelection f{}; + switch (current_state) { + case PlayerState::Walk: + f.movement = MovementType::Walk; + f.frame_index = walk_index; + break; + case PlayerState::Run: + f.movement = MovementType::Run; + f.frame_index = run_index; + break; + case PlayerState::Crawl: + f.movement = MovementType::Crawl; + f.frame_index = crawl_index; + break; + default: + f.movement = MovementType::Walk; + f.frame_index = walk_index; + break; + } + return f; } diff --git a/semestralka1/src/game/state.h b/semestralka1/src/game/state.h index c5dcb66..091b5ba 100644 --- a/semestralka1/src/game/state.h +++ b/semestralka1/src/game/state.h @@ -19,15 +19,17 @@ private: int walk_index = 0; // cycles 0‑6 int run_index = 0; // cycles 0‑6 int crawl_index = 0; // cycles 0‑1 + float phase = 0.0f; public: void update(); void start_crawl(); void set_motion_state_for_speed(int player_speed, int ground_speed); - void toggle_walk_frame(float player_speed, int tick_counter); FrameSelection get_frame_selection() const; PlayerState get_state() const { return current_state; } + + void set_state(PlayerState s); }; diff --git a/semestralka1/src/render/loop.cpp b/semestralka1/src/render/loop.cpp index 951be48..3028a00 100644 --- a/semestralka1/src/render/loop.cpp +++ b/semestralka1/src/render/loop.cpp @@ -35,9 +35,11 @@ void render_loop(int speed) { const char *bg_file = "background_dark_inverted.txt"; bool need_redraw = false; + int anim_tick_counter = 0; int tick_counter = 0; - int player_speed = 5; + int player_speed = 6; + player_state.set_state(PlayerState::Run); while (true) { tick_counter++; @@ -61,22 +63,18 @@ void render_loop(int speed) { player_state.update(); if (animation.tick(speed)) { + anim_tick_counter++; need_redraw = true; } if (need_redraw) { + player_state.toggle_walk_frame(player_speed, anim_tick_counter); draw_mask(bg_file, animation.get_shift(), uart.get_message()); FrameSelection frame = player_state.get_frame_selection(); CharacterPosition draw_pos = get_aligned_frame_position(pos, frame.movement, frame.frame_index); draw_character(draw_pos.x, draw_pos.y, frame.movement, frame.frame_index); - - if (player_state.get_state() != PlayerState::Crawl) { - player_state.set_motion_state_for_speed(player_speed, timing.get_ground_speed()); - player_state.toggle_walk_frame(player_speed, tick_counter); - } - ThisThread::sleep_for(50ms); }