chaos-drop/src/game.cc

275 lines
7.6 KiB
C++

/* Functions for game-specific mechanics. */
#include "chaos-drop.h"
#include <azur/azur.h>
#ifdef AZUR_TOOLKIT_GINT
extern struct level level_1_1, level_1_2, level_1_3;
extern struct level level_2_1, level_2_2, level_2_3;
extern struct level level_3_1, level_3_2, level_3_3;
static struct level const *ALL_LEVELS[] = {
&level_1_1, &level_1_2, &level_1_3,
&level_2_1, &level_2_2, &level_2_3,
&level_3_1, &level_3_2, &level_3_3,
};
#else
/* TODO: PC/SDL build: Embed levels as in the add-in */
static struct level const lv_test = {
.name = "<Test>",
.finish = num(1024),
.mirror_count = 1,
.mirrors = (struct element_mirror[]){
{ .begin = 256, .end = 768 },
},
.plane_count = 2,
.planes = (struct element_plane[]){
{ .y = 384, .shape = 0b01010'10101'01010'10101'01010,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
{ .y = 384+64, .shape = 0b11111'10001'10001'10001'11111,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
},
.text_count = 1,
.texts = (struct element_text[]){
{ .begin = 32, .end = 256,
.str = "Red stuff bad!\nDon't touch the red stuff!", },
},
};
static struct level const *ALL_LEVELS[] = { &lv_test };
#endif
void camera_set_fov(struct camera *camera, float fov_degrees)
{
camera->fov = num(fov_degrees);
float fov_radians = fov_degrees * 3.14159 / 180;
/* Use FOV as the horizontal viewing angle */
float sd = (VWIDTH * HALF_SCREEN_HEIGHT / VHEIGHT) / tanf(fov_radians / 2);
/* The screen is at such a distance that 16 units is half the height. We
don't care where it is placed as we'll always send rays from the camera
itself. This is to ensure good ranges for fixed point values */
camera->screen_distance = num(sd);
}
bool camera_rolling(struct camera const *camera)
{
float target_roll = camera->roll_quadrant * M_PI / 2;
float distance = fabsf(target_roll - camera->roll);
/* Wrap-around case */
if(camera->roll > 6.0 && camera->roll_quadrant == 0)
distance = fabsf(target_roll - (camera->roll - 2*M_PI));
return distance > 0.3;
}
void camera_roll(struct camera *camera, float snap_factor)
{
float target_roll = camera->roll_quadrant * M_PI / 2;
float distance_nowrap = target_roll - camera->roll;
float distance_wrap_up = (target_roll + 2*M_PI) - camera->roll;
float distance_wrap_down = (target_roll - 2*M_PI) - camera->roll;
float mv;
/* Snap with closest direction */
if(fabsf(distance_nowrap) < fabsf(distance_wrap_up)) {
if(fabsf(distance_nowrap) < fabsf(distance_wrap_down))
mv = distance_nowrap * snap_factor;
else
mv = distance_wrap_down * snap_factor;
}
else {
if(fabsf(distance_wrap_down) < fabsf(distance_wrap_down))
mv = distance_wrap_down * snap_factor;
else
mv = distance_wrap_up * snap_factor;
}
camera->roll += mv;
if(camera->roll < 0)
camera->roll += 2 * M_PI;
else if(camera->roll > 2 * M_PI)
camera->roll -= 2 * M_PI;
/* Also rotate the player along with it */
float cos_mv, sin_mv;
sincosf(-mv, &sin_mv, &cos_mv);
num c = cos_mv, s = sin_mv;
num nx = camera->pos.x * c + camera->pos.z * s;
num nz = -camera->pos.x * s + camera->pos.z * c;
camera->pos.x = nx;
camera->pos.z = nz;
}
mat3 camera_camera2world_matrix(struct camera const *camera)
{
num cos_r = camera->cos_r;
num sin_r = camera->sin_r;
num cos_p = camera->cos_p;
num sin_p = camera->sin_p;
num cos_y = camera->cos_y;
num sin_y = camera->sin_y;
mat3 m_anti_roll = {
cos_r, 0, -sin_r,
0, 1, 0,
+sin_r, 0, cos_r,
};
mat3 m_anti_pitch = {
1, 0, 0,
0, cos_p, -sin_p,
0, +sin_p, cos_p,
};
mat3 m_anti_yaw = {
cos_y, -sin_y, 0,
+sin_y, cos_y, 0,
0, 0, 1,
};
return m_anti_yaw * (m_anti_pitch * m_anti_roll);
}
void camera_update_angles(struct camera *camera)
{
float c, s;
sincosf(camera->roll, &s, &c);
camera->sin_r = s;
camera->cos_r = c;
sincosf(camera->pitch, &s, &c);
camera->sin_p = s;
camera->cos_p = c;
sincosf(camera->yaw, &s, &c);
camera->sin_y = s;
camera->cos_y = c;
mat3 c2w = camera_camera2world_matrix(camera);
camera->forward = c2w * vec3(0, 1, 0);
camera->right = c2w * vec3(1, 0, 0);
camera->up = c2w * vec3(0, 0, 1);
}
int level_count(void)
{
return sizeof ALL_LEVELS / sizeof ALL_LEVELS[0];
}
const struct level *level_get(int id)
{
return (id >= 0 && id < level_count()) ? ALL_LEVELS[id] : NULL;
}
bool level_plane_collides(num16 x, num16 z, uint32_t shape)
{
int cx = ((x + num16(WORLD_SIZE)) * num16(5.0 / 2 / WORLD_SIZE)).ifloor();
int cz = ((z + num16(WORLD_SIZE)) * num16(5.0 / 2 / WORLD_SIZE)).ifloor();
return (unsigned)cx < 5 && (unsigned)cz < 5
&& ((shape >> (5*cz + 4-cx)) & 1);
}
bool game_in_menu(struct game const *g)
{
return !g->level;
}
static void game_reset_level_data(struct game *g)
{
g->level = NULL;
g->level_id = -1;
g->mirror_index = 0;
g->plane_index = 0;
g->text_index = 0;
g->vfx_index = 0;
g->mirror = NULL;
g->plane = NULL;
g->text = NULL;
g->vfx = NULL;
g->depth = 0;
g->plane_collision[0] = num(0x7ff);
g->plane_collision[1] = num(0x7ff);
}
void game_load_menu(struct game *g)
{
game_reset_level_data(g);
g->level = NULL;
g->level_id = -1;
}
void game_load_level(struct game *g, int index, int blank_frames)
{
struct level const *level = level_get(index);
if(!level)
return;
game_reset_level_data(g);
g->level = level;
g->level_id = index;
g->blank_frames = blank_frames;
}
void game_restart_level(struct game *g, int blank_frames)
{
if(!game_in_menu(g))
game_load_level(g, g->level_id, blank_frames);
}
void game_prev_level_or_menu(struct game *g)
{
if(g->level_id > 0)
game_load_level(g, g->level_id - 1, 0);
else
game_load_menu(g);
}
void game_next_level_or_menu(struct game *g)
{
if(g->level_id >= 0 && g->level_id + 1 < level_count())
game_load_level(g, g->level_id + 1, 0);
else
game_load_menu(g);
}
const struct element_plane *game_plane_collision(struct game const *g)
{
return g->plane && g->depth > g->plane->y - num(4) ? g->plane : NULL;
}
void game_advance_in_level(struct game *g)
{
struct level const *level = g->level;
/* Remove old elements */
if(g->mirror && g->depth > g->mirror->end)
g->mirror = NULL;
if(game_plane_collision(g))
g->plane = NULL;
if(g->text && g->depth > g->text->end)
g->text = NULL;
if(g->vfx && g->depth > g->vfx->end)
g->vfx = NULL;
/* Load incoming elements */
if(!g->mirror && g->mirror_index < level->mirror_count
&& level->mirrors[g->mirror_index].begin < g->depth + num(64)) {
g->mirror = &level->mirrors[g->mirror_index++];
}
if(!g->plane && g->plane_index < level->plane_count
&& level->planes[g->plane_index].y < g->depth + num(64)) {
g->plane = &level->planes[g->plane_index++];
}
// FIXME: Should've loaded just in time, not 64 in advance...
// (but now all levels are calibrated for that)
if(!g->text && g->text_index < level->text_count
&& level->texts[g->text_index].begin < g->depth + num(64)) {
g->text = &level->texts[g->text_index++];
}
if(!g->vfx && g->vfx_index < level->vfx_count
&& level->vfxs[g->vfx_index].begin < g->depth) {
g->vfx = &level->vfxs[g->vfx_index++];
}
}