401 lines
8.9 KiB
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);
|
|
|
|
}
|
|
}
|
|
}
|
|
|