Files
pvs/semestralka1/src/game/state.cpp
2025-11-20 17:37:27 +01:00

180 lines
5.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/game/state.cpp
#include "state.h"
#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 {
// Vygenerovane AI, proste to nahodne vymiena obrazky - assests
// Frame selector with two regimes:
// 1) Normal (speed <= 6): pingpong with holds at both ends
// 2) Highspeed (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 pingpong 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 pingpong 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 MovementState::update() {
// stop crawling after duration
if ((current_state == PlayerState::Crawl1 ||
current_state == PlayerState::Crawl2) &&
state_timer.elapsed_time() >= CRAWL_DURATION) {
current_state = previous_state;
state_timer.stop();
}
}
void MovementState::start_crawl(PlayerState crawl_type) {
if (crawl_type != PlayerState::Crawl1 && crawl_type != PlayerState::Crawl2)
return;
previous_state = current_state;
current_state = crawl_type;
state_timer.stop();
state_timer.reset();
state_timer.start();
}
void MovementState::set_state(PlayerState s) {
if (current_state != s) {
walk_index = run_index = crawl1_index = crawl2_index = 0;
phase = 0.0f;
}
current_state = s;
}
void MovementState::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 MovementState::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::Crawl1:
crawl1_index = compute_frame_index(CHARACTER_CRAWL1_FRAME_COUNT, player_speed, tick_counter);
break;
case PlayerState::Crawl2:
crawl2_index = compute_frame_index(CHARACTER_CRAWL2_FRAME_COUNT, player_speed, tick_counter);
break;
}
}
FrameSelection MovementState::get_frame_selection() const {
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::Crawl1:
f.movement = MovementType::Crawl1;
f.frame_index = crawl1_index;
break;
case PlayerState::Crawl2:
f.movement = MovementType::Crawl2;
f.frame_index = crawl2_index;
break;
default:
f.movement = MovementType::Walk;
f.frame_index = walk_index;
break;
}
return f;
}