Whitespace: change line endings to LF everywhere

This commit is contained in:
Dominic Szablewski 2023-09-04 17:34:29 +02:00
parent a829166082
commit 0594285013
26 changed files with 6344 additions and 6344 deletions

View file

@ -1,174 +1,174 @@
#include "../mem.h" #include "../mem.h"
#include "../utils.h" #include "../utils.h"
#include "../types.h" #include "../types.h"
#include "../render.h" #include "../render.h"
#include "../system.h" #include "../system.h"
#include "object.h" #include "object.h"
#include "track.h" #include "track.h"
#include "ship.h" #include "ship.h"
#include "weapon.h" #include "weapon.h"
#include "droid.h" #include "droid.h"
#include "camera.h" #include "camera.h"
void camera_init(camera_t *camera, section_t *section) { void camera_init(camera_t *camera, section_t *section) {
camera->section = section; camera->section = section;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
camera->section = camera->section->next; camera->section = camera->section->next;
} }
camera->position = camera->section->center; camera->position = camera->section->center;
camera->velocity = vec3(0, 0, 0); camera->velocity = vec3(0, 0, 0);
camera->angle = vec3(0, 0, 0); camera->angle = vec3(0, 0, 0);
camera->angular_velocity = vec3(0, 0, 0); camera->angular_velocity = vec3(0, 0, 0);
camera->has_initial_section = false; camera->has_initial_section = false;
} }
vec3_t camera_forward(camera_t *camera) { vec3_t camera_forward(camera_t *camera) {
float sx = sin(camera->angle.x); float sx = sin(camera->angle.x);
float cx = cos(camera->angle.x); float cx = cos(camera->angle.x);
float sy = sin(camera->angle.y); float sy = sin(camera->angle.y);
float cy = cos(camera->angle.y); float cy = cos(camera->angle.y);
return vec3(-(sy * cx), -sx, (cy * cx)); return vec3(-(sy * cx), -sx, (cy * cx));
} }
void camera_update(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update(camera_t *camera, ship_t *ship, droid_t *droid) {
camera->last_position = camera->position; camera->last_position = camera->position;
(camera->update_func)(camera, ship, droid); (camera->update_func)(camera, ship, droid);
camera->real_velocity = vec3_mulf(vec3_sub(camera->position, camera->last_position), 1.0/system_tick()); camera->real_velocity = vec3_mulf(vec3_sub(camera->position, camera->last_position), 1.0/system_tick());
} }
void camera_update_race_external(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_race_external(camera_t *camera, ship_t *ship, droid_t *droid) {
vec3_t pos = vec3_sub(ship->position, vec3_mulf(ship->dir_forward, 1024)); vec3_t pos = vec3_sub(ship->position, vec3_mulf(ship->dir_forward, 1024));
pos.y -= 200; pos.y -= 200;
camera->section = track_nearest_section(pos, camera->section, NULL); camera->section = track_nearest_section(pos, camera->section, NULL);
section_t *next = camera->section->next; section_t *next = camera->section->next;
vec3_t target = vec3_project_to_ray(pos, next->center, camera->section->center); vec3_t target = vec3_project_to_ray(pos, next->center, camera->section->center);
vec3_t diff_from_center = vec3_sub(pos, target); vec3_t diff_from_center = vec3_sub(pos, target);
vec3_t acc = diff_from_center; vec3_t acc = diff_from_center;
acc.y += vec3_len(diff_from_center) * 0.5; acc.y += vec3_len(diff_from_center) * 0.5;
camera->velocity = vec3_sub(camera->velocity, vec3_mulf(acc, 0.015625 * 30 * system_tick())); camera->velocity = vec3_sub(camera->velocity, vec3_mulf(acc, 0.015625 * 30 * system_tick()));
camera->velocity = vec3_sub(camera->velocity, vec3_mulf(camera->velocity, 0.125 * 30 * system_tick())); camera->velocity = vec3_sub(camera->velocity, vec3_mulf(camera->velocity, 0.125 * 30 * system_tick()));
pos = vec3_add(pos, camera->velocity); pos = vec3_add(pos, camera->velocity);
camera->position = pos; camera->position = pos;
camera->angle = vec3(ship->angle.x, ship->angle.y, 0); camera->angle = vec3(ship->angle.x, ship->angle.y, 0);
} }
void camera_update_race_internal(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_race_internal(camera_t *camera, ship_t *ship, droid_t *droid) {
camera->section = ship->section; camera->section = ship->section;
camera->position = ship_cockpit(ship); camera->position = ship_cockpit(ship);
camera->angle = vec3(ship->angle.x, ship->angle.y, ship->angle.z); camera->angle = vec3(ship->angle.x, ship->angle.y, ship->angle.z);
} }
void camera_update_race_intro(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_race_intro(camera_t *camera, ship_t *ship, droid_t *droid) {
// Set to final position // Set to final position
vec3_t pos = vec3_sub(ship->position, vec3_mulf(ship->dir_forward, 0.25 * 4096)); vec3_t pos = vec3_sub(ship->position, vec3_mulf(ship->dir_forward, 0.25 * 4096));
pos.x += sin(( (ship->update_timer - UPDATE_TIME_RACE_VIEW) * 30 * 3.0 * M_PI * 2) / 4096.0) * 4096; pos.x += sin(( (ship->update_timer - UPDATE_TIME_RACE_VIEW) * 30 * 3.0 * M_PI * 2) / 4096.0) * 4096;
pos.y -= (2 * (ship->update_timer - UPDATE_TIME_RACE_VIEW) * 30) + 200; pos.y -= (2 * (ship->update_timer - UPDATE_TIME_RACE_VIEW) * 30) + 200;
pos.z += sin(( (ship->update_timer - UPDATE_TIME_RACE_VIEW) * 30 * 3.0 * M_PI * 2) / 4096.0) * 4096; pos.z += sin(( (ship->update_timer - UPDATE_TIME_RACE_VIEW) * 30 * 3.0 * M_PI * 2) / 4096.0) * 4096;
if (!camera->has_initial_section) { if (!camera->has_initial_section) {
camera->section = ship->section; camera->section = ship->section;
camera->has_initial_section = true; camera->has_initial_section = true;
} }
else { else {
camera->section = track_nearest_section(pos, camera->section, NULL); camera->section = track_nearest_section(pos, camera->section, NULL);
} }
camera->position = pos; camera->position = pos;
camera->angle.z = 0; camera->angle.z = 0;
camera->angle.x = ship->angle.x * 0.5; camera->angle.x = ship->angle.x * 0.5;
vec3_t target = vec3_sub(ship->position, pos); vec3_t target = vec3_sub(ship->position, pos);
camera->angle.y = -atan2(target.x, target.z); camera->angle.y = -atan2(target.x, target.z);
if (ship->update_timer <= UPDATE_TIME_RACE_VIEW) { if (ship->update_timer <= UPDATE_TIME_RACE_VIEW) {
flags_add(ship->flags, SHIP_VIEW_INTERNAL); flags_add(ship->flags, SHIP_VIEW_INTERNAL);
camera->update_func = camera_update_race_internal; camera->update_func = camera_update_race_internal;
} }
} }
void camera_update_attract_circle(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_attract_circle(camera_t *camera, ship_t *ship, droid_t *droid) {
camera->update_timer -= system_tick(); camera->update_timer -= system_tick();
if (camera->update_timer <= 0) { if (camera->update_timer <= 0) {
camera->update_func = camera_update_attract_random; camera->update_func = camera_update_attract_random;
} }
// FIXME: not exactly sure what I'm doing here. The PSX version behaves // FIXME: not exactly sure what I'm doing here. The PSX version behaves
// differently. // differently.
camera->section = ship->section; camera->section = ship->section;
camera->position.x = ship->position.x + sin(ship->angle.y) * 512; camera->position.x = ship->position.x + sin(ship->angle.y) * 512;
camera->position.y = ship->position.y + ((ship->angle.x * 512 / (M_PI * 2)) - 200); camera->position.y = ship->position.y + ((ship->angle.x * 512 / (M_PI * 2)) - 200);
camera->position.z = ship->position.z - cos(ship->angle.y) * 512; camera->position.z = ship->position.z - cos(ship->angle.y) * 512;
camera->position.x += sin(camera->update_timer * 0.25) * 512; camera->position.x += sin(camera->update_timer * 0.25) * 512;
camera->position.y -= 400; camera->position.y -= 400;
camera->position.z += cos(camera->update_timer * 0.25) * 512; camera->position.z += cos(camera->update_timer * 0.25) * 512;
camera->position = vec3_sub(camera->position, vec3_mulf(ship->dir_up, 256)); camera->position = vec3_sub(camera->position, vec3_mulf(ship->dir_up, 256));
vec3_t target = vec3_sub(ship->position, camera->position); vec3_t target = vec3_sub(ship->position, camera->position);
float height = sqrt(target.x * target.x + target.z * target.z); float height = sqrt(target.x * target.x + target.z * target.z);
camera->angle.x = -atan2(target.y, height); camera->angle.x = -atan2(target.y, height);
camera->angle.y = -atan2(target.x, target.z); camera->angle.y = -atan2(target.x, target.z);
} }
void camera_update_rescue(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_rescue(camera_t *camera, ship_t *ship, droid_t *droid) {
camera->position = vec3_add(camera->section->center, vec3(300, -1500, 300)); camera->position = vec3_add(camera->section->center, vec3(300, -1500, 300));
vec3_t target = vec3_sub(droid->position, camera->position); vec3_t target = vec3_sub(droid->position, camera->position);
float height = sqrt(target.x * target.x + target.z * target.z); float height = sqrt(target.x * target.x + target.z * target.z);
camera->angle.x = -atan2(target.y, height); camera->angle.x = -atan2(target.y, height);
camera->angle.y = -atan2(target.x, target.z); camera->angle.y = -atan2(target.x, target.z);
} }
void camera_update_attract_internal(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_attract_internal(camera_t *camera, ship_t *ship, droid_t *droid) {
camera->update_timer -= system_tick(); camera->update_timer -= system_tick();
if (camera->update_timer <= 0) { if (camera->update_timer <= 0) {
camera->update_func = camera_update_attract_random; camera->update_func = camera_update_attract_random;
} }
camera->section = ship->section; camera->section = ship->section;
camera->position = ship_cockpit(ship); camera->position = ship_cockpit(ship);
camera->angle = vec3(ship->angle.x, ship->angle.y, 0); // No roll camera->angle = vec3(ship->angle.x, ship->angle.y, 0); // No roll
} }
void camera_update_static_follow(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_static_follow(camera_t *camera, ship_t *ship, droid_t *droid) {
camera->update_timer -= system_tick(); camera->update_timer -= system_tick();
if (camera->update_timer <= 0) { if (camera->update_timer <= 0) {
camera->update_func = camera_update_attract_random; camera->update_func = camera_update_attract_random;
} }
vec3_t target = vec3_sub(ship->position, camera->position); vec3_t target = vec3_sub(ship->position, camera->position);
float height = sqrt(target.x * target.x + target.z * target.z); float height = sqrt(target.x * target.x + target.z * target.z);
camera->angle.x = -atan2(target.y, height); camera->angle.x = -atan2(target.y, height);
camera->angle.y = -atan2(target.x, target.z); camera->angle.y = -atan2(target.x, target.z);
} }
void camera_update_attract_random(camera_t *camera, ship_t *ship, droid_t *droid) { void camera_update_attract_random(camera_t *camera, ship_t *ship, droid_t *droid) {
flags_rm(ship->flags, SHIP_VIEW_INTERNAL); flags_rm(ship->flags, SHIP_VIEW_INTERNAL);
if (rand() % 2) { if (rand() % 2) {
camera->update_func = camera_update_attract_circle; camera->update_func = camera_update_attract_circle;
camera->update_timer = 5; camera->update_timer = 5;
} }
else { else {
camera->update_func = camera_update_static_follow; camera->update_func = camera_update_static_follow;
camera->update_timer = 5; camera->update_timer = 5;
section_t *section = ship->section->next; section_t *section = ship->section->next;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
section = section->next; section = section->next;
} }
camera->section = section; camera->section = section;
camera->position = section->center; camera->position = section->center;
camera->position.y -= 500; camera->position.y -= 500;
} }
(camera->update_func)(camera, ship, droid); (camera->update_func)(camera, ship, droid);
} }

View file

@ -1,32 +1,32 @@
#ifndef CAMERA_H #ifndef CAMERA_H
#define CAMERA_H #define CAMERA_H
#include "../types.h" #include "../types.h"
#include "droid.h" #include "droid.h"
typedef struct camera_t { typedef struct camera_t {
vec3_t position; vec3_t position;
vec3_t velocity; vec3_t velocity;
vec3_t angle; vec3_t angle;
vec3_t angular_velocity; vec3_t angular_velocity;
vec3_t last_position; vec3_t last_position;
vec3_t real_velocity; vec3_t real_velocity;
section_t *section; section_t *section;
bool has_initial_section; bool has_initial_section;
float update_timer; float update_timer;
void (*update_func)(struct camera_t *, ship_t *, droid_t *); void (*update_func)(struct camera_t *, ship_t *, droid_t *);
} camera_t; } camera_t;
void camera_init(camera_t *camera, section_t *section); void camera_init(camera_t *camera, section_t *section);
vec3_t camera_forward(camera_t *camera); vec3_t camera_forward(camera_t *camera);
void camera_update(camera_t *camera, ship_t *ship, droid_t *droid); void camera_update(camera_t *camera, ship_t *ship, droid_t *droid);
void camera_update_race_external(camera_t *, ship_t *camShip, droid_t *); void camera_update_race_external(camera_t *, ship_t *camShip, droid_t *);
void camera_update_race_internal(camera_t *, ship_t *camShip, droid_t *); void camera_update_race_internal(camera_t *, ship_t *camShip, droid_t *);
void camera_update_race_intro(camera_t *, ship_t *camShip, droid_t *); void camera_update_race_intro(camera_t *, ship_t *camShip, droid_t *);
void camera_update_attract_circle(camera_t *, ship_t *camShip, droid_t *); void camera_update_attract_circle(camera_t *, ship_t *camShip, droid_t *);
void camera_update_attract_internal(camera_t *, ship_t *camShip, droid_t *); void camera_update_attract_internal(camera_t *, ship_t *camShip, droid_t *);
void camera_update_static_follow(camera_t *, ship_t *camShip, droid_t *); void camera_update_static_follow(camera_t *, ship_t *camShip, droid_t *);
void camera_update_attract_random(camera_t *, ship_t *camShip, droid_t *); void camera_update_attract_random(camera_t *, ship_t *camShip, droid_t *);
void camera_update_rescue(camera_t *, ship_t *camShip, droid_t *); void camera_update_rescue(camera_t *, ship_t *camShip, droid_t *);
#endif #endif

View file

@ -1,260 +1,260 @@
#include "../types.h" #include "../types.h"
#include "../mem.h" #include "../mem.h"
#include "../system.h" #include "../system.h"
#include "../utils.h" #include "../utils.h"
#include "object.h" #include "object.h"
#include "track.h" #include "track.h"
#include "ship.h" #include "ship.h"
#include "weapon.h" #include "weapon.h"
#include "hud.h" #include "hud.h"
#include "droid.h" #include "droid.h"
#include "camera.h" #include "camera.h"
#include "image.h" #include "image.h"
#include "scene.h" #include "scene.h"
#include "object.h" #include "object.h"
#include "game.h" #include "game.h"
static Object *droid_model; static Object *droid_model;
void droid_load(void) { void droid_load(void) {
texture_list_t droid_textures = image_get_compressed_textures("wipeout/common/rescu.cmp"); texture_list_t droid_textures = image_get_compressed_textures("wipeout/common/rescu.cmp");
droid_model = objects_load("wipeout/common/rescu.prm", droid_textures); droid_model = objects_load("wipeout/common/rescu.prm", droid_textures);
} }
void droid_init(droid_t *droid, ship_t *ship) { void droid_init(droid_t *droid, ship_t *ship) {
droid->section = g.track.sections; droid->section = g.track.sections;
while (flags_not(droid->section->flags, SECTION_JUMP)) { while (flags_not(droid->section->flags, SECTION_JUMP)) {
droid->section = droid->section->next; droid->section = droid->section->next;
} }
droid->position = vec3_add(ship->position, vec3(0, -200, 0)); droid->position = vec3_add(ship->position, vec3(0, -200, 0));
droid->velocity = vec3(0, 0, 0); droid->velocity = vec3(0, 0, 0);
droid->acceleration = vec3(0, 0, 0); droid->acceleration = vec3(0, 0, 0);
droid->angle = vec3(0, 0, 0); droid->angle = vec3(0, 0, 0);
droid->angular_velocity = vec3(0, 0, 0); droid->angular_velocity = vec3(0, 0, 0);
droid->update_timer = DROID_UPDATE_TIME_INITIAL; droid->update_timer = DROID_UPDATE_TIME_INITIAL;
droid->mat = mat4_identity(); droid->mat = mat4_identity();
droid->cycle_timer = 0; droid->cycle_timer = 0;
droid->update_func = droid_update_intro; droid->update_func = droid_update_intro;
droid->sfx_tractor = sfx_reserve_loop(SFX_TRACTOR); droid->sfx_tractor = sfx_reserve_loop(SFX_TRACTOR);
flags_rm(droid->sfx_tractor->flags, SFX_PLAY); flags_rm(droid->sfx_tractor->flags, SFX_PLAY);
} }
void droid_draw(droid_t *droid) { void droid_draw(droid_t *droid) {
droid->cycle_timer += system_tick() * M_PI * 2; droid->cycle_timer += system_tick() * M_PI * 2;
Prm prm = {.primitive = droid_model->primitives}; Prm prm = {.primitive = droid_model->primitives};
int rf = sin(droid->cycle_timer) * 127 + 128; int rf = sin(droid->cycle_timer) * 127 + 128;
int gf = sin(droid->cycle_timer + 0.2) * 127 + 128; int gf = sin(droid->cycle_timer + 0.2) * 127 + 128;
int bf = sin(droid->cycle_timer * 0.5 + 0.1) * 127 + 128; int bf = sin(droid->cycle_timer * 0.5 + 0.1) * 127 + 128;
int r, g, b; int r, g, b;
for (int i = 0; i < 11; i++) { for (int i = 0; i < 11; i++) {
if (i < 2) { if (i < 2) {
r = 40; r = 40;
g = gf; g = gf;
b = 40; b = 40;
} }
else if (i < 6) { else if (i < 6) {
r = bf >> 1; r = bf >> 1;
b = bf; b = bf;
g = bf >> 1; g = bf >> 1;
} }
else { else {
r = rf; r = rf;
b = 40; b = 40;
g = 40; g = 40;
} }
switch (prm.f3->type) { switch (prm.f3->type) {
case PRM_TYPE_GT3: case PRM_TYPE_GT3:
prm.gt3->color[0].r = r; prm.gt3->color[0].r = r;
prm.gt3->color[0].g = g; prm.gt3->color[0].g = g;
prm.gt3->color[0].b = b; prm.gt3->color[0].b = b;
prm.gt3->color[1].r = r; prm.gt3->color[1].r = r;
prm.gt3->color[1].g = g; prm.gt3->color[1].g = g;
prm.gt3->color[1].b = b; prm.gt3->color[1].b = b;
prm.gt3->color[2].r = r; prm.gt3->color[2].r = r;
prm.gt3->color[2].g = g; prm.gt3->color[2].g = g;
prm.gt3->color[2].b = b; prm.gt3->color[2].b = b;
prm.gt3++; prm.gt3++;
break; break;
case PRM_TYPE_GT4: case PRM_TYPE_GT4:
prm.gt4->color[0].r = r; prm.gt4->color[0].r = r;
prm.gt4->color[0].g = g; prm.gt4->color[0].g = g;
prm.gt4->color[0].b = b; prm.gt4->color[0].b = b;
prm.gt4->color[1].r = r; prm.gt4->color[1].r = r;
prm.gt4->color[1].g = g; prm.gt4->color[1].g = g;
prm.gt4->color[1].b = b; prm.gt4->color[1].b = b;
prm.gt4->color[2].r = r; prm.gt4->color[2].r = r;
prm.gt4->color[2].g = g; prm.gt4->color[2].g = g;
prm.gt4->color[2].b = b; prm.gt4->color[2].b = b;
prm.gt4->color[3].r = 40; prm.gt4->color[3].r = 40;
prm.gt4->color[3].g = 40; prm.gt4->color[3].g = 40;
prm.gt4->color[3].b = 40; prm.gt4->color[3].b = 40;
prm.gt4++; prm.gt4++;
break; break;
} }
} }
mat4_set_translation(&droid->mat, droid->position); mat4_set_translation(&droid->mat, droid->position);
mat4_set_yaw_pitch_roll(&droid->mat, droid->angle); mat4_set_yaw_pitch_roll(&droid->mat, droid->angle);
object_draw(droid_model, &droid->mat); object_draw(droid_model, &droid->mat);
} }
void droid_update(droid_t *droid, ship_t *ship) { void droid_update(droid_t *droid, ship_t *ship) {
(droid->update_func)(droid, ship); (droid->update_func)(droid, ship);
droid->velocity = vec3_add(droid->velocity, vec3_mulf(droid->acceleration, 30 * system_tick())); droid->velocity = vec3_add(droid->velocity, vec3_mulf(droid->acceleration, 30 * system_tick()));
droid->velocity = vec3_sub(droid->velocity, vec3_mulf(droid->velocity, 0.125 * 30 * system_tick())); droid->velocity = vec3_sub(droid->velocity, vec3_mulf(droid->velocity, 0.125 * 30 * system_tick()));
droid->position = vec3_add(droid->position, vec3_mulf(droid->velocity, 0.015625 * 30 * system_tick())); droid->position = vec3_add(droid->position, vec3_mulf(droid->velocity, 0.015625 * 30 * system_tick()));
droid->angle = vec3_add(droid->angle, vec3_mulf(droid->angular_velocity, system_tick())); droid->angle = vec3_add(droid->angle, vec3_mulf(droid->angular_velocity, system_tick()));
droid->angle = vec3_wrap_angle(droid->angle); droid->angle = vec3_wrap_angle(droid->angle);
if (flags_is(droid->sfx_tractor->flags, SFX_PLAY)) { if (flags_is(droid->sfx_tractor->flags, SFX_PLAY)) {
sfx_set_position(droid->sfx_tractor, droid->position, droid->velocity, 0.5); sfx_set_position(droid->sfx_tractor, droid->position, droid->velocity, 0.5);
} }
} }
void droid_update_intro(droid_t *droid, ship_t *ship) { void droid_update_intro(droid_t *droid, ship_t *ship) {
droid->update_timer -= system_tick(); droid->update_timer -= system_tick();
if (droid->update_timer < DROID_UPDATE_TIME_INTRO_3) { if (droid->update_timer < DROID_UPDATE_TIME_INTRO_3) {
droid->acceleration.x = (-sin(droid->angle.y) * cos(droid->angle.x)) * 0.25 * 4096.0; droid->acceleration.x = (-sin(droid->angle.y) * cos(droid->angle.x)) * 0.25 * 4096.0;
droid->acceleration.y = 0; droid->acceleration.y = 0;
droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.25 * 4096.0; droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.25 * 4096.0;
droid->angular_velocity.y = 0; droid->angular_velocity.y = 0;
} }
else if (droid->update_timer < DROID_UPDATE_TIME_INTRO_2) { else if (droid->update_timer < DROID_UPDATE_TIME_INTRO_2) {
droid->acceleration.x = (-sin(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096.0; droid->acceleration.x = (-sin(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096.0;
droid->acceleration.y = -140; droid->acceleration.y = -140;
droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096.0; droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096.0;
droid->angular_velocity.y = (-8.0 / 4096.0) * M_PI * 2 * 30; droid->angular_velocity.y = (-8.0 / 4096.0) * M_PI * 2 * 30;
} }
else if (droid->update_timer < DROID_UPDATE_TIME_INTRO_1) { else if (droid->update_timer < DROID_UPDATE_TIME_INTRO_1) {
droid->acceleration.y -= 90 * system_tick(); droid->acceleration.y -= 90 * system_tick();
droid->angular_velocity.y = (8.0 / 4096.0) * M_PI * 2 * 30; droid->angular_velocity.y = (8.0 / 4096.0) * M_PI * 2 * 30;
} }
if (droid->update_timer <= 0) { if (droid->update_timer <= 0) {
droid->update_timer = DROID_UPDATE_TIME_INITIAL; droid->update_timer = DROID_UPDATE_TIME_INITIAL;
droid->update_func = droid_update_idle; droid->update_func = droid_update_idle;
droid->position.x = droid->section->center.x; droid->position.x = droid->section->center.x;
droid->position.y = -3000; droid->position.y = -3000;
droid->position.z = droid->section->center.z; droid->position.z = droid->section->center.z;
} }
} }
void droid_update_idle(droid_t *droid, ship_t *ship) { void droid_update_idle(droid_t *droid, ship_t *ship) {
section_t *next = droid->section->next; section_t *next = droid->section->next;
vec3_t target = vec3( vec3_t target = vec3(
(droid->section->center.x + next->center.x) * 0.5, (droid->section->center.x + next->center.x) * 0.5,
droid->section->center.y - 3000, droid->section->center.y - 3000,
(droid->section->center.z + next->center.z) * 0.5 (droid->section->center.z + next->center.z) * 0.5
); );
vec3_t target_vector = vec3_sub(target, droid->position); vec3_t target_vector = vec3_sub(target, droid->position);
float target_heading = -atan2(target_vector.x, target_vector.z); float target_heading = -atan2(target_vector.x, target_vector.z);
float quickest_turn = target_heading - droid->angle.y; float quickest_turn = target_heading - droid->angle.y;
float turn; float turn;
if (droid->angle.y < 0) { if (droid->angle.y < 0) {
turn = target_heading - (droid->angle.y + M_PI*2); turn = target_heading - (droid->angle.y + M_PI*2);
} }
else { else {
turn = target_heading - (droid->angle.y - M_PI*2); turn = target_heading - (droid->angle.y - M_PI*2);
} }
if (fabsf(turn) < fabsf(quickest_turn)) { if (fabsf(turn) < fabsf(quickest_turn)) {
droid->angular_velocity.y = turn * 30 / 64.0; droid->angular_velocity.y = turn * 30 / 64.0;
} }
else { else {
droid->angular_velocity.y = quickest_turn * 30.0 / 64.0; droid->angular_velocity.y = quickest_turn * 30.0 / 64.0;
} }
droid->acceleration.x = (-sin(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096; droid->acceleration.x = (-sin(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096;
droid->acceleration.y = target_vector.y / 64.0; droid->acceleration.y = target_vector.y / 64.0;
droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096; droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096;
if (flags_is(ship->flags, SHIP_IN_RESCUE)) { if (flags_is(ship->flags, SHIP_IN_RESCUE)) {
flags_add(droid->sfx_tractor->flags, SFX_PLAY); flags_add(droid->sfx_tractor->flags, SFX_PLAY);
droid->update_func = droid_update_rescue; droid->update_func = droid_update_rescue;
droid->update_timer = DROID_UPDATE_TIME_INITIAL; droid->update_timer = DROID_UPDATE_TIME_INITIAL;
g.camera.update_func = camera_update_rescue; g.camera.update_func = camera_update_rescue;
flags_add(ship->flags, SHIP_VIEW_REMOTE); flags_add(ship->flags, SHIP_VIEW_REMOTE);
if (flags_is(ship->section->flags, SECTION_JUMP)) { if (flags_is(ship->section->flags, SECTION_JUMP)) {
g.camera.section = ship->section->next; g.camera.section = ship->section->next;
} }
else { else {
g.camera.section = ship->section; g.camera.section = ship->section;
} }
// If droid is not nearby the rescue position teleport it in! // If droid is not nearby the rescue position teleport it in!
if (droid->section != ship->section && droid->section != ship->section->prev) { if (droid->section != ship->section && droid->section != ship->section->prev) {
droid->section = ship->section; droid->section = ship->section;
section_t *next = droid->section->next; section_t *next = droid->section->next;
droid->position.x = (droid->section->center.x + next->center.x) * 0.5; droid->position.x = (droid->section->center.x + next->center.x) * 0.5;
droid->position.y = droid->section->center.y - 3000; droid->position.y = droid->section->center.y - 3000;
droid->position.z = (droid->section->center.z + next->center.z) * 0.5; droid->position.z = (droid->section->center.z + next->center.z) * 0.5;
} }
flags_rm(ship->flags, SHIP_IN_TOW); flags_rm(ship->flags, SHIP_IN_TOW);
droid->velocity = vec3(0,0,0); droid->velocity = vec3(0,0,0);
droid->acceleration = vec3(0,0,0); droid->acceleration = vec3(0,0,0);
} }
// AdjustDirectionalNote(START_SIREN, 0, 0, (VECTOR){droid->position.x, droid->position.y, droid->position.z}); // AdjustDirectionalNote(START_SIREN, 0, 0, (VECTOR){droid->position.x, droid->position.y, droid->position.z});
} }
void droid_update_rescue(droid_t *droid, ship_t *ship) { void droid_update_rescue(droid_t *droid, ship_t *ship) {
droid->angular_velocity.y = 0; droid->angular_velocity.y = 0;
droid->angle.y = ship->angle.y; droid->angle.y = ship->angle.y;
vec3_t target = vec3(ship->position.x, ship->position.y - 350, ship->position.z); vec3_t target = vec3(ship->position.x, ship->position.y - 350, ship->position.z);
vec3_t distance = vec3_sub(target, droid->position); vec3_t distance = vec3_sub(target, droid->position);
if (flags_is(ship->flags, SHIP_IN_TOW)) { if (flags_is(ship->flags, SHIP_IN_TOW)) {
droid->velocity = vec3(0,0,0); droid->velocity = vec3(0,0,0);
droid->acceleration = vec3(0,0,0); droid->acceleration = vec3(0,0,0);
droid->position = target; droid->position = target;
} }
else if (vec3_len(distance) < 8) { else if (vec3_len(distance) < 8) {
flags_add(ship->flags, SHIP_IN_TOW); flags_add(ship->flags, SHIP_IN_TOW);
droid->velocity = vec3(0,0,0); droid->velocity = vec3(0,0,0);
droid->acceleration = vec3(0,0,0); droid->acceleration = vec3(0,0,0);
droid->position = target; droid->position = target;
} }
else { else {
droid->velocity = vec3_mulf(distance, 16); droid->velocity = vec3_mulf(distance, 16);
} }
// Are we done rescuing? // Are we done rescuing?
if (flags_not(ship->flags, SHIP_IN_RESCUE)) { if (flags_not(ship->flags, SHIP_IN_RESCUE)) {
flags_rm(droid->sfx_tractor->flags, SFX_PLAY); flags_rm(droid->sfx_tractor->flags, SFX_PLAY);
droid->siren_started = false; droid->siren_started = false;
droid->update_func = droid_update_idle; droid->update_func = droid_update_idle;
droid->update_timer = DROID_UPDATE_TIME_INITIAL; droid->update_timer = DROID_UPDATE_TIME_INITIAL;
while (flags_not(droid->section->flags, SECTION_JUMP)) { while (flags_not(droid->section->flags, SECTION_JUMP)) {
droid->section = droid->section->prev; droid->section = droid->section->prev;
} }
} }
} }

View file

@ -1,39 +1,39 @@
#ifndef DROID_H #ifndef DROID_H
#define DROID_H #define DROID_H
#include "../types.h" #include "../types.h"
#include "track.h" #include "track.h"
#include "ship.h" #include "ship.h"
#include "sfx.h" #include "sfx.h"
#define DROID_UPDATE_TIME_INITIAL (800 * (1.0/30.0)) #define DROID_UPDATE_TIME_INITIAL (800 * (1.0/30.0))
#define DROID_UPDATE_TIME_INTRO_1 (770 * (1.0/30.0)) #define DROID_UPDATE_TIME_INTRO_1 (770 * (1.0/30.0))
#define DROID_UPDATE_TIME_INTRO_2 (710 * (1.0/30.0)) #define DROID_UPDATE_TIME_INTRO_2 (710 * (1.0/30.0))
#define DROID_UPDATE_TIME_INTRO_3 (400 * (1.0/30.0)) #define DROID_UPDATE_TIME_INTRO_3 (400 * (1.0/30.0))
typedef struct droid_t { typedef struct droid_t {
section_t *section; section_t *section;
vec3_t position; vec3_t position;
vec3_t velocity; vec3_t velocity;
vec3_t acceleration; vec3_t acceleration;
vec3_t angle; vec3_t angle;
vec3_t angular_velocity; vec3_t angular_velocity;
bool siren_started; bool siren_started;
float cycle_timer; float cycle_timer;
float update_timer; float update_timer;
void (*update_func)(struct droid_t *, ship_t *); void (*update_func)(struct droid_t *, ship_t *);
mat4_t mat; mat4_t mat;
Object *model; Object *model;
sfx_t *sfx_tractor; sfx_t *sfx_tractor;
} droid_t; } droid_t;
void droid_draw(droid_t *droid); void droid_draw(droid_t *droid);
void droid_load(void); void droid_load(void);
void droid_init(droid_t *droid, ship_t *ship); void droid_init(droid_t *droid, ship_t *ship);
void droid_update(droid_t *droid, ship_t *ship); void droid_update(droid_t *droid, ship_t *ship);
void droid_update_intro(droid_t *droid, ship_t *ship); void droid_update_intro(droid_t *droid, ship_t *ship);
void droid_update_idle(droid_t *droid, ship_t *ship); void droid_update_idle(droid_t *droid, ship_t *ship);
void droid_update_rescue(droid_t *droid, ship_t *ship); void droid_update_rescue(droid_t *droid, ship_t *ship);
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -1,270 +1,270 @@
#ifndef GAME_H #ifndef GAME_H
#define GAME_H #define GAME_H
#include "../types.h" #include "../types.h"
#include "droid.h" #include "droid.h"
#include "ship.h" #include "ship.h"
#include "camera.h" #include "camera.h"
#include "track.h" #include "track.h"
#define NUM_AI_OPPONENTS 7 #define NUM_AI_OPPONENTS 7
#define NUM_PILOTS_PER_TEAM 2 #define NUM_PILOTS_PER_TEAM 2
#define NUM_NON_BONUS_CIRCUTS 6 #define NUM_NON_BONUS_CIRCUTS 6
#define NUM_MUSIC_TRACKS 11 #define NUM_MUSIC_TRACKS 11
#define NUM_HIGHSCORES 5 #define NUM_HIGHSCORES 5
#define NUM_LAPS 3 #define NUM_LAPS 3
#define NUM_LIVES 3 #define NUM_LIVES 3
#define QUALIFYING_RANK 3 #define QUALIFYING_RANK 3
#define SAVE_DATA_MAGIC 0x64736f77 #define SAVE_DATA_MAGIC 0x64736f77
typedef enum { typedef enum {
A_UP, A_UP,
A_DOWN, A_DOWN,
A_LEFT, A_LEFT,
A_RIGHT, A_RIGHT,
A_BRAKE_LEFT, A_BRAKE_LEFT,
A_BRAKE_RIGHT, A_BRAKE_RIGHT,
A_THRUST, A_THRUST,
A_FIRE, A_FIRE,
A_CHANGE_VIEW, A_CHANGE_VIEW,
NUM_GAME_ACTIONS, NUM_GAME_ACTIONS,
A_MENU_UP, A_MENU_UP,
A_MENU_DOWN, A_MENU_DOWN,
A_MENU_LEFT, A_MENU_LEFT,
A_MENU_RIGHT, A_MENU_RIGHT,
A_MENU_BACK, A_MENU_BACK,
A_MENU_SELECT, A_MENU_SELECT,
A_MENU_START, A_MENU_START,
A_MENU_QUIT, A_MENU_QUIT,
} action_t; } action_t;
typedef enum { typedef enum {
GAME_SCENE_INTRO, GAME_SCENE_INTRO,
GAME_SCENE_TITLE, GAME_SCENE_TITLE,
GAME_SCENE_MAIN_MENU, GAME_SCENE_MAIN_MENU,
GAME_SCENE_HIGHSCORES, GAME_SCENE_HIGHSCORES,
GAME_SCENE_RACE, GAME_SCENE_RACE,
GAME_SCENE_NONE, GAME_SCENE_NONE,
NUM_GAME_SCENES NUM_GAME_SCENES
} game_scene_t; } game_scene_t;
enum race_class { enum race_class {
RACE_CLASS_VENOM, RACE_CLASS_VENOM,
RACE_CLASS_RAPIER, RACE_CLASS_RAPIER,
NUM_RACE_CLASSES NUM_RACE_CLASSES
}; };
enum race_type { enum race_type {
RACE_TYPE_CHAMPIONSHIP, RACE_TYPE_CHAMPIONSHIP,
RACE_TYPE_SINGLE, RACE_TYPE_SINGLE,
RACE_TYPE_TIME_TRIAL, RACE_TYPE_TIME_TRIAL,
NUM_RACE_TYPES, NUM_RACE_TYPES,
}; };
enum highscore_tab { enum highscore_tab {
HIGHSCORE_TAB_TIME_TRIAL, HIGHSCORE_TAB_TIME_TRIAL,
HIGHSCORE_TAB_RACE, HIGHSCORE_TAB_RACE,
NUM_HIGHSCORE_TABS NUM_HIGHSCORE_TABS
}; };
enum pilot { enum pilot {
PILOT_JOHN_DEKKA, PILOT_JOHN_DEKKA,
PILOT_DANIEL_CHANG, PILOT_DANIEL_CHANG,
PILOT_ARIAL_TETSUO, PILOT_ARIAL_TETSUO,
PILOT_ANASTASIA_CHEROVOSKI, PILOT_ANASTASIA_CHEROVOSKI,
PILOT_KEL_SOLAAR, PILOT_KEL_SOLAAR,
PILOT_ARIAN_TETSUO, PILOT_ARIAN_TETSUO,
PILOT_SOFIA_DE_LA_RENTE, PILOT_SOFIA_DE_LA_RENTE,
PILOT_PAUL_JACKSON, PILOT_PAUL_JACKSON,
NUM_PILOTS NUM_PILOTS
}; };
enum team { enum team {
TEAM_AG_SYSTEMS, TEAM_AG_SYSTEMS,
TEAM_AURICOM, TEAM_AURICOM,
TEAM_QIREX, TEAM_QIREX,
TEAM_FEISAR, TEAM_FEISAR,
NUM_TEAMS NUM_TEAMS
}; };
enum circut { enum circut {
CIRCUT_ALTIMA_VII, CIRCUT_ALTIMA_VII,
CIRCUT_KARBONIS_V, CIRCUT_KARBONIS_V,
CIRCUT_TERRAMAX, CIRCUT_TERRAMAX,
CIRCUT_KORODERA, CIRCUT_KORODERA,
CIRCUT_ARRIDOS_IV, CIRCUT_ARRIDOS_IV,
CIRCUT_SILVERSTREAM, CIRCUT_SILVERSTREAM,
CIRCUT_FIRESTAR, CIRCUT_FIRESTAR,
NUM_CIRCUTS NUM_CIRCUTS
}; };
// Game definitions // Game definitions
typedef struct { typedef struct {
char *name; char *name;
} race_class_t; } race_class_t;
typedef struct { typedef struct {
char *name; char *name;
} race_type_t; } race_type_t;
typedef struct { typedef struct {
char *name; char *name;
char *portrait; char *portrait;
int logo_model; int logo_model;
int team; int team;
} pilot_t; } pilot_t;
typedef struct { typedef struct {
float thrust_max; float thrust_max;
float thrust_magnitude; float thrust_magnitude;
bool fight_back; bool fight_back;
} ai_setting_t; } ai_setting_t;
typedef struct { typedef struct {
float mass; float mass;
float thrust_max; float thrust_max;
float resistance; float resistance;
float turn_rate; float turn_rate;
float turn_rate_max; float turn_rate_max;
float skid; float skid;
} team_attributes_t; } team_attributes_t;
typedef struct { typedef struct {
char *name; char *name;
int logo_model; int logo_model;
int pilots[NUM_PILOTS_PER_TEAM]; int pilots[NUM_PILOTS_PER_TEAM];
team_attributes_t attributes[NUM_RACE_CLASSES]; team_attributes_t attributes[NUM_RACE_CLASSES];
} team_t; } team_t;
typedef struct { typedef struct {
char *path; char *path;
float start_line_pos; float start_line_pos;
float behind_speed; float behind_speed;
float spread_base; float spread_base;
float spread_factor; float spread_factor;
float sky_y_offset; float sky_y_offset;
} circut_settings_t; } circut_settings_t;
typedef struct { typedef struct {
char *name; char *name;
bool is_bonus_circut; bool is_bonus_circut;
circut_settings_t settings[NUM_RACE_CLASSES]; circut_settings_t settings[NUM_RACE_CLASSES];
} circut_t; } circut_t;
typedef struct { typedef struct {
char *path; char *path;
char *name; char *name;
} music_track_t; } music_track_t;
typedef struct { typedef struct {
race_class_t race_classes[NUM_RACE_CLASSES]; race_class_t race_classes[NUM_RACE_CLASSES];
race_type_t race_types[NUM_RACE_TYPES]; race_type_t race_types[NUM_RACE_TYPES];
pilot_t pilots[NUM_PILOTS]; pilot_t pilots[NUM_PILOTS];
team_t teams[NUM_TEAMS]; team_t teams[NUM_TEAMS];
ai_setting_t ai_settings[NUM_RACE_CLASSES][NUM_AI_OPPONENTS]; ai_setting_t ai_settings[NUM_RACE_CLASSES][NUM_AI_OPPONENTS];
circut_t circuts[NUM_CIRCUTS]; circut_t circuts[NUM_CIRCUTS];
int ship_model_to_pilot[NUM_PILOTS]; int ship_model_to_pilot[NUM_PILOTS];
int race_points_for_rank[NUM_PILOTS]; int race_points_for_rank[NUM_PILOTS];
music_track_t music[NUM_MUSIC_TRACKS]; music_track_t music[NUM_MUSIC_TRACKS];
char *credits[104]; char *credits[104];
struct { struct {
char *venom[15]; char *venom[15];
char *venom_all_circuts[19]; char *venom_all_circuts[19];
char *rapier[26]; char *rapier[26];
char *rapier_all_circuts[24]; char *rapier_all_circuts[24];
} congratulations; } congratulations;
} game_def_t; } game_def_t;
// Running game data // Running game data
typedef struct { typedef struct {
uint16_t pilot; uint16_t pilot;
uint16_t points; uint16_t points;
} pilot_points_t; } pilot_points_t;
typedef struct { typedef struct {
float frame_time; float frame_time;
float frame_rate; float frame_rate;
int race_class; int race_class;
int race_type; int race_type;
int highscore_tab; int highscore_tab;
int team; int team;
int pilot; int pilot;
int circut; int circut;
bool is_attract_mode; bool is_attract_mode;
bool show_credits; bool show_credits;
bool is_new_lap_record; bool is_new_lap_record;
bool is_new_race_record; bool is_new_race_record;
float best_lap; float best_lap;
float race_time; float race_time;
int lives; int lives;
int race_position; int race_position;
float lap_times[NUM_PILOTS][NUM_LAPS]; float lap_times[NUM_PILOTS][NUM_LAPS];
pilot_points_t race_ranks[NUM_PILOTS]; pilot_points_t race_ranks[NUM_PILOTS];
pilot_points_t championship_ranks[NUM_PILOTS]; pilot_points_t championship_ranks[NUM_PILOTS];
camera_t camera; camera_t camera;
droid_t droid; droid_t droid;
ship_t ships[NUM_PILOTS]; ship_t ships[NUM_PILOTS];
track_t track; track_t track;
} game_t; } game_t;
// Save Data // Save Data
typedef struct { typedef struct {
char name[4]; char name[4];
float time; float time;
} highscores_entry_t; } highscores_entry_t;
typedef struct { typedef struct {
highscores_entry_t entries[NUM_HIGHSCORES]; highscores_entry_t entries[NUM_HIGHSCORES];
float lap_record; float lap_record;
} highscores_t; } highscores_t;
typedef struct { typedef struct {
uint32_t magic; uint32_t magic;
bool is_dirty; bool is_dirty;
float sfx_volume; float sfx_volume;
float music_volume; float music_volume;
uint8_t ui_scale; uint8_t ui_scale;
bool show_fps; bool show_fps;
bool fullscreen; bool fullscreen;
int screen_res; int screen_res;
int post_effect; int post_effect;
uint32_t has_rapier_class; uint32_t has_rapier_class;
uint32_t has_bonus_circuts; uint32_t has_bonus_circuts;
uint8_t buttons[NUM_GAME_ACTIONS][2]; uint8_t buttons[NUM_GAME_ACTIONS][2];
char highscores_name[4]; char highscores_name[4];
highscores_t highscores[NUM_RACE_CLASSES][NUM_CIRCUTS][NUM_HIGHSCORE_TABS]; highscores_t highscores[NUM_RACE_CLASSES][NUM_CIRCUTS][NUM_HIGHSCORE_TABS];
} save_t; } save_t;
extern const game_def_t def; extern const game_def_t def;
extern game_t g; extern game_t g;
extern save_t save; extern save_t save;
void game_init(void); void game_init(void);
void game_set_scene(game_scene_t scene); void game_set_scene(game_scene_t scene);
void game_reset_championship(void); void game_reset_championship(void);
void game_update(void); void game_update(void);
#endif #endif

View file

@ -1,253 +1,253 @@
#include "../types.h" #include "../types.h"
#include "../mem.h" #include "../mem.h"
#include "../utils.h" #include "../utils.h"
#include "../system.h" #include "../system.h"
#include "object.h" #include "object.h"
#include "track.h" #include "track.h"
#include "ship.h" #include "ship.h"
#include "weapon.h" #include "weapon.h"
#include "hud.h" #include "hud.h"
#include "droid.h" #include "droid.h"
#include "camera.h" #include "camera.h"
#include "image.h" #include "image.h"
#include "ship_ai.h" #include "ship_ai.h"
#include "game.h" #include "game.h"
#include "ui.h" #include "ui.h"
static texture_list_t weapon_icon_textures; static texture_list_t weapon_icon_textures;
static uint16_t target_reticle; static uint16_t target_reticle;
typedef struct { typedef struct {
vec2i_t offset; vec2i_t offset;
uint16_t height; uint16_t height;
rgba_t color; rgba_t color;
} speedo_bar_t; } speedo_bar_t;
const struct { const struct {
uint16_t width; uint16_t width;
uint16_t skew; uint16_t skew;
speedo_bar_t bars[13]; speedo_bar_t bars[13];
} speedo = { } speedo = {
.width = 121, .width = 121,
.skew = 2, .skew = 2,
.bars = { .bars = {
{{.x = 6, .y = 12}, .height = 10, .color = rgba( 66, 16, 49, 255)}, {{.x = 6, .y = 12}, .height = 10, .color = rgba( 66, 16, 49, 255)},
{{.x = 13, .y = 12}, .height = 10, .color = rgba(115, 33, 90, 255)}, {{.x = 13, .y = 12}, .height = 10, .color = rgba(115, 33, 90, 255)},
{{.x = 20, .y = 12}, .height = 10, .color = rgba(132, 58, 164, 255)}, {{.x = 20, .y = 12}, .height = 10, .color = rgba(132, 58, 164, 255)},
{{.x = 27, .y = 12}, .height = 10, .color = rgba( 99, 90, 197, 255)}, {{.x = 27, .y = 12}, .height = 10, .color = rgba( 99, 90, 197, 255)},
{{.x = 34, .y = 12}, .height = 10, .color = rgba( 74, 148, 181, 255)}, {{.x = 34, .y = 12}, .height = 10, .color = rgba( 74, 148, 181, 255)},
{{.x = 41, .y = 12}, .height = 10, .color = rgba( 66, 173, 115, 255)}, {{.x = 41, .y = 12}, .height = 10, .color = rgba( 66, 173, 115, 255)},
{{.x = 50, .y = 10}, .height = 12, .color = rgba( 99, 206, 58, 255)}, {{.x = 50, .y = 10}, .height = 12, .color = rgba( 99, 206, 58, 255)},
{{.x = 59, .y = 8}, .height = 12, .color = rgba(189, 206, 41, 255)}, {{.x = 59, .y = 8}, .height = 12, .color = rgba(189, 206, 41, 255)},
{{.x = 69, .y = 5}, .height = 13, .color = rgba(247, 140, 33, 255)}, {{.x = 69, .y = 5}, .height = 13, .color = rgba(247, 140, 33, 255)},
{{.x = 81, .y = 2}, .height = 15, .color = rgba(255, 197, 49, 255)}, {{.x = 81, .y = 2}, .height = 15, .color = rgba(255, 197, 49, 255)},
{{.x = 95, .y = 1}, .height = 16, .color = rgba(255, 222, 115, 255)}, {{.x = 95, .y = 1}, .height = 16, .color = rgba(255, 222, 115, 255)},
{{.x = 110, .y = 1}, .height = 16, .color = rgba(255, 239, 181, 255)}, {{.x = 110, .y = 1}, .height = 16, .color = rgba(255, 239, 181, 255)},
{{.x = 126, .y = 1}, .height = 16, .color = rgba(255, 255, 255, 255)} {{.x = 126, .y = 1}, .height = 16, .color = rgba(255, 255, 255, 255)}
} }
}; };
static uint16_t speedo_facia_texture; static uint16_t speedo_facia_texture;
void hud_load(void) { void hud_load(void) {
speedo_facia_texture = image_get_texture("wipeout/textures/speedo.tim"); speedo_facia_texture = image_get_texture("wipeout/textures/speedo.tim");
target_reticle = image_get_texture_semi_trans("wipeout/textures/target2.tim"); target_reticle = image_get_texture_semi_trans("wipeout/textures/target2.tim");
weapon_icon_textures = image_get_compressed_textures("wipeout/common/wicons.cmp"); weapon_icon_textures = image_get_compressed_textures("wipeout/common/wicons.cmp");
} }
static void hud_draw_speedo_bar(vec2i_t *pos, const speedo_bar_t *a, const speedo_bar_t *b, float f, rgba_t color_override) { static void hud_draw_speedo_bar(vec2i_t *pos, const speedo_bar_t *a, const speedo_bar_t *b, float f, rgba_t color_override) {
rgba_t left_color, right_color; rgba_t left_color, right_color;
if (color_override.a > 0) { if (color_override.a > 0) {
left_color = color_override; left_color = color_override;
right_color = color_override; right_color = color_override;
} }
else { else {
left_color = a->color; left_color = a->color;
right_color = rgba( right_color = rgba(
lerp(a->color.r, b->color.r, f), lerp(a->color.r, b->color.r, f),
lerp(a->color.g, b->color.g, f), lerp(a->color.g, b->color.g, f),
lerp(a->color.b, b->color.b, f), lerp(a->color.b, b->color.b, f),
lerp(a->color.a, b->color.a, f) lerp(a->color.a, b->color.a, f)
); );
} }
float right_h = lerp(a->height, b->height, f); float right_h = lerp(a->height, b->height, f);
vec2i_t top_left = vec2i(a->offset.x + 1, a->offset.y); vec2i_t top_left = vec2i(a->offset.x + 1, a->offset.y);
vec2i_t bottom_left = vec2i(a->offset.x + 1 - a->height / speedo.skew, a->offset.y + a->height); vec2i_t bottom_left = vec2i(a->offset.x + 1 - a->height / speedo.skew, a->offset.y + a->height);
vec2i_t top_right = vec2i(lerp(a->offset.x + 1, b->offset.x, f), lerp(a->offset.y, b->offset.y, f)); vec2i_t top_right = vec2i(lerp(a->offset.x + 1, b->offset.x, f), lerp(a->offset.y, b->offset.y, f));
vec2i_t bottom_right = vec2i(top_right.x - right_h / speedo.skew, top_right.y + right_h); vec2i_t bottom_right = vec2i(top_right.x - right_h / speedo.skew, top_right.y + right_h);
top_left = ui_scaled(top_left); top_left = ui_scaled(top_left);
bottom_left = ui_scaled(bottom_left); bottom_left = ui_scaled(bottom_left);
top_right = ui_scaled(top_right); top_right = ui_scaled(top_right);
bottom_right = ui_scaled(bottom_right); bottom_right = ui_scaled(bottom_right);
render_push_tris((tris_t) { render_push_tris((tris_t) {
.vertices = { .vertices = {
{ {
.pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0}, .pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0},
.uv = {0, 0}, .uv = {0, 0},
.color = left_color .color = left_color
}, },
{ {
.pos = {pos->x + top_right.x, pos->y + top_right.y, 0}, .pos = {pos->x + top_right.x, pos->y + top_right.y, 0},
.uv = {0, 0}, .uv = {0, 0},
.color = right_color .color = right_color
}, },
{ {
.pos = {pos->x + top_left.x, pos->y + top_left.y, 0}, .pos = {pos->x + top_left.x, pos->y + top_left.y, 0},
.uv = {0, 0}, .uv = {0, 0},
.color = left_color .color = left_color
}, },
} }
}, RENDER_NO_TEXTURE); }, RENDER_NO_TEXTURE);
render_push_tris((tris_t) { render_push_tris((tris_t) {
.vertices = { .vertices = {
{ {
.pos = {pos->x + bottom_right.x, pos->y + bottom_right.y, 0}, .pos = {pos->x + bottom_right.x, pos->y + bottom_right.y, 0},
.uv = {0, 0}, .uv = {0, 0},
.color = right_color .color = right_color
}, },
{ {
.pos = {pos->x + top_right.x, pos->y + top_right.y, 0}, .pos = {pos->x + top_right.x, pos->y + top_right.y, 0},
.uv = {0, 0}, .uv = {0, 0},
.color = right_color .color = right_color
}, },
{ {
.pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0}, .pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0},
.uv = {0, 0}, .uv = {0, 0},
.color = left_color .color = left_color
}, },
} }
}, RENDER_NO_TEXTURE); }, RENDER_NO_TEXTURE);
} }
static void hud_draw_speedo_bars(vec2i_t *pos, float f, rgba_t color_override) { static void hud_draw_speedo_bars(vec2i_t *pos, float f, rgba_t color_override) {
if (f <= 0) { if (f <= 0) {
return; return;
} }
if (f - floor(f) > 0.9) { if (f - floor(f) > 0.9) {
f = ceil(f); f = ceil(f);
} }
if (f > 13) { if (f > 13) {
f = 13; f = 13;
} }
int bars = f; int bars = f;
for (int i = 1; i < bars; i++) { for (int i = 1; i < bars; i++) {
hud_draw_speedo_bar(pos, &speedo.bars[i - 1], &speedo.bars[i], 1, color_override); hud_draw_speedo_bar(pos, &speedo.bars[i - 1], &speedo.bars[i], 1, color_override);
} }
if (bars > 12) { if (bars > 12) {
return; return;
} }
float last_bar_fraction = f - bars + 0.1; float last_bar_fraction = f - bars + 0.1;
if (last_bar_fraction <= 0) { if (last_bar_fraction <= 0) {
return; return;
} }
if (last_bar_fraction > 1) { if (last_bar_fraction > 1) {
last_bar_fraction = 1; last_bar_fraction = 1;
} }
int last_bar = bars == 0 ? 1 : bars; int last_bar = bars == 0 ? 1 : bars;
hud_draw_speedo_bar(pos, &speedo.bars[last_bar - 1], &speedo.bars[last_bar], last_bar_fraction, color_override); hud_draw_speedo_bar(pos, &speedo.bars[last_bar - 1], &speedo.bars[last_bar], last_bar_fraction, color_override);
} }
static void hud_draw_speedo(int speed, int thrust) { static void hud_draw_speedo(int speed, int thrust) {
vec2i_t facia_pos = ui_scaled_pos(UI_POS_BOTTOM | UI_POS_RIGHT, vec2i(-141, -45)); vec2i_t facia_pos = ui_scaled_pos(UI_POS_BOTTOM | UI_POS_RIGHT, vec2i(-141, -45));
vec2i_t bar_pos = ui_scaled_pos(UI_POS_BOTTOM | UI_POS_RIGHT, vec2i(-141, -40)); vec2i_t bar_pos = ui_scaled_pos(UI_POS_BOTTOM | UI_POS_RIGHT, vec2i(-141, -40));
hud_draw_speedo_bars(&bar_pos, thrust / 65.0, rgba(255, 0, 0, 128)); hud_draw_speedo_bars(&bar_pos, thrust / 65.0, rgba(255, 0, 0, 128));
hud_draw_speedo_bars(&bar_pos, speed / 2166.0, rgba(0, 0, 0, 0)); hud_draw_speedo_bars(&bar_pos, speed / 2166.0, rgba(0, 0, 0, 0));
render_push_2d(facia_pos, ui_scaled(render_texture_size(speedo_facia_texture)), rgba(128, 128, 128, 255), speedo_facia_texture); render_push_2d(facia_pos, ui_scaled(render_texture_size(speedo_facia_texture)), rgba(128, 128, 128, 255), speedo_facia_texture);
} }
static void hud_draw_target_icon(vec3_t position) { static void hud_draw_target_icon(vec3_t position) {
vec2i_t screen_size = render_size(); vec2i_t screen_size = render_size();
vec2i_t size = ui_scaled(render_texture_size(target_reticle)); vec2i_t size = ui_scaled(render_texture_size(target_reticle));
vec3_t projected = render_transform(position); vec3_t projected = render_transform(position);
// Not on screen? // Not on screen?
if ( if (
projected.x < -1 || projected.x > 1 || projected.x < -1 || projected.x > 1 ||
projected.y < -1 || projected.y > 1 || projected.y < -1 || projected.y > 1 ||
projected.z >= 1 projected.z >= 1
) { ) {
return; return;
} }
vec2i_t pos = vec2i( vec2i_t pos = vec2i(
(( projected.x + 1.0) / 2.0) * screen_size.x - size.x / 2, (( projected.x + 1.0) / 2.0) * screen_size.x - size.x / 2,
((-projected.y + 1.0) / 2.0) * screen_size.y - size.y / 2 ((-projected.y + 1.0) / 2.0) * screen_size.y - size.y / 2
); );
render_push_2d(pos, size, rgba(128, 128, 128, 128), target_reticle); render_push_2d(pos, size, rgba(128, 128, 128, 128), target_reticle);
} }
void hud_draw(ship_t *ship) { void hud_draw(ship_t *ship) {
// Current lap time // Current lap time
if (ship->lap >= 0) { if (ship->lap >= 0) {
ui_draw_time(ship->lap_time, ui_scaled_pos(UI_POS_BOTTOM | UI_POS_LEFT, vec2i(16, -30)), UI_SIZE_16, UI_COLOR_DEFAULT); ui_draw_time(ship->lap_time, ui_scaled_pos(UI_POS_BOTTOM | UI_POS_LEFT, vec2i(16, -30)), UI_SIZE_16, UI_COLOR_DEFAULT);
for (int i = 0; i < ship->lap && i < NUM_LAPS-1; i++) { for (int i = 0; i < ship->lap && i < NUM_LAPS-1; i++) {
ui_draw_time(g.lap_times[ship->pilot][i], ui_scaled_pos(UI_POS_BOTTOM | UI_POS_LEFT, vec2i(16, -45 - (10 * i))), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_time(g.lap_times[ship->pilot][i], ui_scaled_pos(UI_POS_BOTTOM | UI_POS_LEFT, vec2i(16, -45 - (10 * i))), UI_SIZE_8, UI_COLOR_ACCENT);
} }
} }
// Current Lap // Current Lap
int display_lap = max(0, ship->lap + 1); int display_lap = max(0, ship->lap + 1);
ui_draw_text("LAP", ui_scaled(vec2i(15, 8)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("LAP", ui_scaled(vec2i(15, 8)), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_number(display_lap, ui_scaled(vec2i(10, 19)), UI_SIZE_16, UI_COLOR_DEFAULT); ui_draw_number(display_lap, ui_scaled(vec2i(10, 19)), UI_SIZE_16, UI_COLOR_DEFAULT);
int width = ui_char_width('0' + display_lap, UI_SIZE_16); int width = ui_char_width('0' + display_lap, UI_SIZE_16);
ui_draw_text("OF", ui_scaled(vec2i((10 + width), 27)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("OF", ui_scaled(vec2i((10 + width), 27)), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_number(NUM_LAPS, ui_scaled(vec2i((32 + width), 19)), UI_SIZE_16, UI_COLOR_DEFAULT); ui_draw_number(NUM_LAPS, ui_scaled(vec2i((32 + width), 19)), UI_SIZE_16, UI_COLOR_DEFAULT);
// Race Position // Race Position
if (g.race_type != RACE_TYPE_TIME_TRIAL) { if (g.race_type != RACE_TYPE_TIME_TRIAL) {
ui_draw_text("POSITION", ui_scaled_pos(UI_POS_TOP | UI_POS_RIGHT, vec2i(-90, 8)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("POSITION", ui_scaled_pos(UI_POS_TOP | UI_POS_RIGHT, vec2i(-90, 8)), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_number(ship->position_rank, ui_scaled_pos(UI_POS_TOP | UI_POS_RIGHT, vec2i(-60, 19)), UI_SIZE_16, UI_COLOR_DEFAULT); ui_draw_number(ship->position_rank, ui_scaled_pos(UI_POS_TOP | UI_POS_RIGHT, vec2i(-60, 19)), UI_SIZE_16, UI_COLOR_DEFAULT);
} }
// Framerate // Framerate
if (save.show_fps) { if (save.show_fps) {
ui_draw_text("FPS", ui_scaled(vec2i(16, 78)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("FPS", ui_scaled(vec2i(16, 78)), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_number((int)(g.frame_rate), ui_scaled(vec2i(16, 90)), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_number((int)(g.frame_rate), ui_scaled(vec2i(16, 90)), UI_SIZE_8, UI_COLOR_DEFAULT);
} }
// Lap Record // Lap Record
ui_draw_text("LAP RECORD", ui_scaled(vec2i(15, 43)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("LAP RECORD", ui_scaled(vec2i(15, 43)), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_time(save.highscores[g.race_class][g.circut][g.highscore_tab].lap_record, ui_scaled(vec2i(15, 55)), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_time(save.highscores[g.race_class][g.circut][g.highscore_tab].lap_record, ui_scaled(vec2i(15, 55)), UI_SIZE_8, UI_COLOR_DEFAULT);
// Wrong way // Wrong way
if (flags_not(ship->flags, SHIP_DIRECTION_FORWARD)) { if (flags_not(ship->flags, SHIP_DIRECTION_FORWARD)) {
ui_draw_text_centered("WRONG WAY", ui_scaled_pos(UI_POS_MIDDLE | UI_POS_CENTER, vec2i(-20, 0)), UI_SIZE_16, UI_COLOR_ACCENT); ui_draw_text_centered("WRONG WAY", ui_scaled_pos(UI_POS_MIDDLE | UI_POS_CENTER, vec2i(-20, 0)), UI_SIZE_16, UI_COLOR_ACCENT);
} }
// Speedo // Speedo
int speedo_speed = (g.camera.update_func == camera_update_attract_internal) int speedo_speed = (g.camera.update_func == camera_update_attract_internal)
? ship->speed * 7 ? ship->speed * 7
: ship->speed; : ship->speed;
hud_draw_speedo(speedo_speed, ship->thrust_mag); hud_draw_speedo(speedo_speed, ship->thrust_mag);
// Weapon icon // Weapon icon
if (ship->weapon_type != WEAPON_TYPE_NONE) { if (ship->weapon_type != WEAPON_TYPE_NONE) {
vec2i_t pos = ui_scaled_pos(UI_POS_TOP | UI_POS_CENTER, vec2i(-16, 20)); vec2i_t pos = ui_scaled_pos(UI_POS_TOP | UI_POS_CENTER, vec2i(-16, 20));
vec2i_t size = ui_scaled(vec2i(32, 32)); vec2i_t size = ui_scaled(vec2i(32, 32));
uint16_t icon = texture_from_list(weapon_icon_textures, ship->weapon_type-1); uint16_t icon = texture_from_list(weapon_icon_textures, ship->weapon_type-1);
render_push_2d(pos, size, rgba(128,128,128,255), icon); render_push_2d(pos, size, rgba(128,128,128,255), icon);
} }
// Lives // Lives
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
for (int i = 0; i < g.lives; i++) { for (int i = 0; i < g.lives; i++) {
ui_draw_icon(UI_ICON_STAR, ui_scaled_pos(UI_POS_BOTTOM | UI_POS_RIGHT, vec2i(-26 - 13 * i, -50)), UI_COLOR_DEFAULT); ui_draw_icon(UI_ICON_STAR, ui_scaled_pos(UI_POS_BOTTOM | UI_POS_RIGHT, vec2i(-26 - 13 * i, -50)), UI_COLOR_DEFAULT);
} }
} }
// Weapon target reticle // Weapon target reticle
if (ship->weapon_target) { if (ship->weapon_target) {
hud_draw_target_icon(ship->weapon_target->position); hud_draw_target_icon(ship->weapon_target->position);
} }
} }

View file

@ -1,9 +1,9 @@
#ifndef HUD_H #ifndef HUD_H
#define HUD_H #define HUD_H
#include "ship.h" #include "ship.h"
void hud_load(void); void hud_load(void);
void hud_draw(ship_t *ship); void hud_draw(ship_t *ship);
#endif #endif

View file

@ -1,322 +1,322 @@
#include "../types.h" #include "../types.h"
#include "../mem.h" #include "../mem.h"
#include "../utils.h" #include "../utils.h"
#include "../platform.h" #include "../platform.h"
#include "object.h" #include "object.h"
#include "track.h" #include "track.h"
#include "ship.h" #include "ship.h"
#include "weapon.h" #include "weapon.h"
#include "droid.h" #include "droid.h"
#include "camera.h" #include "camera.h"
#include "object.h" #include "object.h"
#include "scene.h" #include "scene.h"
#include "game.h" #include "game.h"
#include "hud.h" #include "hud.h"
#include "image.h" #include "image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h> #include <stb_image_write.h>
#define TIM_TYPE_PALETTED_4_BPP 0x08 #define TIM_TYPE_PALETTED_4_BPP 0x08
#define TIM_TYPE_PALETTED_8_BPP 0x09 #define TIM_TYPE_PALETTED_8_BPP 0x09
#define TIM_TYPE_TRUE_COLOR_16_BPP 0x02 #define TIM_TYPE_TRUE_COLOR_16_BPP 0x02
static inline rgba_t tim_16bit_to_rgba(uint16_t c, bool transparent_bit) { static inline rgba_t tim_16bit_to_rgba(uint16_t c, bool transparent_bit) {
return rgba( return rgba(
((c >> 0) & 0x1f) << 3, ((c >> 0) & 0x1f) << 3,
((c >> 5) & 0x1f) << 3, ((c >> 5) & 0x1f) << 3,
((c >> 10) & 0x1f) << 3, ((c >> 10) & 0x1f) << 3,
(c == 0 (c == 0
? 0x00 ? 0x00
: transparent_bit && (c & 0x7fff) == 0 ? 0x00 : 0xff : transparent_bit && (c & 0x7fff) == 0 ? 0x00 : 0xff
) )
); );
} }
image_t *image_alloc(uint32_t width, uint32_t height) { image_t *image_alloc(uint32_t width, uint32_t height) {
image_t *image = mem_temp_alloc(sizeof(image_t) + width * height * sizeof(rgba_t)); image_t *image = mem_temp_alloc(sizeof(image_t) + width * height * sizeof(rgba_t));
image->width = width; image->width = width;
image->height = height; image->height = height;
image->pixels = (rgba_t *)(((uint8_t *)image) + sizeof(image_t)); image->pixels = (rgba_t *)(((uint8_t *)image) + sizeof(image_t));
return image; return image;
} }
image_t *image_load_from_bytes(uint8_t *bytes, bool transparent) { image_t *image_load_from_bytes(uint8_t *bytes, bool transparent) {
uint32_t p = 0; uint32_t p = 0;
uint32_t magic = get_i32_le(bytes, &p); uint32_t magic = get_i32_le(bytes, &p);
uint32_t type = get_i32_le(bytes, &p); uint32_t type = get_i32_le(bytes, &p);
rgba_t palette[256]; rgba_t palette[256];
if ( if (
type == TIM_TYPE_PALETTED_4_BPP || type == TIM_TYPE_PALETTED_4_BPP ||
type == TIM_TYPE_PALETTED_8_BPP type == TIM_TYPE_PALETTED_8_BPP
) { ) {
uint32_t header_length = get_i32_le(bytes, &p); uint32_t header_length = get_i32_le(bytes, &p);
uint16_t palette_x = get_i16_le(bytes, &p); uint16_t palette_x = get_i16_le(bytes, &p);
uint16_t palette_y = get_i16_le(bytes, &p); uint16_t palette_y = get_i16_le(bytes, &p);
uint16_t palette_colors = get_i16_le(bytes, &p); uint16_t palette_colors = get_i16_le(bytes, &p);
uint16_t palettes = get_i16_le(bytes, &p); uint16_t palettes = get_i16_le(bytes, &p);
for (int i = 0; i < palette_colors; i++) { for (int i = 0; i < palette_colors; i++) {
palette[i] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent); palette[i] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent);
} }
} }
uint32_t data_size = get_i32_le(bytes, &p); uint32_t data_size = get_i32_le(bytes, &p);
int32_t pixels_per_16bit = 1; int32_t pixels_per_16bit = 1;
if (type == TIM_TYPE_PALETTED_8_BPP) { if (type == TIM_TYPE_PALETTED_8_BPP) {
pixels_per_16bit = 2; pixels_per_16bit = 2;
} }
else if (type == TIM_TYPE_PALETTED_4_BPP) { else if (type == TIM_TYPE_PALETTED_4_BPP) {
pixels_per_16bit = 4; pixels_per_16bit = 4;
} }
uint16_t skip_x = get_i16_le(bytes, &p); uint16_t skip_x = get_i16_le(bytes, &p);
uint16_t skip_y = get_i16_le(bytes, &p); uint16_t skip_y = get_i16_le(bytes, &p);
uint16_t entries_per_row = get_i16_le(bytes, &p); uint16_t entries_per_row = get_i16_le(bytes, &p);
uint16_t rows = get_i16_le(bytes, &p); uint16_t rows = get_i16_le(bytes, &p);
int32_t width = entries_per_row * pixels_per_16bit; int32_t width = entries_per_row * pixels_per_16bit;
int32_t height = rows; int32_t height = rows;
int32_t entries = entries_per_row * rows; int32_t entries = entries_per_row * rows;
image_t *image = image_alloc(width, height); image_t *image = image_alloc(width, height);
int32_t pixel_pos = 0; int32_t pixel_pos = 0;
if (type == TIM_TYPE_TRUE_COLOR_16_BPP) { if (type == TIM_TYPE_TRUE_COLOR_16_BPP) {
for (int i = 0; i < entries; i++) { for (int i = 0; i < entries; i++) {
image->pixels[pixel_pos++] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent); image->pixels[pixel_pos++] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent);
} }
} }
else if (type == TIM_TYPE_PALETTED_8_BPP) { else if (type == TIM_TYPE_PALETTED_8_BPP) {
for (int i = 0; i < entries; i++) { for (int i = 0; i < entries; i++) {
int32_t palette_pos = get_i16_le(bytes, &p); int32_t palette_pos = get_i16_le(bytes, &p);
image->pixels[pixel_pos++] = palette[(palette_pos >> 0) & 0xff]; image->pixels[pixel_pos++] = palette[(palette_pos >> 0) & 0xff];
image->pixels[pixel_pos++] = palette[(palette_pos >> 8) & 0xff]; image->pixels[pixel_pos++] = palette[(palette_pos >> 8) & 0xff];
} }
} }
else if (type == TIM_TYPE_PALETTED_4_BPP) { else if (type == TIM_TYPE_PALETTED_4_BPP) {
for (int i = 0; i < entries; i++) { for (int i = 0; i < entries; i++) {
int32_t palette_pos = get_i16_le(bytes, &p); int32_t palette_pos = get_i16_le(bytes, &p);
image->pixels[pixel_pos++] = palette[(palette_pos >> 0) & 0xf]; image->pixels[pixel_pos++] = palette[(palette_pos >> 0) & 0xf];
image->pixels[pixel_pos++] = palette[(palette_pos >> 4) & 0xf]; image->pixels[pixel_pos++] = palette[(palette_pos >> 4) & 0xf];
image->pixels[pixel_pos++] = palette[(palette_pos >> 8) & 0xf]; image->pixels[pixel_pos++] = palette[(palette_pos >> 8) & 0xf];
image->pixels[pixel_pos++] = palette[(palette_pos >> 12) & 0xf]; image->pixels[pixel_pos++] = palette[(palette_pos >> 12) & 0xf];
} }
} }
return image; return image;
} }
#define LZSS_INDEX_BIT_COUNT 13 #define LZSS_INDEX_BIT_COUNT 13
#define LZSS_LENGTH_BIT_COUNT 4 #define LZSS_LENGTH_BIT_COUNT 4
#define LZSS_WINDOW_SIZE (1 << LZSS_INDEX_BIT_COUNT) #define LZSS_WINDOW_SIZE (1 << LZSS_INDEX_BIT_COUNT)
#define LZSS_BREAK_EVEN ((1 + LZSS_INDEX_BIT_COUNT + LZSS_LENGTH_BIT_COUNT) / 9) #define LZSS_BREAK_EVEN ((1 + LZSS_INDEX_BIT_COUNT + LZSS_LENGTH_BIT_COUNT) / 9)
#define LZSS_END_OF_STREAM 0 #define LZSS_END_OF_STREAM 0
#define LZSS_MOD_WINDOW(a) ((a) & (LZSS_WINDOW_SIZE - 1)) #define LZSS_MOD_WINDOW(a) ((a) & (LZSS_WINDOW_SIZE - 1))
void lzss_decompress(uint8_t *in_data, uint8_t *out_data) { void lzss_decompress(uint8_t *in_data, uint8_t *out_data) {
int16_t i; int16_t i;
int16_t current_position; int16_t current_position;
uint8_t cc; uint8_t cc;
int16_t match_length; int16_t match_length;
int16_t match_position; int16_t match_position;
uint32_t mask; uint32_t mask;
uint32_t return_value; uint32_t return_value;
uint8_t in_bfile_mask; uint8_t in_bfile_mask;
int16_t in_bfile_rack; int16_t in_bfile_rack;
int16_t value; int16_t value;
uint8_t window[LZSS_WINDOW_SIZE]; uint8_t window[LZSS_WINDOW_SIZE];
in_bfile_rack = 0; in_bfile_rack = 0;
in_bfile_mask = 0x80; in_bfile_mask = 0x80;
current_position = 1; current_position = 1;
while (true) { while (true) {
if (in_bfile_mask == 0x80) { if (in_bfile_mask == 0x80) {
in_bfile_rack = (int16_t) * in_data++; in_bfile_rack = (int16_t) * in_data++;
} }
value = in_bfile_rack & in_bfile_mask; value = in_bfile_rack & in_bfile_mask;
in_bfile_mask >>= 1; in_bfile_mask >>= 1;
if (in_bfile_mask == 0) { if (in_bfile_mask == 0) {
in_bfile_mask = 0x80; in_bfile_mask = 0x80;
} }
if (value) { if (value) {
mask = 1L << (8 - 1); mask = 1L << (8 - 1);
return_value = 0; return_value = 0;
while (mask != 0) { while (mask != 0) {
if (in_bfile_mask == 0x80) { if (in_bfile_mask == 0x80) {
in_bfile_rack = (int16_t) * in_data++; in_bfile_rack = (int16_t) * in_data++;
} }
if (in_bfile_rack & in_bfile_mask) { if (in_bfile_rack & in_bfile_mask) {
return_value |= mask; return_value |= mask;
} }
mask >>= 1; mask >>= 1;
in_bfile_mask >>= 1; in_bfile_mask >>= 1;
if (in_bfile_mask == 0) { if (in_bfile_mask == 0) {
in_bfile_mask = 0x80; in_bfile_mask = 0x80;
} }
} }
cc = (uint8_t) return_value; cc = (uint8_t) return_value;
*out_data++ = cc; *out_data++ = cc;
window[ current_position ] = cc; window[ current_position ] = cc;
current_position = LZSS_MOD_WINDOW(current_position + 1); current_position = LZSS_MOD_WINDOW(current_position + 1);
} }
else { else {
mask = 1L << (LZSS_INDEX_BIT_COUNT - 1); mask = 1L << (LZSS_INDEX_BIT_COUNT - 1);
return_value = 0; return_value = 0;
while (mask != 0) { while (mask != 0) {
if (in_bfile_mask == 0x80) { if (in_bfile_mask == 0x80) {
in_bfile_rack = (int16_t) * in_data++; in_bfile_rack = (int16_t) * in_data++;
} }
if (in_bfile_rack & in_bfile_mask) { if (in_bfile_rack & in_bfile_mask) {
return_value |= mask; return_value |= mask;
} }
mask >>= 1; mask >>= 1;
in_bfile_mask >>= 1; in_bfile_mask >>= 1;
if (in_bfile_mask == 0) { if (in_bfile_mask == 0) {
in_bfile_mask = 0x80; in_bfile_mask = 0x80;
} }
} }
match_position = (int16_t) return_value; match_position = (int16_t) return_value;
if (match_position == LZSS_END_OF_STREAM) { if (match_position == LZSS_END_OF_STREAM) {
break; break;
} }
mask = 1L << (LZSS_LENGTH_BIT_COUNT - 1); mask = 1L << (LZSS_LENGTH_BIT_COUNT - 1);
return_value = 0; return_value = 0;
while (mask != 0) { while (mask != 0) {
if (in_bfile_mask == 0x80) { if (in_bfile_mask == 0x80) {
in_bfile_rack = (int16_t) * in_data++; in_bfile_rack = (int16_t) * in_data++;
} }
if (in_bfile_rack & in_bfile_mask) { if (in_bfile_rack & in_bfile_mask) {
return_value |= mask; return_value |= mask;
} }
mask >>= 1; mask >>= 1;
in_bfile_mask >>= 1; in_bfile_mask >>= 1;
if (in_bfile_mask == 0) { if (in_bfile_mask == 0) {
in_bfile_mask = 0x80; in_bfile_mask = 0x80;
} }
} }
match_length = (int16_t) return_value; match_length = (int16_t) return_value;
match_length += LZSS_BREAK_EVEN; match_length += LZSS_BREAK_EVEN;
for (i = 0 ; i <= match_length ; i++) { for (i = 0 ; i <= match_length ; i++) {
cc = window[LZSS_MOD_WINDOW(match_position + i)]; cc = window[LZSS_MOD_WINDOW(match_position + i)];
*out_data++ = cc; *out_data++ = cc;
window[current_position] = cc; window[current_position] = cc;
current_position = LZSS_MOD_WINDOW(current_position + 1); current_position = LZSS_MOD_WINDOW(current_position + 1);
} }
} }
} }
} }
cmp_t *image_load_compressed(char *name) { cmp_t *image_load_compressed(char *name) {
printf("load cmp %s\n", name); printf("load cmp %s\n", name);
uint32_t compressed_size; uint32_t compressed_size;
uint8_t *compressed_bytes = platform_load_asset(name, &compressed_size); uint8_t *compressed_bytes = platform_load_asset(name, &compressed_size);
uint32_t p = 0; uint32_t p = 0;
int32_t decompressed_size = 0; int32_t decompressed_size = 0;
int32_t image_count = get_i32_le(compressed_bytes, &p); int32_t image_count = get_i32_le(compressed_bytes, &p);
// Calculate the total uncompressed size // Calculate the total uncompressed size
for (int i = 0; i < image_count; i++) { for (int i = 0; i < image_count; i++) {
decompressed_size += get_i32_le(compressed_bytes, &p); decompressed_size += get_i32_le(compressed_bytes, &p);
} }
uint32_t struct_size = sizeof(cmp_t) + sizeof(uint8_t *) * image_count; uint32_t struct_size = sizeof(cmp_t) + sizeof(uint8_t *) * image_count;
cmp_t *cmp = mem_temp_alloc(struct_size + decompressed_size); cmp_t *cmp = mem_temp_alloc(struct_size + decompressed_size);
cmp->len = image_count; cmp->len = image_count;
uint8_t *decompressed_bytes = ((uint8_t *)cmp) + struct_size; uint8_t *decompressed_bytes = ((uint8_t *)cmp) + struct_size;
// Rewind and load all offsets // Rewind and load all offsets
p = 4; p = 4;
uint32_t offset = 0; uint32_t offset = 0;
for (int i = 0; i < image_count; i++) { for (int i = 0; i < image_count; i++) {
cmp->entries[i] = decompressed_bytes + offset; cmp->entries[i] = decompressed_bytes + offset;
offset += get_i32_le(compressed_bytes, &p); offset += get_i32_le(compressed_bytes, &p);
} }
lzss_decompress(compressed_bytes + p, decompressed_bytes); lzss_decompress(compressed_bytes + p, decompressed_bytes);
mem_temp_free(compressed_bytes); mem_temp_free(compressed_bytes);
return cmp; return cmp;
} }
uint16_t image_get_texture(char *name) { uint16_t image_get_texture(char *name) {
printf("load: %s\n", name); printf("load: %s\n", name);
uint32_t size; uint32_t size;
uint8_t *bytes = platform_load_asset(name, &size); uint8_t *bytes = platform_load_asset(name, &size);
image_t *image = image_load_from_bytes(bytes, false); image_t *image = image_load_from_bytes(bytes, false);
uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels); uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels);
mem_temp_free(image); mem_temp_free(image);
mem_temp_free(bytes); mem_temp_free(bytes);
return texture_index; return texture_index;
} }
uint16_t image_get_texture_semi_trans(char *name) { uint16_t image_get_texture_semi_trans(char *name) {
printf("load: %s\n", name); printf("load: %s\n", name);
uint32_t size; uint32_t size;
uint8_t *bytes = platform_load_asset(name, &size); uint8_t *bytes = platform_load_asset(name, &size);
image_t *image = image_load_from_bytes(bytes, true); image_t *image = image_load_from_bytes(bytes, true);
uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels); uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels);
mem_temp_free(image); mem_temp_free(image);
mem_temp_free(bytes); mem_temp_free(bytes);
return texture_index; return texture_index;
} }
texture_list_t image_get_compressed_textures(char *name) { texture_list_t image_get_compressed_textures(char *name) {
cmp_t *cmp = image_load_compressed(name); cmp_t *cmp = image_load_compressed(name);
texture_list_t list = {.start = render_textures_len(), .len = cmp->len}; texture_list_t list = {.start = render_textures_len(), .len = cmp->len};
for (int i = 0; i < cmp->len; i++) { for (int i = 0; i < cmp->len; i++) {
int32_t width, height; int32_t width, height;
image_t *image = image_load_from_bytes(cmp->entries[i], false); image_t *image = image_load_from_bytes(cmp->entries[i], false);
// char png_name[1024] = {0}; // char png_name[1024] = {0};
// sprintf(png_name, "%s.%d.png", name, i); // sprintf(png_name, "%s.%d.png", name, i);
// stbi_write_png(png_name, image->width, image->height, 4, image->pixels, 0); // stbi_write_png(png_name, image->width, image->height, 4, image->pixels, 0);
render_texture_create(image->width, image->height, image->pixels); render_texture_create(image->width, image->height, image->pixels);
mem_temp_free(image); mem_temp_free(image);
} }
mem_temp_free(cmp); mem_temp_free(cmp);
return list; return list;
} }
uint16_t texture_from_list(texture_list_t tl, uint16_t index) { uint16_t texture_from_list(texture_list_t tl, uint16_t index) {
error_if(index >= tl.len, "Texture %d not in list of len %d", index, tl.len); error_if(index >= tl.len, "Texture %d not in list of len %d", index, tl.len);
return tl.start + index; return tl.start + index;
} }
void image_copy(image_t *src, image_t *dst, uint32_t sx, uint32_t sy, uint32_t sw, uint32_t sh, uint32_t dx, uint32_t dy) { void image_copy(image_t *src, image_t *dst, uint32_t sx, uint32_t sy, uint32_t sw, uint32_t sh, uint32_t dx, uint32_t dy) {
rgba_t *src_pixels = src->pixels + sy * src->width + sx; rgba_t *src_pixels = src->pixels + sy * src->width + sx;
rgba_t *dst_pixels = dst->pixels + dy * dst->width + dx; rgba_t *dst_pixels = dst->pixels + dy * dst->width + dx;
for (uint32_t y = 0; y < sh; y++) { for (uint32_t y = 0; y < sh; y++) {
for (uint32_t x = 0; x < sw; x++) { for (uint32_t x = 0; x < sw; x++) {
*(dst_pixels++) = *(src_pixels++); *(dst_pixels++) = *(src_pixels++);
} }
src_pixels += src->width - sw; src_pixels += src->width - sw;
dst_pixels += dst->width - sw; dst_pixels += dst->width - sw;
} }
} }

View file

@ -1,34 +1,34 @@
#ifndef INIT_H #ifndef INIT_H
#define INIT_H #define INIT_H
#include "../types.h" #include "../types.h"
typedef struct { typedef struct {
uint16_t start; uint16_t start;
uint16_t len; uint16_t len;
} texture_list_t; } texture_list_t;
#define texture_list_empty() ((texture_list_t){0, 0}) #define texture_list_empty() ((texture_list_t){0, 0})
typedef struct { typedef struct {
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
rgba_t *pixels; rgba_t *pixels;
} image_t; } image_t;
typedef struct { typedef struct {
uint32_t len; uint32_t len;
uint8_t *entries[]; uint8_t *entries[];
} cmp_t; } cmp_t;
image_t *image_alloc(uint32_t width, uint32_t height); image_t *image_alloc(uint32_t width, uint32_t height);
void image_copy(image_t *src, image_t *dst, uint32_t sx, uint32_t sy, uint32_t sw, uint32_t sh, uint32_t dx, uint32_t dy); void image_copy(image_t *src, image_t *dst, uint32_t sx, uint32_t sy, uint32_t sw, uint32_t sh, uint32_t dx, uint32_t dy);
image_t *image_load_from_bytes(uint8_t *bytes, bool transparent); image_t *image_load_from_bytes(uint8_t *bytes, bool transparent);
cmp_t *image_load_compressed(char *name); cmp_t *image_load_compressed(char *name);
uint16_t image_get_texture(char *name); uint16_t image_get_texture(char *name);
uint16_t image_get_texture_semi_trans(char *name); uint16_t image_get_texture_semi_trans(char *name);
texture_list_t image_get_compressed_textures(char *name); texture_list_t image_get_compressed_textures(char *name);
uint16_t texture_from_list(texture_list_t tl, uint16_t index); uint16_t texture_from_list(texture_list_t tl, uint16_t index);
#endif #endif

View file

@ -1,486 +1,486 @@
#include <string.h> #include <string.h>
#include "../input.h" #include "../input.h"
#include "../system.h" #include "../system.h"
#include "../utils.h" #include "../utils.h"
#include "../mem.h" #include "../mem.h"
#include "menu.h" #include "menu.h"
#include "ingame_menus.h" #include "ingame_menus.h"
#include "game.h" #include "game.h"
#include "image.h" #include "image.h"
#include "ui.h" #include "ui.h"
#include "race.h" #include "race.h"
static void page_race_points_init(menu_t * menu); static void page_race_points_init(menu_t * menu);
static void page_championship_points_init(menu_t * menu); static void page_championship_points_init(menu_t * menu);
static void page_hall_of_fame_init(menu_t * menu); static void page_hall_of_fame_init(menu_t * menu);
static texture_list_t pilot_portraits; static texture_list_t pilot_portraits;
static menu_t *ingame_menu; static menu_t *ingame_menu;
void ingame_menus_load(void) { void ingame_menus_load(void) {
pilot_portraits = image_get_compressed_textures(def.pilots[g.pilot].portrait); pilot_portraits = image_get_compressed_textures(def.pilots[g.pilot].portrait);
ingame_menu = mem_bump(sizeof(menu_t)); ingame_menu = mem_bump(sizeof(menu_t));
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Pause Menu // Pause Menu
static void button_continue(menu_t *menu, int data) { static void button_continue(menu_t *menu, int data) {
race_unpause(); race_unpause();
} }
static void button_restart_confirm(menu_t *menu, int data) { static void button_restart_confirm(menu_t *menu, int data) {
if (data) { if (data) {
race_restart(); race_restart();
} }
else { else {
menu_pop(menu); menu_pop(menu);
} }
} }
static void button_restart_or_quit(menu_t *menu, int data) { static void button_restart_or_quit(menu_t *menu, int data) {
if (data) { if (data) {
race_restart(); race_restart();
} }
else { else {
game_set_scene(GAME_SCENE_MAIN_MENU); game_set_scene(GAME_SCENE_MAIN_MENU);
} }
} }
static void button_restart(menu_t *menu, int data) { static void button_restart(menu_t *menu, int data) {
menu_confirm(menu, "ARE YOU SURE YOU", "WANT TO RESTART", "YES", "NO", button_restart_confirm); menu_confirm(menu, "ARE YOU SURE YOU", "WANT TO RESTART", "YES", "NO", button_restart_confirm);
} }
static void button_quit_confirm(menu_t *menu, int data) { static void button_quit_confirm(menu_t *menu, int data) {
if (data) { if (data) {
game_set_scene(GAME_SCENE_MAIN_MENU); game_set_scene(GAME_SCENE_MAIN_MENU);
} }
else { else {
menu_pop(menu); menu_pop(menu);
} }
} }
static void button_quit(menu_t *menu, int data) { static void button_quit(menu_t *menu, int data) {
menu_confirm(menu, "ARE YOU SURE YOU", "WANT TO QUIT", "YES", "NO", button_quit_confirm); menu_confirm(menu, "ARE YOU SURE YOU", "WANT TO QUIT", "YES", "NO", button_quit_confirm);
} }
static void button_music_track(menu_t *menu, int data) { static void button_music_track(menu_t *menu, int data) {
sfx_music_play(data); sfx_music_play(data);
sfx_music_mode(SFX_MUSIC_LOOP); sfx_music_mode(SFX_MUSIC_LOOP);
} }
static void button_music_random(menu_t *menu, int data) { static void button_music_random(menu_t *menu, int data) {
sfx_music_play(rand_int(0, len(def.music))); sfx_music_play(rand_int(0, len(def.music)));
sfx_music_mode(SFX_MUSIC_RANDOM); sfx_music_mode(SFX_MUSIC_RANDOM);
} }
static void button_music(menu_t *menu, int data) { static void button_music(menu_t *menu, int data) {
menu_page_t *page = menu_push(menu, "MUSIC", NULL); menu_page_t *page = menu_push(menu, "MUSIC", NULL);
for (int i = 0; i < len(def.music); i++) { for (int i = 0; i < len(def.music); i++) {
menu_page_add_button(page, i, def.music[i].name, button_music_track); menu_page_add_button(page, i, def.music[i].name, button_music_track);
} }
menu_page_add_button(page, 0, "RANDOM", button_music_random); menu_page_add_button(page, 0, "RANDOM", button_music_random);
} }
menu_t *pause_menu_init(void) { menu_t *pause_menu_init(void) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
menu_reset(ingame_menu); menu_reset(ingame_menu);
menu_page_t *page = menu_push(ingame_menu, "PAUSED", NULL); menu_page_t *page = menu_push(ingame_menu, "PAUSED", NULL);
menu_page_add_button(page, 0, "CONTINUE", button_continue); menu_page_add_button(page, 0, "CONTINUE", button_continue);
menu_page_add_button(page, 0, "RESTART", button_restart); menu_page_add_button(page, 0, "RESTART", button_restart);
menu_page_add_button(page, 0, "QUIT", button_quit); menu_page_add_button(page, 0, "QUIT", button_quit);
menu_page_add_button(page, 0, "MUSIC", button_music); menu_page_add_button(page, 0, "MUSIC", button_music);
return ingame_menu; return ingame_menu;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Game Over // Game Over
menu_t *game_over_menu_init(void) { menu_t *game_over_menu_init(void) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
menu_reset(ingame_menu); menu_reset(ingame_menu);
menu_page_t *page = menu_push(ingame_menu, "GAME OVER", NULL); menu_page_t *page = menu_push(ingame_menu, "GAME OVER", NULL);
menu_page_add_button(page, 1, "", button_quit_confirm); menu_page_add_button(page, 1, "", button_quit_confirm);
return ingame_menu; return ingame_menu;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Race Stats // Race Stats
static void button_qualify_confirm(menu_t *menu, int data) { static void button_qualify_confirm(menu_t *menu, int data) {
if (data) { if (data) {
race_restart(); race_restart();
} }
else { else {
game_set_scene(GAME_SCENE_MAIN_MENU); game_set_scene(GAME_SCENE_MAIN_MENU);
} }
} }
static void button_race_stats_continue(menu_t *menu, int data) { static void button_race_stats_continue(menu_t *menu, int data) {
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
if (g.race_position <= QUALIFYING_RANK) { if (g.race_position <= QUALIFYING_RANK) {
page_race_points_init(menu); page_race_points_init(menu);
} }
else { else {
menu_page_t *page = menu_confirm(menu, "CONTINUE QUALIFYING OR QUIT", "", "QUALIFY", "QUIT", button_qualify_confirm); menu_page_t *page = menu_confirm(menu, "CONTINUE QUALIFYING OR QUIT", "", "QUALIFY", "QUIT", button_qualify_confirm);
page->index = 0; page->index = 0;
} }
} }
else { else {
if (g.is_new_race_record) { if (g.is_new_race_record) {
page_hall_of_fame_init(menu); page_hall_of_fame_init(menu);
} }
else { else {
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit); menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
} }
} }
} }
static void page_race_stats_draw(menu_t *menu, int data) { static void page_race_stats_draw(menu_t *menu, int data) {
menu_page_t *page = &menu->pages[menu->index]; menu_page_t *page = &menu->pages[menu->index];
vec2i_t pos = page->title_pos; vec2i_t pos = page->title_pos;
pos.x -= 140; pos.x -= 140;
pos.y += 32; pos.y += 32;
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER; ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
// Pilot portrait and race position - only for championship or single race // Pilot portrait and race position - only for championship or single race
if (g.race_type != RACE_TYPE_TIME_TRIAL) { if (g.race_type != RACE_TYPE_TIME_TRIAL) {
vec2i_t image_pos = ui_scaled_pos(anchor, vec2i(pos.x + 180, pos.y)); vec2i_t image_pos = ui_scaled_pos(anchor, vec2i(pos.x + 180, pos.y));
uint16_t image = texture_from_list(pilot_portraits, g.race_position <= QUALIFYING_RANK ? 1 : 0); uint16_t image = texture_from_list(pilot_portraits, g.race_position <= QUALIFYING_RANK ? 1 : 0);
render_push_2d(image_pos, ui_scaled(render_texture_size(image)), rgba(0, 0, 0, 128), RENDER_NO_TEXTURE); render_push_2d(image_pos, ui_scaled(render_texture_size(image)), rgba(0, 0, 0, 128), RENDER_NO_TEXTURE);
ui_draw_image(image_pos, image); ui_draw_image(image_pos, image);
ui_draw_text("RACE POSITION", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("RACE POSITION", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_number(g.race_position, ui_scaled_pos(anchor, vec2i(pos.x + ui_text_width("RACE POSITION", UI_SIZE_8)+8, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_number(g.race_position, ui_scaled_pos(anchor, vec2i(pos.x + ui_text_width("RACE POSITION", UI_SIZE_8)+8, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT);
} }
pos.y += 32; pos.y += 32;
ui_draw_text("RACE STATISTICS", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("RACE STATISTICS", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
pos.y += 16; pos.y += 16;
for (int i = 0; i < NUM_LAPS; i++) { for (int i = 0; i < NUM_LAPS; i++) {
ui_draw_text("LAP", ui_scaled_pos(anchor, vec2i(pos.x + 8, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("LAP", ui_scaled_pos(anchor, vec2i(pos.x + 8, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_number(i+1, ui_scaled_pos(anchor, vec2i(pos.x + 50, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_number(i+1, ui_scaled_pos(anchor, vec2i(pos.x + 50, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_time(g.lap_times[g.pilot][i], ui_scaled_pos(anchor, vec2i(pos.x + 72, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_time(g.lap_times[g.pilot][i], ui_scaled_pos(anchor, vec2i(pos.x + 72, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT);
pos.y+= 12; pos.y+= 12;
} }
pos.y += 32; pos.y += 32;
ui_draw_text("RACE TIME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("RACE TIME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
pos.y += 12; pos.y += 12;
ui_draw_time(g.race_time, ui_scaled_pos(anchor, vec2i(pos.x + 8, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_time(g.race_time, ui_scaled_pos(anchor, vec2i(pos.x + 8, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT);
pos.y += 12; pos.y += 12;
ui_draw_text("BEST LAP", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("BEST LAP", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
pos.y += 12; pos.y += 12;
ui_draw_time(g.best_lap, ui_scaled_pos(anchor, vec2i(pos.x + 8, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_time(g.best_lap, ui_scaled_pos(anchor, vec2i(pos.x + 8, pos.y)), UI_SIZE_8, UI_COLOR_DEFAULT);
pos.y += 12; pos.y += 12;
} }
menu_t *race_stats_menu_init(void) { menu_t *race_stats_menu_init(void) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
menu_reset(ingame_menu); menu_reset(ingame_menu);
char *title; char *title;
if (g.race_type == RACE_TYPE_TIME_TRIAL) { if (g.race_type == RACE_TYPE_TIME_TRIAL) {
title = ""; title = "";
} }
else if (g.race_position <= QUALIFYING_RANK) { else if (g.race_position <= QUALIFYING_RANK) {
title = "CONGRATULATIONS"; title = "CONGRATULATIONS";
} }
else { else {
title = "FAILED TO QUALIFY"; title = "FAILED TO QUALIFY";
} }
menu_page_t *page = menu_push(ingame_menu, title, page_race_stats_draw); menu_page_t *page = menu_push(ingame_menu, title, page_race_stats_draw);
flags_add(page->layout_flags, MENU_FIXED); flags_add(page->layout_flags, MENU_FIXED);
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
page->title_pos = vec2i(0, -100); page->title_pos = vec2i(0, -100);
menu_page_add_button(page, 1, "", button_race_stats_continue); menu_page_add_button(page, 1, "", button_race_stats_continue);
return ingame_menu; return ingame_menu;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Race Table // Race Table
static void button_race_points_continue(menu_t *menu, int data) { static void button_race_points_continue(menu_t *menu, int data) {
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
page_championship_points_init(menu); page_championship_points_init(menu);
} }
else if (g.is_new_race_record) { else if (g.is_new_race_record) {
page_hall_of_fame_init(menu); page_hall_of_fame_init(menu);
} }
else { else {
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit); menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
} }
} }
static void page_race_points_draw(menu_t *menu, int data) { static void page_race_points_draw(menu_t *menu, int data) {
menu_page_t *page = &menu->pages[menu->index]; menu_page_t *page = &menu->pages[menu->index];
vec2i_t pos = page->title_pos; vec2i_t pos = page->title_pos;
pos.x -= 140; pos.x -= 140;
pos.y += 32; pos.y += 32;
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER; ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
ui_draw_text("PILOT NAME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("PILOT NAME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_text("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
pos.y += 24; pos.y += 24;
for (int i = 0; i < len(g.race_ranks); i++) { for (int i = 0; i < len(g.race_ranks); i++) {
rgba_t color = g.race_ranks[i].pilot == g.pilot ? UI_COLOR_ACCENT : UI_COLOR_DEFAULT; rgba_t color = g.race_ranks[i].pilot == g.pilot ? UI_COLOR_ACCENT : UI_COLOR_DEFAULT;
ui_draw_text(def.pilots[g.race_ranks[i].pilot].name, ui_scaled_pos(anchor, pos), UI_SIZE_8, color); ui_draw_text(def.pilots[g.race_ranks[i].pilot].name, ui_scaled_pos(anchor, pos), UI_SIZE_8, color);
int w = ui_number_width(g.race_ranks[i].points, UI_SIZE_8); int w = ui_number_width(g.race_ranks[i].points, UI_SIZE_8);
ui_draw_number(g.race_ranks[i].points, ui_scaled_pos(anchor, vec2i(pos.x + 280 - w, pos.y)), UI_SIZE_8, color); ui_draw_number(g.race_ranks[i].points, ui_scaled_pos(anchor, vec2i(pos.x + 280 - w, pos.y)), UI_SIZE_8, color);
pos.y += 12; pos.y += 12;
} }
} }
static void page_race_points_init(menu_t *menu) { static void page_race_points_init(menu_t *menu) {
menu_page_t *page = menu_push(menu, "RACE POINTS", page_race_points_draw); menu_page_t *page = menu_push(menu, "RACE POINTS", page_race_points_draw);
flags_add(page->layout_flags, MENU_FIXED); flags_add(page->layout_flags, MENU_FIXED);
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
page->title_pos = vec2i(0, -100); page->title_pos = vec2i(0, -100);
menu_page_add_button(page, 1, "", button_race_points_continue); menu_page_add_button(page, 1, "", button_race_points_continue);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Championship Table // Championship Table
static void button_championship_points_continue(menu_t *menu, int data) { static void button_championship_points_continue(menu_t *menu, int data) {
if (g.is_new_race_record) { if (g.is_new_race_record) {
page_hall_of_fame_init(menu); page_hall_of_fame_init(menu);
} }
else if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { else if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
race_next(); race_next();
} }
else { else {
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_quit_confirm); menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_quit_confirm);
} }
} }
static void page_championship_points_draw(menu_t *menu, int data) { static void page_championship_points_draw(menu_t *menu, int data) {
menu_page_t *page = &menu->pages[menu->index]; menu_page_t *page = &menu->pages[menu->index];
vec2i_t pos = page->title_pos; vec2i_t pos = page->title_pos;
pos.x -= 140; pos.x -= 140;
pos.y += 32; pos.y += 32;
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER; ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
ui_draw_text("PILOT NAME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("PILOT NAME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
ui_draw_text("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT); ui_draw_text("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
pos.y += 24; pos.y += 24;
for (int i = 0; i < len(g.championship_ranks); i++) { for (int i = 0; i < len(g.championship_ranks); i++) {
rgba_t color = g.championship_ranks[i].pilot == g.pilot ? UI_COLOR_ACCENT : UI_COLOR_DEFAULT; rgba_t color = g.championship_ranks[i].pilot == g.pilot ? UI_COLOR_ACCENT : UI_COLOR_DEFAULT;
ui_draw_text(def.pilots[g.championship_ranks[i].pilot].name, ui_scaled_pos(anchor, pos), UI_SIZE_8, color); ui_draw_text(def.pilots[g.championship_ranks[i].pilot].name, ui_scaled_pos(anchor, pos), UI_SIZE_8, color);
int w = ui_number_width(g.championship_ranks[i].points, UI_SIZE_8); int w = ui_number_width(g.championship_ranks[i].points, UI_SIZE_8);
ui_draw_number(g.championship_ranks[i].points, ui_scaled_pos(anchor, vec2i(pos.x + 280 - w, pos.y)), UI_SIZE_8, color); ui_draw_number(g.championship_ranks[i].points, ui_scaled_pos(anchor, vec2i(pos.x + 280 - w, pos.y)), UI_SIZE_8, color);
pos.y += 12; pos.y += 12;
} }
} }
static void page_championship_points_init(menu_t *menu) { static void page_championship_points_init(menu_t *menu) {
menu_page_t *page = menu_push(menu, "CHAMPIONSHIP TABLE", page_championship_points_draw); menu_page_t *page = menu_push(menu, "CHAMPIONSHIP TABLE", page_championship_points_draw);
flags_add(page->layout_flags, MENU_FIXED); flags_add(page->layout_flags, MENU_FIXED);
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
page->title_pos = vec2i(0, -100); page->title_pos = vec2i(0, -100);
menu_page_add_button(page, 1, "", button_championship_points_continue); menu_page_add_button(page, 1, "", button_championship_points_continue);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Hall of Fame // Hall of Fame
static highscores_entry_t hs_new_entry = { static highscores_entry_t hs_new_entry = {
.time = 0, .time = 0,
.name = "" .name = ""
}; };
static const char *hs_charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static const char *hs_charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static int hs_char_index = 0; static int hs_char_index = 0;
static bool hs_entry_complete = false; static bool hs_entry_complete = false;
static void hall_of_fame_draw_name_entry(menu_t *menu, ui_pos_t anchor, vec2i_t pos) { static void hall_of_fame_draw_name_entry(menu_t *menu, ui_pos_t anchor, vec2i_t pos) {
int entry_len = strlen(hs_new_entry.name); int entry_len = strlen(hs_new_entry.name);
int entry_width = ui_text_width(hs_new_entry.name, UI_SIZE_16); int entry_width = ui_text_width(hs_new_entry.name, UI_SIZE_16);
vec2i_t c_pos = ui_scaled_pos(anchor, vec2i(pos.x + entry_width, pos.y)); vec2i_t c_pos = ui_scaled_pos(anchor, vec2i(pos.x + entry_width, pos.y));
int c_first = 0; int c_first = 0;
int c_last = 38; int c_last = 38;
if (entry_len == 0) { if (entry_len == 0) {
c_last = 37; c_last = 37;
} }
else if (entry_len == 3) { else if (entry_len == 3) {
c_first = 36; c_first = 36;
} }
if (input_pressed(A_MENU_UP)) { if (input_pressed(A_MENU_UP)) {
hs_char_index++; hs_char_index++;
} }
else if (input_pressed(A_MENU_DOWN)) { else if (input_pressed(A_MENU_DOWN)) {
hs_char_index--; hs_char_index--;
} }
if (hs_char_index < c_first) { if (hs_char_index < c_first) {
hs_char_index = c_last-1; hs_char_index = c_last-1;
} }
if (hs_char_index >= c_last) { if (hs_char_index >= c_last) {
hs_char_index = c_first; hs_char_index = c_first;
} }
// DEL // DEL
if (hs_char_index == 36) { if (hs_char_index == 36) {
ui_draw_icon(UI_ICON_DEL, c_pos, UI_COLOR_ACCENT); ui_draw_icon(UI_ICON_DEL, c_pos, UI_COLOR_ACCENT);
if (input_pressed(A_MENU_SELECT)) { if (input_pressed(A_MENU_SELECT)) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
if (entry_len > 0) { if (entry_len > 0) {
hs_new_entry.name[entry_len-1] = '\0'; hs_new_entry.name[entry_len-1] = '\0';
} }
} }
} }
// END // END
else if (hs_char_index == 37) { else if (hs_char_index == 37) {
ui_draw_icon(UI_ICON_END, c_pos, UI_COLOR_ACCENT); ui_draw_icon(UI_ICON_END, c_pos, UI_COLOR_ACCENT);
if (input_pressed(A_MENU_SELECT)) { if (input_pressed(A_MENU_SELECT)) {
hs_entry_complete = true; hs_entry_complete = true;
} }
} }
// A-Z, 0-9 // A-Z, 0-9
else { else {
char selector[2] = {hs_charset[hs_char_index], '\0'}; char selector[2] = {hs_charset[hs_char_index], '\0'};
ui_draw_text(selector, c_pos, UI_SIZE_16, UI_COLOR_ACCENT); ui_draw_text(selector, c_pos, UI_SIZE_16, UI_COLOR_ACCENT);
if (input_pressed(A_MENU_SELECT)) { if (input_pressed(A_MENU_SELECT)) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
hs_new_entry.name[entry_len] = hs_charset[hs_char_index]; hs_new_entry.name[entry_len] = hs_charset[hs_char_index];
hs_new_entry.name[entry_len+1] = '\0'; hs_new_entry.name[entry_len+1] = '\0';
} }
} }
ui_draw_text(hs_new_entry.name, ui_scaled_pos(anchor, pos), UI_SIZE_16, UI_COLOR_ACCENT); ui_draw_text(hs_new_entry.name, ui_scaled_pos(anchor, pos), UI_SIZE_16, UI_COLOR_ACCENT);
} }
static void page_hall_of_fame_draw(menu_t *menu, int data) { static void page_hall_of_fame_draw(menu_t *menu, int data) {
// FIXME: doing this all in the draw() function leads to all kinds of // FIXME: doing this all in the draw() function leads to all kinds of
// complications // complications
highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab]; highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab];
if (hs_entry_complete) { if (hs_entry_complete) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
strncpy(save.highscores_name, hs_new_entry.name, 4); strncpy(save.highscores_name, hs_new_entry.name, 4);
save.is_dirty = true; save.is_dirty = true;
// Insert new highscore entry into the save struct // Insert new highscore entry into the save struct
highscores_entry_t temp_entry = hs->entries[0]; highscores_entry_t temp_entry = hs->entries[0];
for (int i = 0; i < NUM_HIGHSCORES; i++) { for (int i = 0; i < NUM_HIGHSCORES; i++) {
if (hs_new_entry.time < hs->entries[i].time) { if (hs_new_entry.time < hs->entries[i].time) {
for (int j = NUM_HIGHSCORES - 2; j >= i; j--) { for (int j = NUM_HIGHSCORES - 2; j >= i; j--) {
hs->entries[j+1] = hs->entries[j]; hs->entries[j+1] = hs->entries[j];
} }
hs->entries[i] = hs_new_entry; hs->entries[i] = hs_new_entry;
break; break;
} }
} }
save.is_dirty = true; save.is_dirty = true;
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
race_next(); race_next();
} }
else { else {
menu_reset(menu); // Can't go back! menu_reset(menu); // Can't go back!
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit); menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
} }
return; return;
} }
menu_page_t *page = &menu->pages[menu->index]; menu_page_t *page = &menu->pages[menu->index];
vec2i_t pos = page->title_pos; vec2i_t pos = page->title_pos;
pos.x -= 120; pos.x -= 120;
pos.y += 48; pos.y += 48;
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER; ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
bool has_shown_new_entry = false; bool has_shown_new_entry = false;
for (int i = 0, j = 0; i < NUM_HIGHSCORES; i++, j++) { for (int i = 0, j = 0; i < NUM_HIGHSCORES; i++, j++) {
if (!has_shown_new_entry && hs_new_entry.time < hs->entries[i].time) { if (!has_shown_new_entry && hs_new_entry.time < hs->entries[i].time) {
hall_of_fame_draw_name_entry(menu, anchor, pos); hall_of_fame_draw_name_entry(menu, anchor, pos);
ui_draw_time(hs_new_entry.time, ui_scaled_pos(anchor, vec2i(pos.x + 120, pos.y)), UI_SIZE_16, UI_COLOR_DEFAULT); ui_draw_time(hs_new_entry.time, ui_scaled_pos(anchor, vec2i(pos.x + 120, pos.y)), UI_SIZE_16, UI_COLOR_DEFAULT);
has_shown_new_entry = true; has_shown_new_entry = true;
j--; j--;
} }
else { else {
ui_draw_text(hs->entries[j].name, ui_scaled_pos(anchor, pos), UI_SIZE_16, UI_COLOR_DEFAULT); ui_draw_text(hs->entries[j].name, ui_scaled_pos(anchor, pos), UI_SIZE_16, UI_COLOR_DEFAULT);
ui_draw_time(hs->entries[j].time, ui_scaled_pos(anchor, vec2i(pos.x + 120, pos.y)), UI_SIZE_16, UI_COLOR_DEFAULT); ui_draw_time(hs->entries[j].time, ui_scaled_pos(anchor, vec2i(pos.x + 120, pos.y)), UI_SIZE_16, UI_COLOR_DEFAULT);
} }
pos.y += 24; pos.y += 24;
} }
} }
static void page_hall_of_fame_init(menu_t *menu) { static void page_hall_of_fame_init(menu_t *menu) {
menu_reset(menu); // Can't go back! menu_reset(menu); // Can't go back!
menu_page_t *page = menu_push(menu, "HALL OF FAME", page_hall_of_fame_draw); menu_page_t *page = menu_push(menu, "HALL OF FAME", page_hall_of_fame_draw);
flags_add(page->layout_flags, MENU_FIXED); flags_add(page->layout_flags, MENU_FIXED);
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
page->title_pos = vec2i(0, -100); page->title_pos = vec2i(0, -100);
hs_new_entry.time = g.race_time; hs_new_entry.time = g.race_time;
strncpy(hs_new_entry.name, save.highscores_name, 4); strncpy(hs_new_entry.name, save.highscores_name, 4);
hs_char_index = 0; hs_char_index = 0;
hs_entry_complete = false; hs_entry_complete = false;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Text scroller // Text scroller
static char * const *text_scroll_lines; static char * const *text_scroll_lines;
static int text_scroll_lines_len; static int text_scroll_lines_len;
static double text_scroll_start_time; static double text_scroll_start_time;
static void text_scroll_menu_draw(menu_t *menu, int data) { static void text_scroll_menu_draw(menu_t *menu, int data) {
double time = system_time() - text_scroll_start_time; double time = system_time() - text_scroll_start_time;
int scale = ui_get_scale(); int scale = ui_get_scale();
int speed = 32; int speed = 32;
vec2i_t screen = render_size(); vec2i_t screen = render_size();
vec2i_t pos = vec2i(screen.x / 2, screen.y - time * scale * speed); vec2i_t pos = vec2i(screen.x / 2, screen.y - time * scale * speed);
for (int i = 0; i < text_scroll_lines_len; i++) { for (int i = 0; i < text_scroll_lines_len; i++) {
const char *line = text_scroll_lines[i]; const char *line = text_scroll_lines[i];
if (line[0] == '#') { if (line[0] == '#') {
pos.y += 48 * scale; pos.y += 48 * scale;
ui_draw_text_centered(line + 1, pos, UI_SIZE_16, UI_COLOR_ACCENT); ui_draw_text_centered(line + 1, pos, UI_SIZE_16, UI_COLOR_ACCENT);
pos.y += 32 * scale; pos.y += 32 * scale;
} }
else { else {
ui_draw_text_centered(line, pos, UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_text_centered(line, pos, UI_SIZE_8, UI_COLOR_DEFAULT);
pos.y += 12 * scale; pos.y += 12 * scale;
} }
} }
} }
menu_t *text_scroll_menu_init(char * const *lines, int len) { menu_t *text_scroll_menu_init(char * const *lines, int len) {
text_scroll_lines = lines; text_scroll_lines = lines;
text_scroll_lines_len = len; text_scroll_lines_len = len;
text_scroll_start_time = system_time(); text_scroll_start_time = system_time();
menu_reset(ingame_menu); menu_reset(ingame_menu);
menu_page_t *page = menu_push(ingame_menu, "", text_scroll_menu_draw); menu_page_t *page = menu_push(ingame_menu, "", text_scroll_menu_draw);
menu_page_add_button(page, 1, "", button_quit_confirm); menu_page_add_button(page, 1, "", button_quit_confirm);
return ingame_menu; return ingame_menu;
} }

View file

@ -1,13 +1,13 @@
#ifndef PAUSE_MENU_H #ifndef PAUSE_MENU_H
#define PAUSE_MENU_H #define PAUSE_MENU_H
#include "menu.h" #include "menu.h"
void ingame_menus_load(void); void ingame_menus_load(void);
menu_t *pause_menu_init(void); menu_t *pause_menu_init(void);
menu_t *game_over_menu_init(void); menu_t *game_over_menu_init(void);
menu_t *race_stats_menu_init(void); menu_t *race_stats_menu_init(void);
menu_t *text_scroll_menu_init(char * const *lines, int len); menu_t *text_scroll_menu_init(char * const *lines, int len);
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
#ifndef MAIN_MENU_H #ifndef MAIN_MENU_H
#define MAIN_MENU_H #define MAIN_MENU_H
void main_menu_init(void); void main_menu_init(void);
void main_menu_update(void); void main_menu_update(void);
#endif #endif

View file

@ -1,245 +1,245 @@
#include "../system.h" #include "../system.h"
#include "../input.h" #include "../input.h"
#include "../utils.h" #include "../utils.h"
#include "game.h" #include "game.h"
#include "menu.h" #include "menu.h"
#include "ui.h" #include "ui.h"
#include "sfx.h" #include "sfx.h"
bool blink(void) { bool blink(void) {
// blink 30 times per second // blink 30 times per second
return fmod(system_cycle_time(), 1.0/15.0) < 1.0/30.0; return fmod(system_cycle_time(), 1.0/15.0) < 1.0/30.0;
} }
void menu_reset(menu_t *menu) { void menu_reset(menu_t *menu) {
menu->index = -1; menu->index = -1;
} }
menu_page_t *menu_push(menu_t *menu, char *title, void(*draw_func)(menu_t *, int)) { menu_page_t *menu_push(menu_t *menu, char *title, void(*draw_func)(menu_t *, int)) {
error_if(menu->index >= MENU_PAGES_MAX-1, "MENU_PAGES_MAX exceeded"); error_if(menu->index >= MENU_PAGES_MAX-1, "MENU_PAGES_MAX exceeded");
menu_page_t *page = &menu->pages[++menu->index]; menu_page_t *page = &menu->pages[++menu->index];
page->layout_flags = MENU_VERTICAL | MENU_ALIGN_CENTER; page->layout_flags = MENU_VERTICAL | MENU_ALIGN_CENTER;
page->block_width = 320; page->block_width = 320;
page->title = title; page->title = title;
page->subtitle = NULL; page->subtitle = NULL;
page->draw_func = draw_func; page->draw_func = draw_func;
page->entries_len = 0; page->entries_len = 0;
page->index = 0; page->index = 0;
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
return page; return page;
} }
menu_page_t *menu_confirm(menu_t *menu, char *title, char *subtitle, char *yes, char *no, void(*confirm_func)(menu_t *, int)) { menu_page_t *menu_confirm(menu_t *menu, char *title, char *subtitle, char *yes, char *no, void(*confirm_func)(menu_t *, int)) {
error_if(menu->index >= MENU_PAGES_MAX-1, "MENU_PAGES_MAX exceeded"); error_if(menu->index >= MENU_PAGES_MAX-1, "MENU_PAGES_MAX exceeded");
menu_page_t *page = &menu->pages[++menu->index]; menu_page_t *page = &menu->pages[++menu->index];
page->layout_flags = MENU_HORIZONTAL; page->layout_flags = MENU_HORIZONTAL;
page->title = title; page->title = title;
page->subtitle = subtitle; page->subtitle = subtitle;
page->draw_func = NULL; page->draw_func = NULL;
page->entries_len = 0; page->entries_len = 0;
menu_page_add_button(page, 1, yes, confirm_func); menu_page_add_button(page, 1, yes, confirm_func);
menu_page_add_button(page, 0, no, confirm_func); menu_page_add_button(page, 0, no, confirm_func);
page->index = 1; page->index = 1;
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
return page; return page;
} }
void menu_pop(menu_t *menu) { void menu_pop(menu_t *menu) {
if (menu->index == 0) { if (menu->index == 0) {
return; return;
} }
menu->index--; menu->index--;
} }
void menu_page_add_button(menu_page_t *page, int data, char *text, void(*select_func)(menu_t *, int)) { void menu_page_add_button(menu_page_t *page, int data, char *text, void(*select_func)(menu_t *, int)) {
error_if(page->entries_len >= MENU_ENTRIES_MAX-1, "MENU_ENTRIES_MAX exceeded"); error_if(page->entries_len >= MENU_ENTRIES_MAX-1, "MENU_ENTRIES_MAX exceeded");
menu_entry_t *entry = &page->entries[page->entries_len++]; menu_entry_t *entry = &page->entries[page->entries_len++];
entry->data = data; entry->data = data;
entry->text = text; entry->text = text;
entry->select_func = select_func; entry->select_func = select_func;
entry->type = MENU_ENTRY_BUTTON; entry->type = MENU_ENTRY_BUTTON;
} }
void menu_page_add_toggle(menu_page_t *page, int data, char *text, const char **options, int len, void(*select_func)(menu_t *, int)) { void menu_page_add_toggle(menu_page_t *page, int data, char *text, const char **options, int len, void(*select_func)(menu_t *, int)) {
error_if(page->entries_len >= MENU_ENTRIES_MAX-1, "MENU_ENTRIES_MAX exceeded"); error_if(page->entries_len >= MENU_ENTRIES_MAX-1, "MENU_ENTRIES_MAX exceeded");
menu_entry_t *entry = &page->entries[page->entries_len++]; menu_entry_t *entry = &page->entries[page->entries_len++];
entry->data = data; entry->data = data;
entry->text = text; entry->text = text;
entry->select_func = select_func; entry->select_func = select_func;
entry->type = MENU_ENTRY_TOGGLE; entry->type = MENU_ENTRY_TOGGLE;
entry->options = options; entry->options = options;
entry->options_len = len; entry->options_len = len;
} }
void menu_update(menu_t *menu) { void menu_update(menu_t *menu) {
render_set_view_2d(); render_set_view_2d();
error_if(menu->index < 0, "Attempt to update menu without a page"); error_if(menu->index < 0, "Attempt to update menu without a page");
menu_page_t *page = &menu->pages[menu->index]; menu_page_t *page = &menu->pages[menu->index];
// Handle menu entry selecting // Handle menu entry selecting
int last_index = page->index; int last_index = page->index;
int selected_data = 0; int selected_data = 0;
if (page->entries_len > 0) { if (page->entries_len > 0) {
if (flags_is(page->layout_flags, MENU_HORIZONTAL)) { if (flags_is(page->layout_flags, MENU_HORIZONTAL)) {
if (input_pressed(A_MENU_LEFT)) { if (input_pressed(A_MENU_LEFT)) {
page->index--; page->index--;
} }
else if (input_pressed(A_MENU_RIGHT)) { else if (input_pressed(A_MENU_RIGHT)) {
page->index++; page->index++;
} }
} }
else { else {
if (input_pressed(A_MENU_UP)) { if (input_pressed(A_MENU_UP)) {
page->index--; page->index--;
} }
if (input_pressed(A_MENU_DOWN)) { if (input_pressed(A_MENU_DOWN)) {
page->index++; page->index++;
} }
} }
if (page->index >= page->entries_len) { if (page->index >= page->entries_len) {
page->index = 0; page->index = 0;
} }
if (page->index < 0) { if (page->index < 0) {
page->index = page->entries_len - 1; page->index = page->entries_len - 1;
} }
if (last_index != page->index) { if (last_index != page->index) {
sfx_play(SFX_MENU_MOVE); sfx_play(SFX_MENU_MOVE);
} }
selected_data = page->entries[page->index].data; selected_data = page->entries[page->index].data;
} }
if (page->draw_func) { if (page->draw_func) {
page->draw_func(menu, selected_data); page->draw_func(menu, selected_data);
} }
render_set_view_2d(); render_set_view_2d();
// Draw Horizontal (confirm) // Draw Horizontal (confirm)
if (flags_is(page->layout_flags, MENU_HORIZONTAL)) { if (flags_is(page->layout_flags, MENU_HORIZONTAL)) {
vec2i_t pos = vec2i(0, -20); vec2i_t pos = vec2i(0, -20);
ui_draw_text_centered(page->title, ui_scaled_pos(page->title_anchor, pos), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_text_centered(page->title, ui_scaled_pos(page->title_anchor, pos), UI_SIZE_8, UI_COLOR_DEFAULT);
if (page->subtitle) { if (page->subtitle) {
pos.y += 12; pos.y += 12;
ui_draw_text_centered(page->subtitle, ui_scaled_pos(page->title_anchor, pos), UI_SIZE_8, UI_COLOR_DEFAULT); ui_draw_text_centered(page->subtitle, ui_scaled_pos(page->title_anchor, pos), UI_SIZE_8, UI_COLOR_DEFAULT);
} }
pos.y += 16; pos.y += 16;
page = &menu->pages[menu->index]; page = &menu->pages[menu->index];
pos.x = -50; pos.x = -50;
for (int i = 0; i < page->entries_len; i++) { for (int i = 0; i < page->entries_len; i++) {
menu_entry_t *entry = &page->entries[i]; menu_entry_t *entry = &page->entries[i];
rgba_t text_color; rgba_t text_color;
if (i == page->index && blink()) { if (i == page->index && blink()) {
text_color = UI_COLOR_ACCENT; text_color = UI_COLOR_ACCENT;
} }
else { else {
text_color = UI_COLOR_DEFAULT; text_color = UI_COLOR_DEFAULT;
} }
ui_draw_text_centered(entry->text, ui_scaled_pos(page->items_anchor, pos), UI_SIZE_16, text_color); ui_draw_text_centered(entry->text, ui_scaled_pos(page->items_anchor, pos), UI_SIZE_16, text_color);
pos.x = 60; pos.x = 60;
} }
} }
// Draw Vertical // Draw Vertical
else { else {
vec2i_t title_pos, items_pos; vec2i_t title_pos, items_pos;
if (flags_not(page->layout_flags, MENU_FIXED)) { if (flags_not(page->layout_flags, MENU_FIXED)) {
int height = 20 + page->entries_len * 12; int height = 20 + page->entries_len * 12;
title_pos = vec2i(0, -height/2); title_pos = vec2i(0, -height/2);
items_pos = vec2i(0, -height/2 + 20); items_pos = vec2i(0, -height/2 + 20);
} }
else { else {
title_pos = page->title_pos; title_pos = page->title_pos;
items_pos = page->items_pos; items_pos = page->items_pos;
} }
if (flags_is(page->layout_flags, MENU_ALIGN_CENTER)) { if (flags_is(page->layout_flags, MENU_ALIGN_CENTER)) {
ui_draw_text_centered(page->title, ui_scaled_pos(page->title_anchor, title_pos), UI_SIZE_12, UI_COLOR_ACCENT); ui_draw_text_centered(page->title, ui_scaled_pos(page->title_anchor, title_pos), UI_SIZE_12, UI_COLOR_ACCENT);
} }
else { else {
ui_draw_text(page->title, ui_scaled_pos(page->title_anchor, title_pos), UI_SIZE_12, UI_COLOR_ACCENT); ui_draw_text(page->title, ui_scaled_pos(page->title_anchor, title_pos), UI_SIZE_12, UI_COLOR_ACCENT);
} }
page = &menu->pages[menu->index]; page = &menu->pages[menu->index];
for (int i = 0; i < page->entries_len; i++) { for (int i = 0; i < page->entries_len; i++) {
menu_entry_t *entry = &page->entries[i]; menu_entry_t *entry = &page->entries[i];
rgba_t text_color; rgba_t text_color;
if (i == page->index && blink()) { if (i == page->index && blink()) {
text_color = UI_COLOR_ACCENT; text_color = UI_COLOR_ACCENT;
} }
else { else {
text_color = UI_COLOR_DEFAULT; text_color = UI_COLOR_DEFAULT;
} }
if (flags_is(page->layout_flags, MENU_ALIGN_CENTER)) { if (flags_is(page->layout_flags, MENU_ALIGN_CENTER)) {
ui_draw_text_centered(entry->text, ui_scaled_pos(page->items_anchor, items_pos), UI_SIZE_8, text_color); ui_draw_text_centered(entry->text, ui_scaled_pos(page->items_anchor, items_pos), UI_SIZE_8, text_color);
} }
else { else {
ui_draw_text(entry->text, ui_scaled_pos(page->items_anchor, items_pos), UI_SIZE_8, text_color); ui_draw_text(entry->text, ui_scaled_pos(page->items_anchor, items_pos), UI_SIZE_8, text_color);
} }
if (entry->type == MENU_ENTRY_TOGGLE) { if (entry->type == MENU_ENTRY_TOGGLE) {
vec2i_t toggle_pos = items_pos; vec2i_t toggle_pos = items_pos;
toggle_pos.x += page->block_width - ui_text_width(entry->options[entry->data], UI_SIZE_8); toggle_pos.x += page->block_width - ui_text_width(entry->options[entry->data], UI_SIZE_8);
ui_draw_text(entry->options[entry->data], ui_scaled_pos(page->items_anchor, toggle_pos), UI_SIZE_8, text_color); ui_draw_text(entry->options[entry->data], ui_scaled_pos(page->items_anchor, toggle_pos), UI_SIZE_8, text_color);
} }
items_pos.y += 12; items_pos.y += 12;
} }
} }
// Handle back buttons // Handle back buttons
if (input_pressed(A_MENU_BACK) || input_pressed(A_MENU_QUIT)) { if (input_pressed(A_MENU_BACK) || input_pressed(A_MENU_QUIT)) {
if (menu->index != 0) { if (menu->index != 0) {
menu_pop(menu); menu_pop(menu);
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
} }
return; return;
} }
if (page->entries_len == 0) { if (page->entries_len == 0) {
return; return;
} }
// Handle toggle entries // Handle toggle entries
menu_entry_t *entry = &page->entries[page->index]; menu_entry_t *entry = &page->entries[page->index];
if (entry->type == MENU_ENTRY_TOGGLE) { if (entry->type == MENU_ENTRY_TOGGLE) {
if (input_pressed(A_MENU_LEFT)) { if (input_pressed(A_MENU_LEFT)) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
entry->data--; entry->data--;
if (entry->data < 0) { if (entry->data < 0) {
entry->data = entry->options_len-1; entry->data = entry->options_len-1;
} }
if (entry->select_func) { if (entry->select_func) {
entry->select_func(menu, entry->data); entry->select_func(menu, entry->data);
} }
} }
else if (input_pressed(A_MENU_RIGHT) || input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) { else if (input_pressed(A_MENU_RIGHT) || input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
entry->data = (entry->data + 1) % entry->options_len; entry->data = (entry->data + 1) % entry->options_len;
if (entry->select_func) { if (entry->select_func) {
entry->select_func(menu, entry->data); entry->select_func(menu, entry->data);
} }
} }
} }
// Handle buttons // Handle buttons
else { else {
if (input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) { if (input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) {
if (entry->select_func) { if (entry->select_func) {
sfx_play(SFX_MENU_SELECT); sfx_play(SFX_MENU_SELECT);
if (entry->type == MENU_ENTRY_TOGGLE) { if (entry->type == MENU_ENTRY_TOGGLE) {
entry->data = (entry->data + 1) % entry->options_len; entry->data = (entry->data + 1) % entry->options_len;
} }
entry->select_func(menu, entry->data); entry->select_func(menu, entry->data);
} }
} }
} }
} }

View file

@ -1,65 +1,65 @@
#ifndef MENU_H #ifndef MENU_H
#define MENU_H #define MENU_H
#include "../types.h" #include "../types.h"
#include "ui.h" #include "ui.h"
#define MENU_PAGES_MAX 8 #define MENU_PAGES_MAX 8
#define MENU_ENTRIES_MAX 16 #define MENU_ENTRIES_MAX 16
typedef enum { typedef enum {
MENU_ENTRY_BUTTON, MENU_ENTRY_BUTTON,
MENU_ENTRY_TOGGLE MENU_ENTRY_TOGGLE
} menu_entry_type_t; } menu_entry_type_t;
typedef enum { typedef enum {
MENU_VERTICAL = (1<<0), MENU_VERTICAL = (1<<0),
MENU_HORIZONTAL = (1<<1), MENU_HORIZONTAL = (1<<1),
MENU_FIXED = (1<<2), MENU_FIXED = (1<<2),
MENU_ALIGN_CENTER = (1<<3), MENU_ALIGN_CENTER = (1<<3),
MENU_ALIGN_BLOCK = (1<<4) MENU_ALIGN_BLOCK = (1<<4)
} menu_page_layout_t; } menu_page_layout_t;
typedef struct menu_t menu_t; typedef struct menu_t menu_t;
typedef struct menu_page_t menu_page_t; typedef struct menu_page_t menu_page_t;
typedef struct menu_entry_t menu_entry_t; typedef struct menu_entry_t menu_entry_t;
typedef struct menu_entry_options_t menu_entry_options_t; typedef struct menu_entry_options_t menu_entry_options_t;
struct menu_entry_t { struct menu_entry_t {
menu_entry_type_t type; menu_entry_type_t type;
int data; int data;
char *text; char *text;
void (*select_func)(menu_t *, int); void (*select_func)(menu_t *, int);
const char **options; const char **options;
int options_len; int options_len;
}; };
struct menu_page_t { struct menu_page_t {
char *title, *subtitle; char *title, *subtitle;
menu_page_layout_t layout_flags; menu_page_layout_t layout_flags;
void (*draw_func)(menu_t *, int); void (*draw_func)(menu_t *, int);
menu_entry_t entries[MENU_ENTRIES_MAX]; menu_entry_t entries[MENU_ENTRIES_MAX];
int entries_len; int entries_len;
int index; int index;
int block_width; int block_width;
vec2i_t title_pos; vec2i_t title_pos;
ui_pos_t title_anchor; ui_pos_t title_anchor;
vec2i_t items_pos; vec2i_t items_pos;
ui_pos_t items_anchor; ui_pos_t items_anchor;
}; };
struct menu_t { struct menu_t {
menu_page_t pages[MENU_PAGES_MAX]; menu_page_t pages[MENU_PAGES_MAX];
int index; int index;
}; };
void menu_reset(menu_t *menu); void menu_reset(menu_t *menu);
menu_page_t *menu_push(menu_t *menu, char *title, void(*draw_func)(menu_t *, int)); menu_page_t *menu_push(menu_t *menu, char *title, void(*draw_func)(menu_t *, int));
menu_page_t *menu_confirm(menu_t *menu, char *title, char *subtitle, char *yes, char *no, void(*confirm_func)(menu_t *, int)); menu_page_t *menu_confirm(menu_t *menu, char *title, char *subtitle, char *yes, char *no, void(*confirm_func)(menu_t *, int));
void menu_pop(menu_t *menu); void menu_pop(menu_t *menu);
void menu_page_add_button(menu_page_t *page, int data, char *text, void(*select_func)(menu_t *, int)); void menu_page_add_button(menu_page_t *page, int data, char *text, void(*select_func)(menu_t *, int));
void menu_page_add_toggle(menu_page_t *page, int data, char *text, const char **options, int len, void(*select_func)(menu_t *, int)); void menu_page_add_toggle(menu_page_t *page, int data, char *text, const char **options, int len, void(*select_func)(menu_t *, int));
void menu_update(menu_t *menu); void menu_update(menu_t *menu);
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -1,384 +1,384 @@
#ifndef OBJECT_H #ifndef OBJECT_H
#define OBJECT_H #define OBJECT_H
#include "../types.h" #include "../types.h"
#include "../render.h" #include "../render.h"
#include "../utils.h" #include "../utils.h"
#include "image.h" #include "image.h"
// Primitive Structure Stub ( Structure varies with primitive type ) // Primitive Structure Stub ( Structure varies with primitive type )
typedef struct Primitive { typedef struct Primitive {
int16_t type; // Type of Primitive int16_t type; // Type of Primitive
} Primitive; } Primitive;
typedef struct F3 { typedef struct F3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t pad1; int16_t pad1;
rgba_t color; rgba_t color;
} F3; } F3;
typedef struct FT3 { typedef struct FT3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
int16_t pad1; int16_t pad1;
rgba_t color; rgba_t color;
} FT3; } FT3;
typedef struct F4 { typedef struct F4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
rgba_t color; rgba_t color;
} F4; } F4;
typedef struct FT4 { typedef struct FT4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
uint8_t u3; uint8_t u3;
uint8_t v3; uint8_t v3;
int16_t pad1; int16_t pad1;
rgba_t color; rgba_t color;
} FT4; } FT4;
typedef struct G3 { typedef struct G3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t pad1; int16_t pad1;
rgba_t color[3]; rgba_t color[3];
} G3; } G3;
typedef struct GT3 { typedef struct GT3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
int16_t pad1; int16_t pad1;
rgba_t color[3]; rgba_t color[3];
} GT3; } GT3;
typedef struct G4 { typedef struct G4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
rgba_t color[4]; rgba_t color[4];
} G4; } G4;
typedef struct GT4 { typedef struct GT4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
uint8_t u3; uint8_t u3;
uint8_t v3; uint8_t v3;
int16_t pad1; int16_t pad1;
rgba_t color[4]; rgba_t color[4];
} GT4; } GT4;
/* LIGHT SOURCED POLYGONS /* LIGHT SOURCED POLYGONS
*/ */
typedef struct LSF3 { typedef struct LSF3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t normal; // Indices of the normals int16_t normal; // Indices of the normals
rgba_t color; rgba_t color;
} LSF3; } LSF3;
typedef struct LSFT3 { typedef struct LSFT3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t normal; // Indices of the normals int16_t normal; // Indices of the normals
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
rgba_t color; rgba_t color;
} LSFT3; } LSFT3;
typedef struct LSF4 { typedef struct LSF4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
int16_t normal; // Indices of the normals int16_t normal; // Indices of the normals
int16_t pad1; int16_t pad1;
rgba_t color; rgba_t color;
} LSF4; } LSF4;
typedef struct LSFT4 { typedef struct LSFT4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
int16_t normal; // Indices of the normals int16_t normal; // Indices of the normals
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
uint8_t u3; uint8_t u3;
uint8_t v3; uint8_t v3;
rgba_t color; rgba_t color;
} LSFT4; } LSFT4;
typedef struct LSG3 { typedef struct LSG3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t normals[3]; // Indices of the normals int16_t normals[3]; // Indices of the normals
rgba_t color[3]; rgba_t color[3];
} LSG3; } LSG3;
typedef struct LSGT3 { typedef struct LSGT3 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[3]; // Indices of the coords int16_t coords[3]; // Indices of the coords
int16_t normals[3]; // Indices of the normals int16_t normals[3]; // Indices of the normals
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
rgba_t color[3]; rgba_t color[3];
} LSGT3; } LSGT3;
typedef struct LSG4 { typedef struct LSG4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
int16_t normals[4]; // Indices of the normals int16_t normals[4]; // Indices of the normals
rgba_t color[4]; rgba_t color[4];
} LSG4; } LSG4;
typedef struct LSGT4 { typedef struct LSGT4 {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
int16_t coords[4]; // Indices of the coords int16_t coords[4]; // Indices of the coords
int16_t normals[4]; // Indices of the normals int16_t normals[4]; // Indices of the normals
int16_t texture; int16_t texture;
int16_t cba; int16_t cba;
int16_t tsb; int16_t tsb;
uint8_t u0; uint8_t u0;
uint8_t v0; uint8_t v0;
uint8_t u1; uint8_t u1;
uint8_t v1; uint8_t v1;
uint8_t u2; uint8_t u2;
uint8_t v2; uint8_t v2;
uint8_t u3; uint8_t u3;
uint8_t v3; uint8_t v3;
int16_t pad1; int16_t pad1;
rgba_t color[4]; rgba_t color[4];
} LSGT4; } LSGT4;
/* OTHER PRIMITIVE TYPES /* OTHER PRIMITIVE TYPES
*/ */
typedef struct SPR { typedef struct SPR {
int16_t type; int16_t type;
int16_t flag; int16_t flag;
int16_t coord; int16_t coord;
int16_t width; int16_t width;
int16_t height; int16_t height;
int16_t texture; int16_t texture;
rgba_t color; rgba_t color;
} SPR; } SPR;
typedef struct Spline { typedef struct Spline {
int16_t type; // Type of primitive int16_t type; // Type of primitive
int16_t flag; int16_t flag;
vec3_t control1; vec3_t control1;
vec3_t position; vec3_t position;
vec3_t control2; vec3_t control2;
rgba_t color; rgba_t color;
} Spline; } Spline;
typedef struct PointLight { typedef struct PointLight {
int16_t type; int16_t type;
int16_t flag; int16_t flag;
vec3_t position; vec3_t position;
rgba_t color; rgba_t color;
int16_t startFalloff; int16_t startFalloff;
int16_t endFalloff; int16_t endFalloff;
} PointLight; } PointLight;
typedef struct SpotLight { typedef struct SpotLight {
int16_t type; int16_t type;
int16_t flag; int16_t flag;
vec3_t position; vec3_t position;
vec3_t direction; vec3_t direction;
rgba_t color; rgba_t color;
int16_t startFalloff; int16_t startFalloff;
int16_t endFalloff; int16_t endFalloff;
int16_t coneAngle; int16_t coneAngle;
int16_t spreadAngle; int16_t spreadAngle;
} SpotLight; } SpotLight;
typedef struct InfiniteLight { typedef struct InfiniteLight {
int16_t type; int16_t type;
int16_t flag; int16_t flag;
vec3_t direction; vec3_t direction;
rgba_t color; rgba_t color;
} InfiniteLight; } InfiniteLight;
// PRIMITIVE FLAGS // PRIMITIVE FLAGS
#define PRM_SINGLE_SIDED 0x0001 #define PRM_SINGLE_SIDED 0x0001
#define PRM_SHIP_ENGINE 0x0002 #define PRM_SHIP_ENGINE 0x0002
#define PRM_TRANSLUCENT 0x0004 #define PRM_TRANSLUCENT 0x0004
#define PRM_TYPE_F3 1 #define PRM_TYPE_F3 1
#define PRM_TYPE_FT3 2 #define PRM_TYPE_FT3 2
#define PRM_TYPE_F4 3 #define PRM_TYPE_F4 3
#define PRM_TYPE_FT4 4 #define PRM_TYPE_FT4 4
#define PRM_TYPE_G3 5 #define PRM_TYPE_G3 5
#define PRM_TYPE_GT3 6 #define PRM_TYPE_GT3 6
#define PRM_TYPE_G4 7 #define PRM_TYPE_G4 7
#define PRM_TYPE_GT4 8 #define PRM_TYPE_GT4 8
#define PRM_TYPE_LF2 9 #define PRM_TYPE_LF2 9
#define PRM_TYPE_TSPR 10 #define PRM_TYPE_TSPR 10
#define PRM_TYPE_BSPR 11 #define PRM_TYPE_BSPR 11
#define PRM_TYPE_LSF3 12 #define PRM_TYPE_LSF3 12
#define PRM_TYPE_LSFT3 13 #define PRM_TYPE_LSFT3 13
#define PRM_TYPE_LSF4 14 #define PRM_TYPE_LSF4 14
#define PRM_TYPE_LSFT4 15 #define PRM_TYPE_LSFT4 15
#define PRM_TYPE_LSG3 16 #define PRM_TYPE_LSG3 16
#define PRM_TYPE_LSGT3 17 #define PRM_TYPE_LSGT3 17
#define PRM_TYPE_LSG4 18 #define PRM_TYPE_LSG4 18
#define PRM_TYPE_LSGT4 19 #define PRM_TYPE_LSGT4 19
#define PRM_TYPE_SPLINE 20 #define PRM_TYPE_SPLINE 20
#define PRM_TYPE_INFINITE_LIGHT 21 #define PRM_TYPE_INFINITE_LIGHT 21
#define PRM_TYPE_POINT_LIGHT 22 #define PRM_TYPE_POINT_LIGHT 22
#define PRM_TYPE_SPOT_LIGHT 23 #define PRM_TYPE_SPOT_LIGHT 23
typedef struct Object { typedef struct Object {
char name[16]; char name[16];
mat4_t mat; mat4_t mat;
int16_t vertices_len; // Number of Vertices int16_t vertices_len; // Number of Vertices
vec3_t *vertices; // Pointer to 3D Points vec3_t *vertices; // Pointer to 3D Points
int16_t normals_len; // Number of Normals int16_t normals_len; // Number of Normals
vec3_t *normals; // Pointer to 3D Normals vec3_t *normals; // Pointer to 3D Normals
int16_t primitives_len; // Number of Primitives int16_t primitives_len; // Number of Primitives
Primitive *primitives; // Pointer to Z Sort Primitives Primitive *primitives; // Pointer to Z Sort Primitives
vec3_t origin; vec3_t origin;
int32_t extent; // Flags for object characteristics int32_t extent; // Flags for object characteristics
int16_t flags; // Next object in list int16_t flags; // Next object in list
float radius; float radius;
struct Object *next; // Next object in list struct Object *next; // Next object in list
} Object; } Object;
typedef union Prm { typedef union Prm {
uint8_t *ptr; uint8_t *ptr;
int16_t *sptr; int16_t *sptr;
int32_t *lptr; int32_t *lptr;
Object *object; Object *object;
Primitive *primitive; Primitive *primitive;
F3 *f3; F3 *f3;
FT3 *ft3; FT3 *ft3;
F4 *f4; F4 *f4;
FT4 *ft4; FT4 *ft4;
G3 *g3; G3 *g3;
GT3 *gt3; GT3 *gt3;
G4 *g4; G4 *g4;
GT4 *gt4; GT4 *gt4;
SPR *spr; SPR *spr;
Spline *spline; Spline *spline;
PointLight *pointLight; PointLight *pointLight;
SpotLight *spotLight; SpotLight *spotLight;
InfiniteLight *infiniteLight; InfiniteLight *infiniteLight;
LSF3 *lsf3; LSF3 *lsf3;
LSFT3 *lsft3; LSFT3 *lsft3;
LSF4 *lsf4; LSF4 *lsf4;
LSFT4 *lsft4; LSFT4 *lsft4;
LSG3 *lsg3; LSG3 *lsg3;
LSGT3 *lsgt3; LSGT3 *lsgt3;
LSG4 *lsg4; LSG4 *lsg4;
LSGT4 *lsgt4; LSGT4 *lsgt4;
} Prm; } Prm;
Object *objects_load(char *name, texture_list_t tl); Object *objects_load(char *name, texture_list_t tl);
void object_draw(Object *object, mat4_t *mat); void object_draw(Object *object, mat4_t *mat);
#endif #endif

View file

@ -1,279 +1,279 @@
#include "../mem.h" #include "../mem.h"
#include "../input.h" #include "../input.h"
#include "../platform.h" #include "../platform.h"
#include "../system.h" #include "../system.h"
#include "../utils.h" #include "../utils.h"
#include "object.h" #include "object.h"
#include "track.h" #include "track.h"
#include "ship.h" #include "ship.h"
#include "weapon.h" #include "weapon.h"
#include "droid.h" #include "droid.h"
#include "camera.h" #include "camera.h"
#include "object.h" #include "object.h"
#include "scene.h" #include "scene.h"
#include "game.h" #include "game.h"
#include "hud.h" #include "hud.h"
#include "sfx.h" #include "sfx.h"
#include "race.h" #include "race.h"
#include "particle.h" #include "particle.h"
#include "menu.h" #include "menu.h"
#include "ship_ai.h" #include "ship_ai.h"
#include "ingame_menus.h" #include "ingame_menus.h"
#define ATTRACT_DURATION 60.0 #define ATTRACT_DURATION 60.0
static bool is_paused = false; static bool is_paused = false;
static bool menu_is_scroll_text = false; static bool menu_is_scroll_text = false;
static bool has_show_credits = false; static bool has_show_credits = false;
static float attract_start_time; static float attract_start_time;
static menu_t *active_menu = NULL; static menu_t *active_menu = NULL;
void race_init(void) { void race_init(void) {
ingame_menus_load(); ingame_menus_load();
menu_is_scroll_text = false; menu_is_scroll_text = false;
const circut_settings_t *cs = &def.circuts[g.circut].settings[g.race_class]; const circut_settings_t *cs = &def.circuts[g.circut].settings[g.race_class];
track_load(cs->path); track_load(cs->path);
scene_load(cs->path, cs->sky_y_offset); scene_load(cs->path, cs->sky_y_offset);
if (g.circut == CIRCUT_SILVERSTREAM && g.race_class == RACE_CLASS_RAPIER) { if (g.circut == CIRCUT_SILVERSTREAM && g.race_class == RACE_CLASS_RAPIER) {
scene_init_aurora_borealis(); scene_init_aurora_borealis();
} }
race_start(); race_start();
// render_textures_dump("texture_atlas.png"); // render_textures_dump("texture_atlas.png");
if (g.is_attract_mode) { if (g.is_attract_mode) {
attract_start_time = system_time(); attract_start_time = system_time();
for (int i = 0; i < len(g.ships); i++) { for (int i = 0; i < len(g.ships); i++) {
// FIXME: this is needed to initializes the engine sound. Should // FIXME: this is needed to initializes the engine sound. Should
// maybe be done in a separate step? // maybe be done in a separate step?
ship_ai_update_intro(&g.ships[i]); ship_ai_update_intro(&g.ships[i]);
g.ships[i].update_func = ship_ai_update_race; g.ships[i].update_func = ship_ai_update_race;
flags_rm(g.ships[i].flags, SHIP_VIEW_INTERNAL); flags_rm(g.ships[i].flags, SHIP_VIEW_INTERNAL);
flags_rm(g.ships[i].flags, SHIP_RACING); flags_rm(g.ships[i].flags, SHIP_RACING);
} }
g.pilot = rand_int(0, len(def.pilots)); g.pilot = rand_int(0, len(def.pilots));
g.camera.update_func = camera_update_attract_random; g.camera.update_func = camera_update_attract_random;
if (!has_show_credits || rand_int(0, 10) == 0) { if (!has_show_credits || rand_int(0, 10) == 0) {
active_menu = text_scroll_menu_init(def.credits, len(def.credits)); active_menu = text_scroll_menu_init(def.credits, len(def.credits));
menu_is_scroll_text = true; menu_is_scroll_text = true;
has_show_credits = true; has_show_credits = true;
} }
} }
is_paused = false; is_paused = false;
} }
void race_update(void) { void race_update(void) {
if (is_paused) { if (is_paused) {
if (!active_menu) { if (!active_menu) {
active_menu = pause_menu_init(); active_menu = pause_menu_init();
} }
if (input_pressed(A_MENU_QUIT)) { if (input_pressed(A_MENU_QUIT)) {
race_unpause(); race_unpause();
} }
} }
else { else {
ships_update(); ships_update();
droid_update(&g.droid, &g.ships[g.pilot]); droid_update(&g.droid, &g.ships[g.pilot]);
camera_update(&g.camera, &g.ships[g.pilot], &g.droid); camera_update(&g.camera, &g.ships[g.pilot], &g.droid);
weapons_update(); weapons_update();
particles_update(); particles_update();
scene_update(); scene_update();
if (g.race_type != RACE_TYPE_TIME_TRIAL) { if (g.race_type != RACE_TYPE_TIME_TRIAL) {
track_cycle_pickups(); track_cycle_pickups();
} }
if (g.is_attract_mode) { if (g.is_attract_mode) {
if (input_pressed(A_MENU_START) || input_pressed(A_MENU_SELECT)) { if (input_pressed(A_MENU_START) || input_pressed(A_MENU_SELECT)) {
game_set_scene(GAME_SCENE_MAIN_MENU); game_set_scene(GAME_SCENE_MAIN_MENU);
} }
float duration = system_time() - attract_start_time; float duration = system_time() - attract_start_time;
if ((!active_menu && duration > 30) || duration > 120) { if ((!active_menu && duration > 30) || duration > 120) {
game_set_scene(GAME_SCENE_TITLE); game_set_scene(GAME_SCENE_TITLE);
} }
} }
else if (active_menu == NULL && (input_pressed(A_MENU_START) || input_pressed(A_MENU_QUIT))) { else if (active_menu == NULL && (input_pressed(A_MENU_START) || input_pressed(A_MENU_QUIT))) {
race_pause(); race_pause();
} }
} }
// Draw 3D // Draw 3D
render_set_view(g.camera.position, g.camera.angle); render_set_view(g.camera.position, g.camera.angle);
render_set_cull_backface(false); render_set_cull_backface(false);
scene_draw(&g.camera); scene_draw(&g.camera);
track_draw(&g.camera); track_draw(&g.camera);
render_set_cull_backface(true); render_set_cull_backface(true);
ships_draw(); ships_draw();
droid_draw(&g.droid); droid_draw(&g.droid);
weapons_draw(); weapons_draw();
particles_draw(); particles_draw();
// Draw 2d // Draw 2d
render_set_view_2d(); render_set_view_2d();
if (flags_is(g.ships[g.pilot].flags, SHIP_RACING)) { if (flags_is(g.ships[g.pilot].flags, SHIP_RACING)) {
hud_draw(&g.ships[g.pilot]); hud_draw(&g.ships[g.pilot]);
} }
if (active_menu) { if (active_menu) {
if (!menu_is_scroll_text) { if (!menu_is_scroll_text) {
vec2i_t size = render_size(); vec2i_t size = render_size();
render_push_2d(vec2i(0, 0), size, rgba(0, 0, 0, 128), RENDER_NO_TEXTURE); render_push_2d(vec2i(0, 0), size, rgba(0, 0, 0, 128), RENDER_NO_TEXTURE);
} }
menu_update(active_menu); menu_update(active_menu);
} }
} }
void race_start(void) { void race_start(void) {
active_menu = NULL; active_menu = NULL;
sfx_reset(); sfx_reset();
scene_init(); scene_init();
camera_init(&g.camera, g.track.sections); camera_init(&g.camera, g.track.sections);
g.camera.update_func = camera_update_race_intro; g.camera.update_func = camera_update_race_intro;
ships_init(g.track.sections); ships_init(g.track.sections);
droid_init(&g.droid, &g.ships[g.pilot]); droid_init(&g.droid, &g.ships[g.pilot]);
particles_init(); particles_init();
weapons_init(); weapons_init();
for (int i = 0; i < len(g.race_ranks); i++) { for (int i = 0; i < len(g.race_ranks); i++) {
g.race_ranks[i].points = 0; g.race_ranks[i].points = 0;
g.race_ranks[i].pilot = i; g.race_ranks[i].pilot = i;
} }
for (int i = 0; i < len(g.lap_times); i++) { for (int i = 0; i < len(g.lap_times); i++) {
for (int j = 0; j < len(g.lap_times[i]); j++) { for (int j = 0; j < len(g.lap_times[i]); j++) {
g.lap_times[i][j] = 0; g.lap_times[i][j] = 0;
} }
} }
g.is_new_race_record = false; g.is_new_race_record = false;
g.is_new_lap_record = false; g.is_new_lap_record = false;
g.best_lap = 0; g.best_lap = 0;
g.race_time = 0; g.race_time = 0;
} }
void race_restart(void) { void race_restart(void) {
race_unpause(); race_unpause();
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
g.lives--; g.lives--;
if (g.lives == 0) { if (g.lives == 0) {
race_release_control(); race_release_control();
active_menu = game_over_menu_init(); active_menu = game_over_menu_init();
return; return;
} }
} }
race_start(); race_start();
} }
static bool sort_points_compare(pilot_points_t *pa, pilot_points_t *pb) { static bool sort_points_compare(pilot_points_t *pa, pilot_points_t *pb) {
return (pa->points < pb->points); return (pa->points < pb->points);
} }
void race_end(void) { void race_end(void) {
race_release_control(); race_release_control();
g.race_position = g.ships[g.pilot].position_rank; g.race_position = g.ships[g.pilot].position_rank;
g.race_time = 0; g.race_time = 0;
g.best_lap = g.lap_times[g.pilot][0]; g.best_lap = g.lap_times[g.pilot][0];
for (int i = 0; i < NUM_LAPS; i++) { for (int i = 0; i < NUM_LAPS; i++) {
g.race_time += g.lap_times[g.pilot][i]; g.race_time += g.lap_times[g.pilot][i];
if (g.lap_times[g.pilot][i] < g.best_lap) { if (g.lap_times[g.pilot][i] < g.best_lap) {
g.best_lap = g.lap_times[g.pilot][i]; g.best_lap = g.lap_times[g.pilot][i];
} }
} }
highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab]; highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab];
if (g.best_lap < hs->lap_record) { if (g.best_lap < hs->lap_record) {
hs->lap_record = g.best_lap; hs->lap_record = g.best_lap;
g.is_new_lap_record = true; g.is_new_lap_record = true;
save.is_dirty = true; save.is_dirty = true;
} }
for (int i = 0; i < NUM_HIGHSCORES; i++) { for (int i = 0; i < NUM_HIGHSCORES; i++) {
if (g.race_time < hs->entries[i].time) { if (g.race_time < hs->entries[i].time) {
g.is_new_race_record = true; g.is_new_race_record = true;
break; break;
} }
} }
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) { if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
for (int i = 0; i < len(def.race_points_for_rank); i++) { for (int i = 0; i < len(def.race_points_for_rank); i++) {
g.race_ranks[i].points = def.race_points_for_rank[i]; g.race_ranks[i].points = def.race_points_for_rank[i];
// Find the pilot for this race rank in the championship table // Find the pilot for this race rank in the championship table
for (int j = 0; j < len(g.championship_ranks); j++) { for (int j = 0; j < len(g.championship_ranks); j++) {
if (g.race_ranks[i].pilot == g.championship_ranks[j].pilot) { if (g.race_ranks[i].pilot == g.championship_ranks[j].pilot) {
g.championship_ranks[j].points += def.race_points_for_rank[i]; g.championship_ranks[j].points += def.race_points_for_rank[i];
break; break;
} }
} }
} }
sort(g.championship_ranks, len(g.championship_ranks), sort_points_compare); sort(g.championship_ranks, len(g.championship_ranks), sort_points_compare);
} }
active_menu = race_stats_menu_init(); active_menu = race_stats_menu_init();
} }
void race_next(void) { void race_next(void) {
int next_circut = g.circut + 1; int next_circut = g.circut + 1;
// Championship complete // Championship complete
if ( if (
(save.has_bonus_circuts && next_circut >= NUM_CIRCUTS) || (save.has_bonus_circuts && next_circut >= NUM_CIRCUTS) ||
(!save.has_bonus_circuts && next_circut >= NUM_NON_BONUS_CIRCUTS) (!save.has_bonus_circuts && next_circut >= NUM_NON_BONUS_CIRCUTS)
) { ) {
if (g.race_class == RACE_CLASS_RAPIER) { if (g.race_class == RACE_CLASS_RAPIER) {
if (save.has_bonus_circuts) { if (save.has_bonus_circuts) {
active_menu = text_scroll_menu_init(def.congratulations.rapier_all_circuts, len(def.congratulations.rapier_all_circuts)); active_menu = text_scroll_menu_init(def.congratulations.rapier_all_circuts, len(def.congratulations.rapier_all_circuts));
} }
else { else {
save.has_bonus_circuts = true; save.has_bonus_circuts = true;
active_menu = text_scroll_menu_init(def.congratulations.rapier, len(def.congratulations.rapier)); active_menu = text_scroll_menu_init(def.congratulations.rapier, len(def.congratulations.rapier));
} }
} }
else { else {
save.has_rapier_class = true; save.has_rapier_class = true;
if (save.has_bonus_circuts) { if (save.has_bonus_circuts) {
active_menu = text_scroll_menu_init(def.congratulations.venom_all_circuts, len(def.congratulations.venom_all_circuts)); active_menu = text_scroll_menu_init(def.congratulations.venom_all_circuts, len(def.congratulations.venom_all_circuts));
} }
else { else {
active_menu = text_scroll_menu_init(def.congratulations.venom, len(def.congratulations.venom)); active_menu = text_scroll_menu_init(def.congratulations.venom, len(def.congratulations.venom));
} }
} }
save.is_dirty = true; save.is_dirty = true;
menu_is_scroll_text = true; menu_is_scroll_text = true;
} }
// Next track // Next track
else { else {
g.circut = next_circut; g.circut = next_circut;
game_set_scene(GAME_SCENE_RACE); game_set_scene(GAME_SCENE_RACE);
} }
} }
void race_release_control(void) { void race_release_control(void) {
flags_rm(g.ships[g.pilot].flags, SHIP_RACING); flags_rm(g.ships[g.pilot].flags, SHIP_RACING);
g.ships[g.pilot].remote_thrust_max = 3160; g.ships[g.pilot].remote_thrust_max = 3160;
g.ships[g.pilot].remote_thrust_mag = 32; g.ships[g.pilot].remote_thrust_mag = 32;
g.ships[g.pilot].speed = 3160; g.ships[g.pilot].speed = 3160;
g.camera.update_func = camera_update_attract_random; g.camera.update_func = camera_update_attract_random;
} }
void race_pause(void) { void race_pause(void) {
sfx_pause(); sfx_pause();
is_paused = true; is_paused = true;
} }
void race_unpause(void) { void race_unpause(void) {
sfx_unpause(); sfx_unpause();
is_paused = false; is_paused = false;
active_menu = NULL; active_menu = NULL;
} }

View file

@ -1,14 +1,14 @@
#ifndef RACE_H #ifndef RACE_H
#define RACE_H #define RACE_H
void race_init(void); void race_init(void);
void race_update(void); void race_update(void);
void race_start(void); void race_start(void);
void race_restart(void); void race_restart(void);
void race_pause(void); void race_pause(void);
void race_unpause(void); void race_unpause(void);
void race_end(void); void race_end(void);
void race_next(void); void race_next(void);
void race_release_control(void); void race_release_control(void);
#endif #endif

View file

@ -1,266 +1,266 @@
#include "../mem.h" #include "../mem.h"
#include "../utils.h" #include "../utils.h"
#include "../system.h" #include "../system.h"
#include "object.h" #include "object.h"
#include "track.h" #include "track.h"
#include "ship.h" #include "ship.h"
#include "weapon.h" #include "weapon.h"
#include "scene.h" #include "scene.h"
#include "droid.h" #include "droid.h"
#include "camera.h" #include "camera.h"
#include "object.h" #include "object.h"
#include "game.h" #include "game.h"
#define SCENE_START_BOOMS_MAX 4 #define SCENE_START_BOOMS_MAX 4
#define SCENE_OIL_PUMPS_MAX 2 #define SCENE_OIL_PUMPS_MAX 2
#define SCENE_RED_LIGHTS_MAX 4 #define SCENE_RED_LIGHTS_MAX 4
#define SCENE_STANDS_MAX 20 #define SCENE_STANDS_MAX 20
static Object *scene_objects; static Object *scene_objects;
static Object *sky_object; static Object *sky_object;
static vec3_t sky_offset; static vec3_t sky_offset;
static Object *start_booms[SCENE_START_BOOMS_MAX]; static Object *start_booms[SCENE_START_BOOMS_MAX];
static int start_booms_len; static int start_booms_len;
static Object *oil_pumps[SCENE_OIL_PUMPS_MAX]; static Object *oil_pumps[SCENE_OIL_PUMPS_MAX];
static int oil_pumps_len; static int oil_pumps_len;
static Object *red_lights[SCENE_RED_LIGHTS_MAX]; static Object *red_lights[SCENE_RED_LIGHTS_MAX];
static int red_lights_len; static int red_lights_len;
typedef struct { typedef struct {
sfx_t *sfx; sfx_t *sfx;
vec3_t pos; vec3_t pos;
} scene_stand_t; } scene_stand_t;
static scene_stand_t stands[SCENE_STANDS_MAX]; static scene_stand_t stands[SCENE_STANDS_MAX];
static int stands_len; static int stands_len;
static struct { static struct {
bool enabled; bool enabled;
GT4 *primitives[80]; GT4 *primitives[80];
int16_t *coords[80]; int16_t *coords[80];
int16_t grey_coords[80]; int16_t grey_coords[80];
} aurora_borealis; } aurora_borealis;
void scene_pulsate_red_light(Object *obj); void scene_pulsate_red_light(Object *obj);
void scene_move_oil_pump(Object *obj); void scene_move_oil_pump(Object *obj);
void scene_update_aurora_borealis(void); void scene_update_aurora_borealis(void);
void scene_load(const char *base_path, float sky_y_offset) { void scene_load(const char *base_path, float sky_y_offset) {
texture_list_t scene_textures = image_get_compressed_textures(get_path(base_path, "scene.cmp")); texture_list_t scene_textures = image_get_compressed_textures(get_path(base_path, "scene.cmp"));
scene_objects = objects_load(get_path(base_path, "scene.prm"), scene_textures); scene_objects = objects_load(get_path(base_path, "scene.prm"), scene_textures);
texture_list_t sky_textures = image_get_compressed_textures(get_path(base_path, "sky.cmp")); texture_list_t sky_textures = image_get_compressed_textures(get_path(base_path, "sky.cmp"));
sky_object = objects_load(get_path(base_path, "sky.prm") , sky_textures); sky_object = objects_load(get_path(base_path, "sky.prm") , sky_textures);
sky_offset = vec3(0, sky_y_offset, 0); sky_offset = vec3(0, sky_y_offset, 0);
// Collect all objects that need to be updated each frame // Collect all objects that need to be updated each frame
start_booms_len = 0; start_booms_len = 0;
oil_pumps_len = 0; oil_pumps_len = 0;
red_lights_len = 0; red_lights_len = 0;
stands_len = 0; stands_len = 0;
Object *obj = scene_objects; Object *obj = scene_objects;
while (obj) { while (obj) {
mat4_set_translation(&obj->mat, obj->origin); mat4_set_translation(&obj->mat, obj->origin);
if (str_starts_with(obj->name, "start")) { if (str_starts_with(obj->name, "start")) {
error_if(start_booms_len >= SCENE_START_BOOMS_MAX, "SCENE_START_BOOMS_MAX reached"); error_if(start_booms_len >= SCENE_START_BOOMS_MAX, "SCENE_START_BOOMS_MAX reached");
start_booms[start_booms_len++] = obj; start_booms[start_booms_len++] = obj;
} }
else if (str_starts_with(obj->name, "redl")) { else if (str_starts_with(obj->name, "redl")) {
error_if(red_lights_len >= SCENE_RED_LIGHTS_MAX, "SCENE_RED_LIGHTS_MAX reached"); error_if(red_lights_len >= SCENE_RED_LIGHTS_MAX, "SCENE_RED_LIGHTS_MAX reached");
red_lights[red_lights_len++] = obj; red_lights[red_lights_len++] = obj;
} }
else if (str_starts_with(obj->name, "donkey")) { else if (str_starts_with(obj->name, "donkey")) {
error_if(oil_pumps_len >= SCENE_OIL_PUMPS_MAX, "SCENE_OIL_PUMPS_MAX reached"); error_if(oil_pumps_len >= SCENE_OIL_PUMPS_MAX, "SCENE_OIL_PUMPS_MAX reached");
oil_pumps[oil_pumps_len++] = obj; oil_pumps[oil_pumps_len++] = obj;
} }
else if ( else if (
str_starts_with(obj->name, "lostad") || str_starts_with(obj->name, "lostad") ||
str_starts_with(obj->name, "stad_") || str_starts_with(obj->name, "stad_") ||
str_starts_with(obj->name, "newstad_") str_starts_with(obj->name, "newstad_")
) { ) {
error_if(stands_len >= SCENE_STANDS_MAX, "SCENE_STANDS_MAX reached"); error_if(stands_len >= SCENE_STANDS_MAX, "SCENE_STANDS_MAX reached");
stands[stands_len++] = (scene_stand_t){.sfx = NULL, .pos = obj->origin}; stands[stands_len++] = (scene_stand_t){.sfx = NULL, .pos = obj->origin};
} }
obj = obj->next; obj = obj->next;
} }
aurora_borealis.enabled = false; aurora_borealis.enabled = false;
} }
void scene_init(void) { void scene_init(void) {
scene_set_start_booms(0); scene_set_start_booms(0);
for (int i = 0; i < stands_len; i++) { for (int i = 0; i < stands_len; i++) {
stands[i].sfx = sfx_reserve_loop(SFX_CROWD); stands[i].sfx = sfx_reserve_loop(SFX_CROWD);
} }
} }
void scene_update(void) { void scene_update(void) {
for (int i = 0; i < red_lights_len; i++) { for (int i = 0; i < red_lights_len; i++) {
scene_pulsate_red_light(red_lights[i]); scene_pulsate_red_light(red_lights[i]);
} }
for (int i = 0; i < oil_pumps_len; i++) { for (int i = 0; i < oil_pumps_len; i++) {
scene_move_oil_pump(oil_pumps[i]); scene_move_oil_pump(oil_pumps[i]);
} }
for (int i = 0; i < stands_len; i++) { for (int i = 0; i < stands_len; i++) {
sfx_set_position(stands[i].sfx, stands[i].pos, vec3(0, 0, 0), 0.4); sfx_set_position(stands[i].sfx, stands[i].pos, vec3(0, 0, 0), 0.4);
} }
if (aurora_borealis.enabled) { if (aurora_borealis.enabled) {
scene_update_aurora_borealis(); scene_update_aurora_borealis();
} }
} }
void scene_draw(camera_t *camera) { void scene_draw(camera_t *camera) {
// Sky // Sky
render_set_depth_write(false); render_set_depth_write(false);
mat4_set_translation(&sky_object->mat, vec3_add(camera->position, sky_offset)); mat4_set_translation(&sky_object->mat, vec3_add(camera->position, sky_offset));
object_draw(sky_object, &sky_object->mat); object_draw(sky_object, &sky_object->mat);
render_set_depth_write(true); render_set_depth_write(true);
// Objects // Objects
// Calculate the camera forward vector, so we can cull everything that's // Calculate the camera forward vector, so we can cull everything that's
// behind. Ideally we'd want to do a full frustum culling here. FIXME. // behind. Ideally we'd want to do a full frustum culling here. FIXME.
vec3_t cam_pos = camera->position; vec3_t cam_pos = camera->position;
vec3_t cam_dir = camera_forward(camera); vec3_t cam_dir = camera_forward(camera);
Object *object = scene_objects; Object *object = scene_objects;
while (object) { while (object) {
vec3_t diff = vec3_sub(cam_pos, object->origin); vec3_t diff = vec3_sub(cam_pos, object->origin);
float cam_dot = vec3_dot(diff, cam_dir); float cam_dot = vec3_dot(diff, cam_dir);
float dist_sq = vec3_dot(diff, diff); float dist_sq = vec3_dot(diff, diff);
if ( if (
cam_dot < object->radius && cam_dot < object->radius &&
dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR) dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR)
) { ) {
object_draw(object, &object->mat); object_draw(object, &object->mat);
} }
object = object->next; object = object->next;
} }
} }
void scene_set_start_booms(int light_index) { void scene_set_start_booms(int light_index) {
int lights_len = 1; int lights_len = 1;
rgba_t color = rgba(0, 0, 0, 0); rgba_t color = rgba(0, 0, 0, 0);
if (light_index == 0) { // reset all 3 if (light_index == 0) { // reset all 3
lights_len = 3; lights_len = 3;
color = rgba(0x20, 0x20, 0x20, 0xff); color = rgba(0x20, 0x20, 0x20, 0xff);
} }
else if (light_index == 1) { else if (light_index == 1) {
color = rgba(0xff, 0x00, 0x00, 0xff); color = rgba(0xff, 0x00, 0x00, 0xff);
} }
else if (light_index == 2) { else if (light_index == 2) {
color = rgba(0xff, 0x80, 0x00, 0xff); color = rgba(0xff, 0x80, 0x00, 0xff);
} }
else if (light_index == 3) { else if (light_index == 3) {
color = rgba(0x00, 0xff, 0x00, 0xff); color = rgba(0x00, 0xff, 0x00, 0xff);
} }
for (int i = 0; i < start_booms_len; i++) { for (int i = 0; i < start_booms_len; i++) {
Prm libPoly = {.primitive = start_booms[i]->primitives}; Prm libPoly = {.primitive = start_booms[i]->primitives};
for (int j = 1; j < light_index; j++) { for (int j = 1; j < light_index; j++) {
libPoly.gt4 += 1; libPoly.gt4 += 1;
} }
for (int j = 0; j < lights_len; j++) { for (int j = 0; j < lights_len; j++) {
for (int v = 0; v < 4; v++) { for (int v = 0; v < 4; v++) {
libPoly.gt4->color[v].r = color.r; libPoly.gt4->color[v].r = color.r;
libPoly.gt4->color[v].g = color.g; libPoly.gt4->color[v].g = color.g;
libPoly.gt4->color[v].b = color.b; libPoly.gt4->color[v].b = color.b;
} }
libPoly.gt4 += 1; libPoly.gt4 += 1;
} }
} }
} }
void scene_pulsate_red_light(Object *obj) { void scene_pulsate_red_light(Object *obj) {
uint8_t r = clamp(sin(system_cycle_time() * M_PI * 2) * 128 + 128, 0, 255); uint8_t r = clamp(sin(system_cycle_time() * M_PI * 2) * 128 + 128, 0, 255);
Prm libPoly = {.primitive = obj->primitives}; Prm libPoly = {.primitive = obj->primitives};
for (int v = 0; v < 4; v++) { for (int v = 0; v < 4; v++) {
libPoly.gt4->color[v].r = r; libPoly.gt4->color[v].r = r;
libPoly.gt4->color[v].g = 0x00; libPoly.gt4->color[v].g = 0x00;
libPoly.gt4->color[v].b = 0x00; libPoly.gt4->color[v].b = 0x00;
} }
} }
void scene_move_oil_pump(Object *pump) { void scene_move_oil_pump(Object *pump) {
mat4_set_yaw_pitch_roll(&pump->mat, vec3(sin(system_cycle_time() * 0.125 * M_PI * 2), 0, 0)); mat4_set_yaw_pitch_roll(&pump->mat, vec3(sin(system_cycle_time() * 0.125 * M_PI * 2), 0, 0));
} }
void scene_init_aurora_borealis(void) { void scene_init_aurora_borealis(void) {
aurora_borealis.enabled = true; aurora_borealis.enabled = true;
clear(aurora_borealis.grey_coords); clear(aurora_borealis.grey_coords);
int count = 0; int count = 0;
int16_t *coords; int16_t *coords;
float y; float y;
Prm poly = {.primitive = sky_object->primitives}; Prm poly = {.primitive = sky_object->primitives};
for (int i = 0; i < sky_object->primitives_len; i++) { for (int i = 0; i < sky_object->primitives_len; i++) {
switch (poly.primitive->type) { switch (poly.primitive->type) {
case PRM_TYPE_GT3: case PRM_TYPE_GT3:
poly.gt3 += 1; poly.gt3 += 1;
break; break;
case PRM_TYPE_GT4: case PRM_TYPE_GT4:
coords = poly.gt4->coords; coords = poly.gt4->coords;
y = sky_object->vertices[coords[0]].y; y = sky_object->vertices[coords[0]].y;
if (y < -6000) { // -8000 if (y < -6000) { // -8000
aurora_borealis.primitives[count] = poly.gt4; aurora_borealis.primitives[count] = poly.gt4;
if (y > -6800) { if (y > -6800) {
aurora_borealis.coords[count] = poly.gt4->coords; aurora_borealis.coords[count] = poly.gt4->coords;
aurora_borealis.grey_coords[count] = -1; aurora_borealis.grey_coords[count] = -1;
} }
else if (y < -11000) { else if (y < -11000) {
aurora_borealis.coords[count] = poly.gt4->coords; aurora_borealis.coords[count] = poly.gt4->coords;
aurora_borealis.grey_coords[count] = -2; aurora_borealis.grey_coords[count] = -2;
} }
else { else {
aurora_borealis.coords[count] = poly.gt4->coords; aurora_borealis.coords[count] = poly.gt4->coords;
} }
count++; count++;
} }
poly.gt4 += 1; poly.gt4 += 1;
break; break;
} }
} }
} }
void scene_update_aurora_borealis(void) { void scene_update_aurora_borealis(void) {
float phase = system_time() / 30.0; float phase = system_time() / 30.0;
for (int i = 0; i < 80; i++) { for (int i = 0; i < 80; i++) {
int16_t *coords = aurora_borealis.coords[i]; int16_t *coords = aurora_borealis.coords[i];
if (aurora_borealis.grey_coords[i] != -2) { if (aurora_borealis.grey_coords[i] != -2) {
aurora_borealis.primitives[i]->color[0].r = (sin(coords[0] * phase) * 64.0) + 190; aurora_borealis.primitives[i]->color[0].r = (sin(coords[0] * phase) * 64.0) + 190;
aurora_borealis.primitives[i]->color[0].g = (sin(coords[0] * (phase + 0.054)) * 64.0) + 190; aurora_borealis.primitives[i]->color[0].g = (sin(coords[0] * (phase + 0.054)) * 64.0) + 190;
aurora_borealis.primitives[i]->color[0].b = (sin(coords[0] * (phase + 0.039)) * 64.0) + 190; aurora_borealis.primitives[i]->color[0].b = (sin(coords[0] * (phase + 0.039)) * 64.0) + 190;
} }
if (aurora_borealis.grey_coords[i] != -2) { if (aurora_borealis.grey_coords[i] != -2) {
aurora_borealis.primitives[i]->color[1].r = (sin(coords[1] * phase) * 64.0) + 190; aurora_borealis.primitives[i]->color[1].r = (sin(coords[1] * phase) * 64.0) + 190;
aurora_borealis.primitives[i]->color[1].g = (sin(coords[1] * (phase + 0.054)) * 64.0) + 190; aurora_borealis.primitives[i]->color[1].g = (sin(coords[1] * (phase + 0.054)) * 64.0) + 190;
aurora_borealis.primitives[i]->color[1].b = (sin(coords[1] * (phase + 0.039)) * 64.0) + 190; aurora_borealis.primitives[i]->color[1].b = (sin(coords[1] * (phase + 0.039)) * 64.0) + 190;
} }
if (aurora_borealis.grey_coords[i] != -1) { if (aurora_borealis.grey_coords[i] != -1) {
aurora_borealis.primitives[i]->color[2].r = (sin(coords[2] * phase) * 64.0) + 190; aurora_borealis.primitives[i]->color[2].r = (sin(coords[2] * phase) * 64.0) + 190;
aurora_borealis.primitives[i]->color[2].g = (sin(coords[2] * (phase + 0.054)) * 64.0) + 190; aurora_borealis.primitives[i]->color[2].g = (sin(coords[2] * (phase + 0.054)) * 64.0) + 190;
aurora_borealis.primitives[i]->color[2].b = (sin(coords[2] * (phase + 0.039)) * 64.0) + 190; aurora_borealis.primitives[i]->color[2].b = (sin(coords[2] * (phase + 0.039)) * 64.0) + 190;
} }
if (aurora_borealis.grey_coords[i] != -1) { if (aurora_borealis.grey_coords[i] != -1) {
aurora_borealis.primitives[i]->color[3].r = (sin(coords[3] * phase) * 64.0) + 190; aurora_borealis.primitives[i]->color[3].r = (sin(coords[3] * phase) * 64.0) + 190;
aurora_borealis.primitives[i]->color[3].g = (sin(coords[3] * (phase + 0.054)) * 64.0) + 190; aurora_borealis.primitives[i]->color[3].g = (sin(coords[3] * (phase + 0.054)) * 64.0) + 190;
aurora_borealis.primitives[i]->color[3].b = (sin(coords[3] * (phase + 0.039)) * 64.0) + 190; aurora_borealis.primitives[i]->color[3].b = (sin(coords[3] * (phase + 0.039)) * 64.0) + 190;
} }
} }
} }

View file

@ -1,14 +1,14 @@
#ifndef SCENE_H #ifndef SCENE_H
#define SCENE_H #define SCENE_H
#include "image.h" #include "image.h"
#include "camera.h" #include "camera.h"
void scene_load(const char *path, float sky_y_offset); void scene_load(const char *path, float sky_y_offset);
void scene_draw(camera_t *camera); void scene_draw(camera_t *camera);
void scene_init(void); void scene_init(void);
void scene_set_start_booms(int num_lights); void scene_set_start_booms(int num_lights);
void scene_init_aurora_borealis(void); void scene_init_aurora_borealis(void);
void scene_update(void); void scene_update(void);
#endif #endif

View file

@ -1,374 +1,374 @@
#include "../mem.h" #include "../mem.h"
#include "../utils.h" #include "../utils.h"
#include "../render.h" #include "../render.h"
#include "../system.h" #include "../system.h"
#include "../platform.h" #include "../platform.h"
#include "object.h" #include "object.h"
#include "track.h" #include "track.h"
#include "camera.h" #include "camera.h"
#include "object.h" #include "object.h"
#include "game.h" #include "game.h"
void track_load(const char *base_path) { void track_load(const char *base_path) {
// Load and assemble high res track tiles // Load and assemble high res track tiles
g.track.textures.start = render_textures_len(); g.track.textures.start = render_textures_len();
g.track.textures.len = 0; g.track.textures.len = 0;
ttf_t *ttf = track_load_tile_format(get_path(base_path, "library.ttf")); ttf_t *ttf = track_load_tile_format(get_path(base_path, "library.ttf"));
cmp_t *cmp = image_load_compressed(get_path(base_path, "library.cmp")); cmp_t *cmp = image_load_compressed(get_path(base_path, "library.cmp"));
image_t *temp_tile = image_alloc(128, 128); image_t *temp_tile = image_alloc(128, 128);
for (int i = 0; i < ttf->len; i++) { for (int i = 0; i < ttf->len; i++) {
for (int tx = 0; tx < 4; tx++) { for (int tx = 0; tx < 4; tx++) {
for (int ty = 0; ty < 4; ty++) { for (int ty = 0; ty < 4; ty++) {
uint32_t sub_tile_index = ttf->tiles[i].near[ty * 4 + tx]; uint32_t sub_tile_index = ttf->tiles[i].near[ty * 4 + tx];
image_t *sub_tile = image_load_from_bytes(cmp->entries[sub_tile_index], false); image_t *sub_tile = image_load_from_bytes(cmp->entries[sub_tile_index], false);
image_copy(sub_tile, temp_tile, 0, 0, 32, 32, tx * 32, ty * 32); image_copy(sub_tile, temp_tile, 0, 0, 32, 32, tx * 32, ty * 32);
mem_temp_free(sub_tile); mem_temp_free(sub_tile);
} }
} }
render_texture_create(temp_tile->width, temp_tile->height, temp_tile->pixels); render_texture_create(temp_tile->width, temp_tile->height, temp_tile->pixels);
g.track.textures.len++; g.track.textures.len++;
} }
mem_temp_free(temp_tile); mem_temp_free(temp_tile);
mem_temp_free(cmp); mem_temp_free(cmp);
mem_temp_free(ttf); mem_temp_free(ttf);
vec3_t *vertices = track_load_vertices(get_path(base_path, "track.trv")); vec3_t *vertices = track_load_vertices(get_path(base_path, "track.trv"));
track_load_faces(get_path(base_path, "track.trf"), vertices); track_load_faces(get_path(base_path, "track.trf"), vertices);
mem_temp_free(vertices); mem_temp_free(vertices);
track_load_sections(get_path(base_path, "track.trs")); track_load_sections(get_path(base_path, "track.trs"));
g.track.pickups_len = 0; g.track.pickups_len = 0;
section_t *s = g.track.sections; section_t *s = g.track.sections;
section_t *j = NULL; section_t *j = NULL;
// Nummerate all sections; take care to give both stretches at a junction // Nummerate all sections; take care to give both stretches at a junction
// the same numbers. // the same numbers.
int num = 0; int num = 0;
do { do {
s->num = num++; s->num = num++;
if (s->junction) { // start junction if (s->junction) { // start junction
j = s->junction; j = s->junction;
do { do {
j->num = num++; j->num = num++;
j = j->next; j = j->next;
} while (!j->junction); // end junction } while (!j->junction); // end junction
num = s->num; num = s->num;
} }
s = s->next; s = s->next;
} while (s != g.track.sections); } while (s != g.track.sections);
g.track.total_section_nums = num; g.track.total_section_nums = num;
g.track.pickups = mem_mark(); g.track.pickups = mem_mark();
for (int i = 0; i < g.track.section_count; i++) { for (int i = 0; i < g.track.section_count; i++) {
track_face_t *face = track_section_get_base_face(&g.track.sections[i]); track_face_t *face = track_section_get_base_face(&g.track.sections[i]);
for (int f = 0; f < 2; f++) { for (int f = 0; f < 2; f++) {
if (flags_any(face->flags, FACE_PICKUP_RIGHT | FACE_PICKUP_LEFT)) { if (flags_any(face->flags, FACE_PICKUP_RIGHT | FACE_PICKUP_LEFT)) {
mem_bump(sizeof(track_pickup_t)); mem_bump(sizeof(track_pickup_t));
g.track.pickups[g.track.pickups_len].face = face; g.track.pickups[g.track.pickups_len].face = face;
g.track.pickups[g.track.pickups_len].cooldown_timer = 0; g.track.pickups[g.track.pickups_len].cooldown_timer = 0;
g.track.pickups_len++; g.track.pickups_len++;
} }
if (flags_is(face->flags, FACE_BOOST)) { if (flags_is(face->flags, FACE_BOOST)) {
track_face_set_color(face, rgba(0, 0, 255, 255)); track_face_set_color(face, rgba(0, 0, 255, 255));
} }
face++; face++;
} }
} }
} }
ttf_t *track_load_tile_format(char *ttf_name) { ttf_t *track_load_tile_format(char *ttf_name) {
uint32_t ttf_size; uint32_t ttf_size;
uint8_t *ttf_bytes = platform_load_asset(ttf_name, &ttf_size); uint8_t *ttf_bytes = platform_load_asset(ttf_name, &ttf_size);
uint32_t p = 0; uint32_t p = 0;
uint32_t num_tiles = ttf_size / 42; uint32_t num_tiles = ttf_size / 42;
ttf_t *ttf = mem_temp_alloc(sizeof(ttf_t) + sizeof(ttf_tile_t) * num_tiles); ttf_t *ttf = mem_temp_alloc(sizeof(ttf_t) + sizeof(ttf_tile_t) * num_tiles);
ttf->len = num_tiles; ttf->len = num_tiles;
for (int t = 0; t < num_tiles; t++) { for (int t = 0; t < num_tiles; t++) {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
ttf->tiles[t].near[i] = get_i16(ttf_bytes, &p); ttf->tiles[t].near[i] = get_i16(ttf_bytes, &p);
} }
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
ttf->tiles[t].med[i] = get_i16(ttf_bytes, &p); ttf->tiles[t].med[i] = get_i16(ttf_bytes, &p);
} }
ttf->tiles[t].far = get_i16(ttf_bytes, &p); ttf->tiles[t].far = get_i16(ttf_bytes, &p);
} }
mem_temp_free(ttf_bytes); mem_temp_free(ttf_bytes);
return ttf; return ttf;
} }
bool track_collect_pickups(track_face_t *face) { bool track_collect_pickups(track_face_t *face) {
if (flags_is(face->flags, FACE_PICKUP_ACTIVE)) { if (flags_is(face->flags, FACE_PICKUP_ACTIVE)) {
flags_rm(face->flags, FACE_PICKUP_ACTIVE); flags_rm(face->flags, FACE_PICKUP_ACTIVE);
flags_add(face->flags, FACE_PICKUP_COLLECTED); flags_add(face->flags, FACE_PICKUP_COLLECTED);
track_face_set_color(face, rgba(255, 255, 255, 255)); track_face_set_color(face, rgba(255, 255, 255, 255));
return true; return true;
} }
else { else {
return false; return false;
} }
} }
vec3_t *track_load_vertices(char *file_name) { vec3_t *track_load_vertices(char *file_name) {
uint32_t size; uint32_t size;
uint8_t *bytes = platform_load_asset(file_name, &size); uint8_t *bytes = platform_load_asset(file_name, &size);
g.track.vertex_count = size / 16; // VECTOR_SIZE g.track.vertex_count = size / 16; // VECTOR_SIZE
vec3_t *vertices = mem_temp_alloc(sizeof(vec3_t) * g.track.vertex_count); vec3_t *vertices = mem_temp_alloc(sizeof(vec3_t) * g.track.vertex_count);
uint32_t p = 0; uint32_t p = 0;
for (int i = 0; i < g.track.vertex_count; i++) { for (int i = 0; i < g.track.vertex_count; i++) {
vertices[i].x = get_i32(bytes, &p); vertices[i].x = get_i32(bytes, &p);
vertices[i].y = get_i32(bytes, &p); vertices[i].y = get_i32(bytes, &p);
vertices[i].z = get_i32(bytes, &p); vertices[i].z = get_i32(bytes, &p);
p += 4; // padding p += 4; // padding
} }
mem_temp_free(bytes); mem_temp_free(bytes);
return vertices; return vertices;
} }
static const vec2_t track_uv[2][4] = { static const vec2_t track_uv[2][4] = {
{{128, 0}, { 0, 0}, { 0, 128}, {128, 128}}, {{128, 0}, { 0, 0}, { 0, 128}, {128, 128}},
{{ 0, 0}, {128, 0}, {128, 128}, { 0, 128}} {{ 0, 0}, {128, 0}, {128, 128}, { 0, 128}}
}; };
void track_load_faces(char *file_name, vec3_t *vertices) { void track_load_faces(char *file_name, vec3_t *vertices) {
uint32_t size; uint32_t size;
uint8_t *bytes = platform_load_asset(file_name, &size); uint8_t *bytes = platform_load_asset(file_name, &size);
g.track.face_count = size / 20; // TRACK_FACE_DATA_SIZE g.track.face_count = size / 20; // TRACK_FACE_DATA_SIZE
g.track.faces = mem_bump(sizeof(track_face_t) * g.track.face_count); g.track.faces = mem_bump(sizeof(track_face_t) * g.track.face_count);
uint32_t p = 0; uint32_t p = 0;
track_face_t *tf = g.track.faces; track_face_t *tf = g.track.faces;
for (int i = 0; i < g.track.face_count; i++) { for (int i = 0; i < g.track.face_count; i++) {
vec3_t v0 = vertices[get_i16(bytes, &p)]; vec3_t v0 = vertices[get_i16(bytes, &p)];
vec3_t v1 = vertices[get_i16(bytes, &p)]; vec3_t v1 = vertices[get_i16(bytes, &p)];
vec3_t v2 = vertices[get_i16(bytes, &p)]; vec3_t v2 = vertices[get_i16(bytes, &p)];
vec3_t v3 = vertices[get_i16(bytes, &p)]; vec3_t v3 = vertices[get_i16(bytes, &p)];
tf->normal.x = (float)get_i16(bytes, &p) / 4096.0; tf->normal.x = (float)get_i16(bytes, &p) / 4096.0;
tf->normal.y = (float)get_i16(bytes, &p) / 4096.0; tf->normal.y = (float)get_i16(bytes, &p) / 4096.0;
tf->normal.z = (float)get_i16(bytes, &p) / 4096.0; tf->normal.z = (float)get_i16(bytes, &p) / 4096.0;
tf->texture = get_i8(bytes, &p); tf->texture = get_i8(bytes, &p);
tf->flags = get_i8(bytes, &p); tf->flags = get_i8(bytes, &p);
rgba_t color = rgba_from_u32(get_u32(bytes, &p)); rgba_t color = rgba_from_u32(get_u32(bytes, &p));
const vec2_t *uv = track_uv[flags_is(tf->flags, FACE_FLIP_TEXTURE) ? 1 : 0]; const vec2_t *uv = track_uv[flags_is(tf->flags, FACE_FLIP_TEXTURE) ? 1 : 0];
tf->tris[0] = (tris_t){ tf->tris[0] = (tris_t){
.vertices = { .vertices = {
{.pos = v0, .uv = uv[0], .color = color}, {.pos = v0, .uv = uv[0], .color = color},
{.pos = v1, .uv = uv[1], .color = color}, {.pos = v1, .uv = uv[1], .color = color},
{.pos = v2, .uv = uv[2], .color = color}, {.pos = v2, .uv = uv[2], .color = color},
} }
}; };
tf->tris[1] = (tris_t){ tf->tris[1] = (tris_t){
.vertices = { .vertices = {
{.pos = v3, .uv = uv[3], .color = color}, {.pos = v3, .uv = uv[3], .color = color},
{.pos = v0, .uv = uv[0], .color = color}, {.pos = v0, .uv = uv[0], .color = color},
{.pos = v2, .uv = uv[2], .color = color}, {.pos = v2, .uv = uv[2], .color = color},
} }
}; };
tf++; tf++;
} }
mem_temp_free(bytes); mem_temp_free(bytes);
} }
void track_load_sections(char *file_name) { void track_load_sections(char *file_name) {
uint32_t size; uint32_t size;
uint8_t *bytes = platform_load_asset(file_name, &size); uint8_t *bytes = platform_load_asset(file_name, &size);
g.track.section_count = size / 156; // SECTION_DATA_SIZE g.track.section_count = size / 156; // SECTION_DATA_SIZE
g.track.sections = mem_bump(sizeof(section_t) * g.track.section_count); g.track.sections = mem_bump(sizeof(section_t) * g.track.section_count);
uint32_t p = 0; uint32_t p = 0;
section_t *ts = g.track.sections; section_t *ts = g.track.sections;
for (int i = 0; i < g.track.section_count; i++) { for (int i = 0; i < g.track.section_count; i++) {
int32_t junction_index = get_i32(bytes, &p); int32_t junction_index = get_i32(bytes, &p);
if (junction_index != -1) { if (junction_index != -1) {
ts->junction = g.track.sections + junction_index; ts->junction = g.track.sections + junction_index;
} }
else { else {
ts->junction = NULL; ts->junction = NULL;
} }
ts->prev = g.track.sections + get_i32(bytes, &p); ts->prev = g.track.sections + get_i32(bytes, &p);
ts->next = g.track.sections + get_i32(bytes, &p); ts->next = g.track.sections + get_i32(bytes, &p);
ts->center.x = get_i32(bytes, &p); ts->center.x = get_i32(bytes, &p);
ts->center.y = get_i32(bytes, &p); ts->center.y = get_i32(bytes, &p);
ts->center.z = get_i32(bytes, &p); ts->center.z = get_i32(bytes, &p);
int16_t version = get_i16(bytes, &p); int16_t version = get_i16(bytes, &p);
error_if(version != TRACK_VERSION, "Convert track with track10: section: %d Track: %d\n", version, TRACK_VERSION); error_if(version != TRACK_VERSION, "Convert track with track10: section: %d Track: %d\n", version, TRACK_VERSION);
p += 2; // padding p += 2; // padding
p += 4 + 4; // objects pointer, objectCount p += 4 + 4; // objects pointer, objectCount
p += 5 * 3 * 4; // view section pointers p += 5 * 3 * 4; // view section pointers
p += 5 * 3 * 2; // view section counts p += 5 * 3 * 2; // view section counts
p += 4 * 2; // high list p += 4 * 2; // high list
p += 4 * 2; // med list p += 4 * 2; // med list
ts->face_start = get_i16(bytes, &p); ts->face_start = get_i16(bytes, &p);
ts->face_count = get_i16(bytes, &p); ts->face_count = get_i16(bytes, &p);
p += 2 * 2; // global/local radius p += 2 * 2; // global/local radius
ts->flags = get_i16(bytes, &p); ts->flags = get_i16(bytes, &p);
ts->num = get_i16(bytes, &p); ts->num = get_i16(bytes, &p);
p += 2; // padding p += 2; // padding
ts++; ts++;
} }
mem_temp_free(bytes); mem_temp_free(bytes);
} }
void track_draw_section(section_t *section) { void track_draw_section(section_t *section) {
track_face_t *face = g.track.faces + section->face_start; track_face_t *face = g.track.faces + section->face_start;
int16_t face_count = section->face_count; int16_t face_count = section->face_count;
for (uint32_t j = 0; j < face_count; j++) { for (uint32_t j = 0; j < face_count; j++) {
uint16_t tex_index = texture_from_list(g.track.textures, face->texture); uint16_t tex_index = texture_from_list(g.track.textures, face->texture);
render_push_tris(face->tris[0], tex_index); render_push_tris(face->tris[0], tex_index);
render_push_tris(face->tris[1], tex_index); render_push_tris(face->tris[1], tex_index);
face++; face++;
} }
} }
void track_draw(camera_t *camera) { void track_draw(camera_t *camera) {
render_set_model_mat(&mat4_identity()); render_set_model_mat(&mat4_identity());
// Calculate the camera forward vector, so we can cull everything that's // Calculate the camera forward vector, so we can cull everything that's
// behind. Ideally we'd want to do a full frustum culling here. FIXME. // behind. Ideally we'd want to do a full frustum culling here. FIXME.
vec3_t cam_pos = camera->position; vec3_t cam_pos = camera->position;
vec3_t cam_dir = camera_forward(camera); vec3_t cam_dir = camera_forward(camera);
int drawn = 0; int drawn = 0;
int skipped = 0; int skipped = 0;
for(int32_t i = 0; i < g.track.section_count; i++) { for(int32_t i = 0; i < g.track.section_count; i++) {
section_t *s = &g.track.sections[i]; section_t *s = &g.track.sections[i];
vec3_t diff = vec3_sub(cam_pos, s->center); vec3_t diff = vec3_sub(cam_pos, s->center);
float cam_dot = vec3_dot(diff, cam_dir); float cam_dot = vec3_dot(diff, cam_dir);
float dist_sq = vec3_dot(diff, diff); float dist_sq = vec3_dot(diff, diff);
if ( if (
cam_dot < 2048 && // FIXME: should use the bounding radius of the section cam_dot < 2048 && // FIXME: should use the bounding radius of the section
dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR) dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR)
) { ) {
track_draw_section(s); track_draw_section(s);
} }
} }
} }
void track_cycle_pickups(void) { void track_cycle_pickups(void) {
float pickup_cycle_time = 1.5 * system_cycle_time(); float pickup_cycle_time = 1.5 * system_cycle_time();
for (int i = 0; i < g.track.pickups_len; i++) { for (int i = 0; i < g.track.pickups_len; i++) {
if (flags_is(g.track.pickups[i].face->flags, FACE_PICKUP_COLLECTED)) { if (flags_is(g.track.pickups[i].face->flags, FACE_PICKUP_COLLECTED)) {
flags_rm(g.track.pickups[i].face->flags, FACE_PICKUP_COLLECTED); flags_rm(g.track.pickups[i].face->flags, FACE_PICKUP_COLLECTED);
g.track.pickups[i].cooldown_timer = TRACK_PICKUP_COOLDOWN_TIME; g.track.pickups[i].cooldown_timer = TRACK_PICKUP_COOLDOWN_TIME;
} }
else if (g.track.pickups[i].cooldown_timer <= 0) { else if (g.track.pickups[i].cooldown_timer <= 0) {
flags_add(g.track.pickups[i].face->flags, FACE_PICKUP_ACTIVE); flags_add(g.track.pickups[i].face->flags, FACE_PICKUP_ACTIVE);
track_face_set_color(g.track.pickups[i].face, rgba( track_face_set_color(g.track.pickups[i].face, rgba(
sin( pickup_cycle_time + i) * 127 + 128, sin( pickup_cycle_time + i) * 127 + 128,
cos( pickup_cycle_time + i) * 127 + 128, cos( pickup_cycle_time + i) * 127 + 128,
sin(-pickup_cycle_time - i) * 127 + 128, sin(-pickup_cycle_time - i) * 127 + 128,
255 255
)); ));
} }
else{ else{
g.track.pickups[i].cooldown_timer -= system_tick(); g.track.pickups[i].cooldown_timer -= system_tick();
} }
} }
} }
void track_face_set_color(track_face_t *face, rgba_t color) { void track_face_set_color(track_face_t *face, rgba_t color) {
face->tris[0].vertices[0].color = color; face->tris[0].vertices[0].color = color;
face->tris[0].vertices[1].color = color; face->tris[0].vertices[1].color = color;
face->tris[0].vertices[2].color = color; face->tris[0].vertices[2].color = color;
face->tris[1].vertices[0].color = color; face->tris[1].vertices[0].color = color;
face->tris[1].vertices[1].color = color; face->tris[1].vertices[1].color = color;
face->tris[1].vertices[2].color = color; face->tris[1].vertices[2].color = color;
} }
track_face_t *track_section_get_base_face(section_t *section) { track_face_t *track_section_get_base_face(section_t *section) {
track_face_t *face = g.track.faces +section->face_start; track_face_t *face = g.track.faces +section->face_start;
while(flags_not(face->flags, FACE_TRACK_BASE)) { while(flags_not(face->flags, FACE_TRACK_BASE)) {
face++; face++;
} }
return face; return face;
} }
section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance) { section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance) {
// Start search several sections before current section // Start search several sections before current section
for (int i = 0; i < TRACK_SEARCH_LOOK_BACK; i++) { for (int i = 0; i < TRACK_SEARCH_LOOK_BACK; i++) {
section = section->prev; section = section->prev;
} }
// Find vector from ship center to track section under // Find vector from ship center to track section under
// consideration // consideration
float shortest_distance = 1000000000.0; float shortest_distance = 1000000000.0;
section_t *nearest_section = section; section_t *nearest_section = section;
section_t *junction = NULL; section_t *junction = NULL;
for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) { for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) {
if (section->junction) { if (section->junction) {
junction = section->junction; junction = section->junction;
} }
float d = vec3_len(vec3_sub(pos, section->center)); float d = vec3_len(vec3_sub(pos, section->center));
if (d < shortest_distance) { if (d < shortest_distance) {
shortest_distance = d; shortest_distance = d;
nearest_section = section; nearest_section = section;
} }
section = section->next; section = section->next;
} }
if (junction) { if (junction) {
section = junction; section = junction;
for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) { for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) {
float d = vec3_len(vec3_sub(pos, section->center)); float d = vec3_len(vec3_sub(pos, section->center));
if (d < shortest_distance) { if (d < shortest_distance) {
shortest_distance = d; shortest_distance = d;
nearest_section = section; nearest_section = section;
} }
if (flags_is(junction->flags, SECTION_JUNCTION_START)) { if (flags_is(junction->flags, SECTION_JUNCTION_START)) {
section = section->next; section = section->next;
} }
else { else {
section = section->prev; section = section->prev;
} }
} }
} }
if (distance != NULL) { if (distance != NULL) {
*distance = shortest_distance; *distance = shortest_distance;
} }
return nearest_section; return nearest_section;
} }

View file

@ -1,96 +1,96 @@
#ifndef TRACK_H #ifndef TRACK_H
#define TRACK_H #define TRACK_H
#include "../types.h" #include "../types.h"
#include "object.h" #include "object.h"
#include "image.h" #include "image.h"
#define TRACK_VERSION 8 #define TRACK_VERSION 8
#define TRACK_PICKUP_COOLDOWN_TIME 1 #define TRACK_PICKUP_COOLDOWN_TIME 1
#define TRACK_SEARCH_LOOK_BACK 3 #define TRACK_SEARCH_LOOK_BACK 3
#define TRACK_SEARCH_LOOK_AHEAD 6 #define TRACK_SEARCH_LOOK_AHEAD 6
typedef struct track_face_t { typedef struct track_face_t {
tris_t tris[2]; tris_t tris[2];
vec3_t normal; vec3_t normal;
uint8_t flags; uint8_t flags;
uint8_t texture; uint8_t texture;
} track_face_t; } track_face_t;
#define FACE_TRACK_BASE (1<<0) #define FACE_TRACK_BASE (1<<0)
#define FACE_PICKUP_LEFT (1<<1) #define FACE_PICKUP_LEFT (1<<1)
#define FACE_FLIP_TEXTURE (1<<2) #define FACE_FLIP_TEXTURE (1<<2)
#define FACE_PICKUP_RIGHT (1<<3) #define FACE_PICKUP_RIGHT (1<<3)
#define FACE_START_GRID (1<<4) #define FACE_START_GRID (1<<4)
#define FACE_BOOST (1<<5) #define FACE_BOOST (1<<5)
#define FACE_PICKUP_COLLECTED (1<<6) #define FACE_PICKUP_COLLECTED (1<<6)
#define FACE_PICKUP_ACTIVE (1<<7) #define FACE_PICKUP_ACTIVE (1<<7)
typedef struct { typedef struct {
uint16_t near[16]; uint16_t near[16];
uint16_t med[4]; uint16_t med[4];
uint16_t far; uint16_t far;
} ttf_tile_t; } ttf_tile_t;
typedef struct { typedef struct {
uint32_t len; uint32_t len;
ttf_tile_t tiles[]; ttf_tile_t tiles[];
} ttf_t; } ttf_t;
typedef struct section_t { typedef struct section_t {
struct section_t *junction; struct section_t *junction;
struct section_t *prev; struct section_t *prev;
struct section_t *next; struct section_t *next;
vec3_t center; vec3_t center;
int16_t face_start; int16_t face_start;
int16_t face_count; int16_t face_count;
int16_t flags; int16_t flags;
int16_t num; int16_t num;
} section_t; } section_t;
#define SECTION_JUMP 1 #define SECTION_JUMP 1
#define SECTION_JUNCTION_END 8 #define SECTION_JUNCTION_END 8
#define SECTION_JUNCTION_START 16 #define SECTION_JUNCTION_START 16
#define SECTION_JUNCTION 32 #define SECTION_JUNCTION 32
typedef struct { typedef struct {
track_face_t *face; track_face_t *face;
float cooldown_timer; float cooldown_timer;
} track_pickup_t; } track_pickup_t;
typedef struct track_t { typedef struct track_t {
int32_t vertex_count; int32_t vertex_count;
int32_t face_count; int32_t face_count;
int32_t section_count; int32_t section_count;
int32_t pickups_len; int32_t pickups_len;
int32_t total_section_nums; int32_t total_section_nums;
texture_list_t textures; texture_list_t textures;
track_face_t *faces; track_face_t *faces;
section_t *sections; section_t *sections;
track_pickup_t *pickups; track_pickup_t *pickups;
} track_t; } track_t;
void track_load(const char *base_path); void track_load(const char *base_path);
ttf_t *track_load_tile_format(char *ttf_name); ttf_t *track_load_tile_format(char *ttf_name);
vec3_t *track_load_vertices(char *file); vec3_t *track_load_vertices(char *file);
void track_load_faces(char *file, vec3_t *vertices); void track_load_faces(char *file, vec3_t *vertices);
void track_load_sections(char *file); void track_load_sections(char *file);
bool track_collect_pickups(track_face_t *face); bool track_collect_pickups(track_face_t *face);
void track_face_set_color(track_face_t *face, rgba_t color); void track_face_set_color(track_face_t *face, rgba_t color);
track_face_t *track_section_get_base_face(section_t *section); track_face_t *track_section_get_base_face(section_t *section);
section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance); section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance);
struct camera_t; struct camera_t;
void track_draw(struct camera_t *camera); void track_draw(struct camera_t *camera);
void track_cycle_pickups(void); void track_cycle_pickups(void);
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -1,51 +1,51 @@
#ifndef WEAPON_H #ifndef WEAPON_H
#define WEAPON_H #define WEAPON_H
#define WEAPONS_MAX 64 #define WEAPONS_MAX 64
#define WEAPON_MINE_DURATION (450 * (1.0/30.0)) #define WEAPON_MINE_DURATION (450 * (1.0/30.0))
#define WEAPON_ROCKET_DURATION (200 * (1.0/30.0)) #define WEAPON_ROCKET_DURATION (200 * (1.0/30.0))
#define WEAPON_EBOLT_DURATION (140 * (1.0/30.0)) #define WEAPON_EBOLT_DURATION (140 * (1.0/30.0))
#define WEAPON_REV_CON_DURATION (60 * (1.0/30.0)) #define WEAPON_REV_CON_DURATION (60 * (1.0/30.0))
#define WEAPON_MISSILE_DURATION (200 * (1.0/30.0)) #define WEAPON_MISSILE_DURATION (200 * (1.0/30.0))
#define WEAPON_SHIELD_DURATION (200 * (1.0/30.0)) #define WEAPON_SHIELD_DURATION (200 * (1.0/30.0))
#define WEAPON_FLARE_DURATION (200 * (1.0/30.0)) #define WEAPON_FLARE_DURATION (200 * (1.0/30.0))
#define WEAPON_SPECIAL_DURATION (400 * (1.0/30.0)) #define WEAPON_SPECIAL_DURATION (400 * (1.0/30.0))
#define WEAPON_MINE_RELEASE_RATE (3 * (1.0/30.0)) #define WEAPON_MINE_RELEASE_RATE (3 * (1.0/30.0))
#define WEAPON_DELAY (40 * (1.0/30.0)) #define WEAPON_DELAY (40 * (1.0/30.0))
#define WEAPON_TYPE_NONE 0 #define WEAPON_TYPE_NONE 0
#define WEAPON_TYPE_MINE 1 #define WEAPON_TYPE_MINE 1
#define WEAPON_TYPE_MISSILE 2 #define WEAPON_TYPE_MISSILE 2
#define WEAPON_TYPE_ROCKET 3 #define WEAPON_TYPE_ROCKET 3
#define WEAPON_TYPE_SPECIAL 4 #define WEAPON_TYPE_SPECIAL 4
#define WEAPON_TYPE_EBOLT 5 #define WEAPON_TYPE_EBOLT 5
#define WEAPON_TYPE_FLARE 6 #define WEAPON_TYPE_FLARE 6
#define WEAPON_TYPE_REV_CON 7 #define WEAPON_TYPE_REV_CON 7
#define WEAPON_TYPE_SHIELD 8 #define WEAPON_TYPE_SHIELD 8
#define WEAPON_TYPE_TURBO 9 #define WEAPON_TYPE_TURBO 9
#define WEAPON_TYPE_MAX 10 #define WEAPON_TYPE_MAX 10
#define WEAPON_MINE_COUNT 5 #define WEAPON_MINE_COUNT 5
#define WEAPON_HIT_NONE 0 #define WEAPON_HIT_NONE 0
#define WEAPON_HIT_SHIP 1 #define WEAPON_HIT_SHIP 1
#define WEAPON_HIT_TRACK 2 #define WEAPON_HIT_TRACK 2
#define WEAPON_PARTICLE_SPAWN_RATE 0.011 #define WEAPON_PARTICLE_SPAWN_RATE 0.011
#define WEAPON_AI_DELAY 1.1 #define WEAPON_AI_DELAY 1.1
#define WEAPON_CLASS_ANY 1 #define WEAPON_CLASS_ANY 1
#define WEAPON_CLASS_PROJECTILE 2 #define WEAPON_CLASS_PROJECTILE 2
void weapons_load(void); void weapons_load(void);
void weapons_init(void); void weapons_init(void);
void weapons_fire(ship_t *ship, int weapon_type); void weapons_fire(ship_t *ship, int weapon_type);
void weapons_fire_delayed(ship_t *ship, int weapon_type); void weapons_fire_delayed(ship_t *ship, int weapon_type);
void weapons_update(void); void weapons_update(void);
void weapons_draw(void); void weapons_draw(void);
int weapon_get_random_type(int type_class); int weapon_get_random_type(int type_class);
#endif #endif