1
0
Fork 0
2022-untitled-game/code/src/game/logic.c

401 lines
8.9 KiB
C

const float PLAYER_SPEED = 2.5f;
const float CAMERA_SENSITIVITY = 0.05f;
const float ENEMY_SPEED = 2.0f;
const float POWERUP_TIME = 15.0f;
const float FOOTSTEP_TIMER = 0.75f;
enum {ENEMY_COUNT = 4};
typedef enum game_mode {
MODE_MAIN_MENU = 0,
MODE_OPTIONS,
MODE_PLAY,
MODE_PAUSE,
MODE_QUIT
} game_mode_e;
typedef struct skull {
vec3 tile_pos;
vec3 tile_pos_prev;
vec3 delta;
vec3 front;
int audio_source_handle;
} skull_t;
typedef struct eyeball {
transform_t transform;
bool used;
} eyeball_t;
typedef struct state {
game_mode_e mode;
game_mode_e mode_last;
input_t input_prev;
camera_t camera;
float time;
int coin_count;
int eyeball_count;
transform_t* coins;
eyeball_t* eyeballs;
skull_t skulls[ENEMY_COUNT];
int background_music_handle;
int powerup_sfx_handle;
bool powered_up;
float powerup_timer;
bool footstep_cycle;
float footstep_timer;
vec2 skybox_offset;
node_t nodes[4];
} state_t;
typedef struct ui {
ui_menu_t main_menu;
ui_menu_t options_menu;
ui_menu_t pause_menu;
} ui_t;
typedef struct game {
data_t data;
state_t state;
ui_t ui;
} game_t;
void
play(float dt, input_t input)
{
state_t* state = &g_game->state;
data_t* data = &g_game->data;
if (state->footstep_timer > 0.0f)
{
state->footstep_timer -= dt;
}
if (state->powerup_timer == POWERUP_TIME)
{
audio_source_t powerup_sfx = {
.wav = get_sfx_by_name("SFX_Powerup")};
state->powerup_sfx_handle = play_sound(powerup_sfx);
state->powered_up = true;
state->powerup_timer -= dt;
}
else if (state->powerup_timer > 0.0f)
{
state->powerup_timer -= dt;
}
else if (state->powerup_timer <= 0.0f)
{
state->powered_up = false;
state->powerup_timer = 0.0f;
}
// Update camera
{
vec3 velocity = {0};
if (input.up) {
velocity.y = 1.0f;
} else if (input.down) {
velocity.y = -1.0f;
}
if (input.left) {
velocity.x = -1.0f;
} else if (input.right) {
velocity.x = 1.0f;
}
float speed = dt * PLAYER_SPEED;
velocity = vec3_mult(vec3_normalize(velocity), speed);
float mouse_offset_x = -1.0f * input.mouse_x_delta * CAMERA_SENSITIVITY;
float mouse_offset_y = -1.0f * input.mouse_y_delta * CAMERA_SENSITIVITY;
camera_t camera_copy = state->camera;
camera_move(&camera_copy, velocity);
aabb_t player_aabb = {
.origin = camera_copy.position,
.extents = {0.3f, 0.3f, 1.0f}};
// Walls
for (size_t i = 0; i < data->wall_count; i++)
{
aabb_t aabb = data->walls[i];
if (aabb_check(player_aabb, aabb))
{
velocity.x = 0;
velocity.y = 0;
break;
}
}
// Score zone
aabb_t aabb = data->score.collider;
if (aabb_check(player_aabb, aabb))
{
velocity.x = 0;
velocity.y = 0;
}
const float baseline_position_z = 1.0f;
const float head_bob_amount = 0.02f;
const float idle_head_bob_freq = 1.5f;
const float moving_head_bob_freq = 8.0f;
float position_z = baseline_position_z;
if (velocity.x != 0.0f || velocity.y != 0.0f)
{
position_z = baseline_position_z + sinf(moving_head_bob_freq * state->footstep_timer) * head_bob_amount;
if (state->footstep_timer < 0.0f)
{
wav_t* wav;
if (state->footstep_cycle) {
wav = get_sfx_by_name("SFX_Footstep1");
} else {
wav = get_sfx_by_name("SFX_Footstep2");
}
audio_source_t footstep_sfx = {
.wav = wav};
play_sound(footstep_sfx);
state->footstep_timer = FOOTSTEP_TIMER;
state->footstep_cycle = !state->footstep_cycle;
}
}
else
{
position_z = baseline_position_z + sinf(idle_head_bob_freq * state->time) * head_bob_amount;
}
state->camera.position.z = position_z;
camera_move(&state->camera, velocity);
camera_rotate(&state->camera, mouse_offset_x, mouse_offset_y);
camera_update_view(&state->camera);
g_audio_mgr->listener.position = state->camera.position;
g_audio_mgr->listener.front = state->camera.front;
}
aabb_t player_aabb = {
.origin = state->camera.position,
.extents = {0.3f, 0.3f, 1.0f}};
// Check for coin collision
{
for (int i = 0; i < state->coin_count; i++)
{
transform_t coin = state->coins[i];
aabb_t coin_aabb = {
.origin = coin.position,
.extents = {0.5f, 0.5f, 1.0f}};
if (aabb_check(player_aabb, coin_aabb))
{
audio_source_t coin_sfx = {
.wav = get_sfx_by_name("SFX_Coin")};
play_sound(coin_sfx);
state->coins[i] = state->coins[state->coin_count-1];
state->coin_count--;
break;
}
}
}
// Check for eyeball collision
{
for (int i = 0; i < state->eyeball_count; i++)
{
transform_t transform = state->eyeballs[i].transform;
aabb_t eyeball_aabb = {
.origin = transform.position,
.extents = {1.0f, 1.0f, 1.0f}};
if (aabb_check(player_aabb, eyeball_aabb) && input.interact)
{
// TODO: Do we want to allow stacking powerups?
if (state->powerup_timer == 0.0f)
{
audio_source_t eyeball_sfx = {
.wav = get_sfx_by_name("SFX_Eyeball")};
play_sound(eyeball_sfx);
state->powerup_timer = POWERUP_TIME;
state->eyeballs[i].used = true;
// Move to end of list
eyeball_t eyeball = state->eyeballs[i];
state->eyeballs[i] = state->eyeballs[state->eyeball_count-1];
state->eyeballs[state->eyeball_count-1] = eyeball;
state->eyeball_count--;
}
break;
}
}
}
// Check for skull collision
{
for (int i = 0; i < ENEMY_COUNT; i++)
{
skull_t* skull = &state->skulls[i];
aabb_t skull_aabb = {
.origin = {skull->tile_pos.x*2.0f+1.0f, skull->tile_pos.y*2.0f+1.0f, 1.0f},
.extents = {1.0f, 1.0f, 1.0f}};
if (aabb_check(player_aabb, skull_aabb))
{
}
}
}
// Update skull positions
{
for (int i = 0; i < ENEMY_COUNT; i++)
{
skull_t* skull = &state->skulls[i];
vec3 skull_front = skull->front;
vec3 skull_right = vec3_cross(skull->front, WORLD_UP);
vec3 skull_left = vec3_negate(skull_right);
float player_dot = 0.0f;
{
vec3 skull_position = {skull->tile_pos.x*2.0f+1.0f, skull->tile_pos.x*2.0f+1.0f, 1.0f};
vec3 player_position = state->camera.position;
vec3 target = vec3_normalize(vec3_sub(player_position, skull_position));
player_dot = vec3_dot(skull_right, target);
}
bool left_tile_open = false;
{
int x = skull->tile_pos.x + (int)skull_left.x;
int y = skull->tile_pos.y + (int)skull_left.y;
left_tile_open = data->tiles[y] & (1U<<(31-x));
}
bool right_tile_open = false;
{
int x = skull->tile_pos.x + (int)skull_right.x;
int y = skull->tile_pos.y + (int)skull_right.y;
right_tile_open = data->tiles[y] & (1U<<(31-x));
}
bool front_tile_open = false;
{
int x = skull->tile_pos.x + (int)skull_front.x;
int y = skull->tile_pos.y + (int)skull_front.y;
front_tile_open = data->tiles[y] & (1U<<(31-x));
}
// Don't re-evaluate direction unless we've moved to a new tile
if (skull->tile_pos.x != skull->tile_pos_prev.x
|| skull->tile_pos.y != skull->tile_pos_prev.y)
{
skull->tile_pos_prev.x = skull->tile_pos.x;
skull->tile_pos_prev.y = skull->tile_pos.y;
// Front
if (front_tile_open && !left_tile_open && !right_tile_open)
{
skull->front = skull_front;
}
// Right
else if (right_tile_open && !left_tile_open && !front_tile_open)
{
skull->front = skull_right;
}
// Left
else if (left_tile_open && !right_tile_open && !front_tile_open)
{
skull->front = skull_left;
}
// Left-Front
else if (left_tile_open && front_tile_open && !right_tile_open)
{
if (player_dot < -0.5f)
{
skull->front = skull_left;
}
}
// Right-Front
else if (right_tile_open && front_tile_open && !left_tile_open)
{
if (player_dot > 0.5f) {
skull->front = skull_right;
}
}
// Left-Right
else if (left_tile_open && right_tile_open && !front_tile_open)
{
if (player_dot <= 0.0f) {
skull->front = skull_left;
} else if (player_dot > 0.0f) {
skull->front = skull_right;
}
}
// Left-Right-Front
else if (front_tile_open && left_tile_open && right_tile_open)
{
if (player_dot < 0.0f) {
skull->front = skull_left;
} else if (player_dot > 0.0f) {
skull->front = skull_right;
}
}
}
// Keep track of smooth position so we can render the skull in world-space but still handle its collisions
// in tile-space
float skull_speed = ENEMY_SPEED;
skull->delta.x += skull->front.x * skull_speed * dt;
skull->delta.y += skull->front.y * skull_speed * dt;
if (skull->delta.x >= 2.0f) {
skull->tile_pos.x++;
skull->delta.x = 0.0f;
}
else if (skull->delta.x <= -2.0f) {
skull->tile_pos.x--;
skull->delta.x = 0.0f;
}
if (skull->delta.y >= 2.0f) {
skull->tile_pos.y++;
skull->delta.y = 0.0f;
}
else if (skull->delta.y <= -2.0f) {
skull->tile_pos.y--;
skull->delta.y = 0.0f;
}
vec3 world_position = {
skull->tile_pos.x*2.0f+1.0f,
skull->tile_pos.y*2.0f+1.0f,
1.0f};
update_sound_position(skull->audio_source_handle, world_position);
}
}
}