mirror of
https://github.com/phoboslab/wipeout-rewrite
synced 2024-12-26 09:59:04 +01:00
Whitespace: change line endings to LF everywhere
This commit is contained in:
parent
a829166082
commit
0594285013
26 changed files with 6344 additions and 6344 deletions
|
@ -1,174 +1,174 @@
|
|||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../types.h"
|
||||
#include "../render.h"
|
||||
#include "../system.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
|
||||
void camera_init(camera_t *camera, section_t *section) {
|
||||
camera->section = section;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
camera->section = camera->section->next;
|
||||
}
|
||||
|
||||
camera->position = camera->section->center;
|
||||
camera->velocity = vec3(0, 0, 0);
|
||||
camera->angle = vec3(0, 0, 0);
|
||||
camera->angular_velocity = vec3(0, 0, 0);
|
||||
camera->has_initial_section = false;
|
||||
}
|
||||
|
||||
vec3_t camera_forward(camera_t *camera) {
|
||||
float sx = sin(camera->angle.x);
|
||||
float cx = cos(camera->angle.x);
|
||||
float sy = sin(camera->angle.y);
|
||||
float cy = cos(camera->angle.y);
|
||||
return vec3(-(sy * cx), -sx, (cy * cx));
|
||||
}
|
||||
|
||||
void camera_update(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->last_position = camera->position;
|
||||
(camera->update_func)(camera, ship, droid);
|
||||
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) {
|
||||
vec3_t pos = vec3_sub(ship->position, vec3_mulf(ship->dir_forward, 1024));
|
||||
pos.y -= 200;
|
||||
camera->section = track_nearest_section(pos, camera->section, NULL);
|
||||
section_t *next = camera->section->next;
|
||||
|
||||
vec3_t target = vec3_project_to_ray(pos, next->center, camera->section->center);
|
||||
|
||||
vec3_t diff_from_center = vec3_sub(pos, target);
|
||||
vec3_t acc = diff_from_center;
|
||||
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(camera->velocity, 0.125 * 30 * system_tick()));
|
||||
pos = vec3_add(pos, camera->velocity);
|
||||
|
||||
camera->position = pos;
|
||||
camera->angle = vec3(ship->angle.x, ship->angle.y, 0);
|
||||
}
|
||||
|
||||
void camera_update_race_internal(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->section = ship->section;
|
||||
camera->position = ship_cockpit(ship);
|
||||
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) {
|
||||
// Set to final position
|
||||
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.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;
|
||||
|
||||
if (!camera->has_initial_section) {
|
||||
camera->section = ship->section;
|
||||
camera->has_initial_section = true;
|
||||
}
|
||||
else {
|
||||
camera->section = track_nearest_section(pos, camera->section, NULL);
|
||||
}
|
||||
|
||||
camera->position = pos;
|
||||
camera->angle.z = 0;
|
||||
camera->angle.x = ship->angle.x * 0.5;
|
||||
vec3_t target = vec3_sub(ship->position, pos);
|
||||
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
|
||||
if (ship->update_timer <= UPDATE_TIME_RACE_VIEW) {
|
||||
flags_add(ship->flags, SHIP_VIEW_INTERNAL);
|
||||
camera->update_func = camera_update_race_internal;
|
||||
}
|
||||
}
|
||||
|
||||
void camera_update_attract_circle(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->update_timer -= system_tick();
|
||||
if (camera->update_timer <= 0) {
|
||||
camera->update_func = camera_update_attract_random;
|
||||
}
|
||||
// FIXME: not exactly sure what I'm doing here. The PSX version behaves
|
||||
// differently.
|
||||
camera->section = ship->section;
|
||||
|
||||
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.z = ship->position.z - cos(ship->angle.y) * 512;
|
||||
|
||||
camera->position.x += sin(camera->update_timer * 0.25) * 512;
|
||||
camera->position.y -= 400;
|
||||
camera->position.z += cos(camera->update_timer * 0.25) * 512;
|
||||
camera->position = vec3_sub(camera->position, vec3_mulf(ship->dir_up, 256));
|
||||
|
||||
vec3_t target = vec3_sub(ship->position, camera->position);
|
||||
float height = sqrt(target.x * target.x + target.z * target.z);
|
||||
camera->angle.x = -atan2(target.y, height);
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
}
|
||||
|
||||
void camera_update_rescue(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->position = vec3_add(camera->section->center, vec3(300, -1500, 300));
|
||||
|
||||
vec3_t target = vec3_sub(droid->position, camera->position);
|
||||
float height = sqrt(target.x * target.x + target.z * target.z);
|
||||
camera->angle.x = -atan2(target.y, height);
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
}
|
||||
|
||||
|
||||
void camera_update_attract_internal(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->update_timer -= system_tick();
|
||||
if (camera->update_timer <= 0) {
|
||||
camera->update_func = camera_update_attract_random;
|
||||
}
|
||||
|
||||
camera->section = ship->section;
|
||||
camera->position = ship_cockpit(ship);
|
||||
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) {
|
||||
camera->update_timer -= system_tick();
|
||||
if (camera->update_timer <= 0) {
|
||||
camera->update_func = camera_update_attract_random;
|
||||
}
|
||||
|
||||
vec3_t target = vec3_sub(ship->position, camera->position);
|
||||
float height = sqrt(target.x * target.x + target.z * target.z);
|
||||
camera->angle.x = -atan2(target.y, height);
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
}
|
||||
|
||||
void camera_update_attract_random(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
flags_rm(ship->flags, SHIP_VIEW_INTERNAL);
|
||||
|
||||
if (rand() % 2) {
|
||||
camera->update_func = camera_update_attract_circle;
|
||||
camera->update_timer = 5;
|
||||
}
|
||||
else {
|
||||
camera->update_func = camera_update_static_follow;
|
||||
camera->update_timer = 5;
|
||||
section_t *section = ship->section->next;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
section = section->next;
|
||||
}
|
||||
|
||||
camera->section = section;
|
||||
camera->position = section->center;
|
||||
camera->position.y -= 500;
|
||||
}
|
||||
|
||||
(camera->update_func)(camera, ship, droid);
|
||||
}
|
||||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../types.h"
|
||||
#include "../render.h"
|
||||
#include "../system.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
|
||||
void camera_init(camera_t *camera, section_t *section) {
|
||||
camera->section = section;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
camera->section = camera->section->next;
|
||||
}
|
||||
|
||||
camera->position = camera->section->center;
|
||||
camera->velocity = vec3(0, 0, 0);
|
||||
camera->angle = vec3(0, 0, 0);
|
||||
camera->angular_velocity = vec3(0, 0, 0);
|
||||
camera->has_initial_section = false;
|
||||
}
|
||||
|
||||
vec3_t camera_forward(camera_t *camera) {
|
||||
float sx = sin(camera->angle.x);
|
||||
float cx = cos(camera->angle.x);
|
||||
float sy = sin(camera->angle.y);
|
||||
float cy = cos(camera->angle.y);
|
||||
return vec3(-(sy * cx), -sx, (cy * cx));
|
||||
}
|
||||
|
||||
void camera_update(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->last_position = camera->position;
|
||||
(camera->update_func)(camera, ship, droid);
|
||||
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) {
|
||||
vec3_t pos = vec3_sub(ship->position, vec3_mulf(ship->dir_forward, 1024));
|
||||
pos.y -= 200;
|
||||
camera->section = track_nearest_section(pos, camera->section, NULL);
|
||||
section_t *next = camera->section->next;
|
||||
|
||||
vec3_t target = vec3_project_to_ray(pos, next->center, camera->section->center);
|
||||
|
||||
vec3_t diff_from_center = vec3_sub(pos, target);
|
||||
vec3_t acc = diff_from_center;
|
||||
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(camera->velocity, 0.125 * 30 * system_tick()));
|
||||
pos = vec3_add(pos, camera->velocity);
|
||||
|
||||
camera->position = pos;
|
||||
camera->angle = vec3(ship->angle.x, ship->angle.y, 0);
|
||||
}
|
||||
|
||||
void camera_update_race_internal(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->section = ship->section;
|
||||
camera->position = ship_cockpit(ship);
|
||||
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) {
|
||||
// Set to final position
|
||||
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.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;
|
||||
|
||||
if (!camera->has_initial_section) {
|
||||
camera->section = ship->section;
|
||||
camera->has_initial_section = true;
|
||||
}
|
||||
else {
|
||||
camera->section = track_nearest_section(pos, camera->section, NULL);
|
||||
}
|
||||
|
||||
camera->position = pos;
|
||||
camera->angle.z = 0;
|
||||
camera->angle.x = ship->angle.x * 0.5;
|
||||
vec3_t target = vec3_sub(ship->position, pos);
|
||||
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
|
||||
if (ship->update_timer <= UPDATE_TIME_RACE_VIEW) {
|
||||
flags_add(ship->flags, SHIP_VIEW_INTERNAL);
|
||||
camera->update_func = camera_update_race_internal;
|
||||
}
|
||||
}
|
||||
|
||||
void camera_update_attract_circle(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->update_timer -= system_tick();
|
||||
if (camera->update_timer <= 0) {
|
||||
camera->update_func = camera_update_attract_random;
|
||||
}
|
||||
// FIXME: not exactly sure what I'm doing here. The PSX version behaves
|
||||
// differently.
|
||||
camera->section = ship->section;
|
||||
|
||||
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.z = ship->position.z - cos(ship->angle.y) * 512;
|
||||
|
||||
camera->position.x += sin(camera->update_timer * 0.25) * 512;
|
||||
camera->position.y -= 400;
|
||||
camera->position.z += cos(camera->update_timer * 0.25) * 512;
|
||||
camera->position = vec3_sub(camera->position, vec3_mulf(ship->dir_up, 256));
|
||||
|
||||
vec3_t target = vec3_sub(ship->position, camera->position);
|
||||
float height = sqrt(target.x * target.x + target.z * target.z);
|
||||
camera->angle.x = -atan2(target.y, height);
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
}
|
||||
|
||||
void camera_update_rescue(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->position = vec3_add(camera->section->center, vec3(300, -1500, 300));
|
||||
|
||||
vec3_t target = vec3_sub(droid->position, camera->position);
|
||||
float height = sqrt(target.x * target.x + target.z * target.z);
|
||||
camera->angle.x = -atan2(target.y, height);
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
}
|
||||
|
||||
|
||||
void camera_update_attract_internal(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
camera->update_timer -= system_tick();
|
||||
if (camera->update_timer <= 0) {
|
||||
camera->update_func = camera_update_attract_random;
|
||||
}
|
||||
|
||||
camera->section = ship->section;
|
||||
camera->position = ship_cockpit(ship);
|
||||
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) {
|
||||
camera->update_timer -= system_tick();
|
||||
if (camera->update_timer <= 0) {
|
||||
camera->update_func = camera_update_attract_random;
|
||||
}
|
||||
|
||||
vec3_t target = vec3_sub(ship->position, camera->position);
|
||||
float height = sqrt(target.x * target.x + target.z * target.z);
|
||||
camera->angle.x = -atan2(target.y, height);
|
||||
camera->angle.y = -atan2(target.x, target.z);
|
||||
}
|
||||
|
||||
void camera_update_attract_random(camera_t *camera, ship_t *ship, droid_t *droid) {
|
||||
flags_rm(ship->flags, SHIP_VIEW_INTERNAL);
|
||||
|
||||
if (rand() % 2) {
|
||||
camera->update_func = camera_update_attract_circle;
|
||||
camera->update_timer = 5;
|
||||
}
|
||||
else {
|
||||
camera->update_func = camera_update_static_follow;
|
||||
camera->update_timer = 5;
|
||||
section_t *section = ship->section->next;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
section = section->next;
|
||||
}
|
||||
|
||||
camera->section = section;
|
||||
camera->position = section->center;
|
||||
camera->position.y -= 500;
|
||||
}
|
||||
|
||||
(camera->update_func)(camera, ship, droid);
|
||||
}
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "droid.h"
|
||||
|
||||
typedef struct camera_t {
|
||||
vec3_t position;
|
||||
vec3_t velocity;
|
||||
vec3_t angle;
|
||||
vec3_t angular_velocity;
|
||||
vec3_t last_position;
|
||||
vec3_t real_velocity;
|
||||
section_t *section;
|
||||
bool has_initial_section;
|
||||
float update_timer;
|
||||
void (*update_func)(struct camera_t *, ship_t *, droid_t *);
|
||||
} camera_t;
|
||||
|
||||
void camera_init(camera_t *camera, section_t *section);
|
||||
vec3_t camera_forward(camera_t *camera);
|
||||
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_internal(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_internal(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_rescue(camera_t *, ship_t *camShip, droid_t *);
|
||||
|
||||
#endif
|
||||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "droid.h"
|
||||
|
||||
typedef struct camera_t {
|
||||
vec3_t position;
|
||||
vec3_t velocity;
|
||||
vec3_t angle;
|
||||
vec3_t angular_velocity;
|
||||
vec3_t last_position;
|
||||
vec3_t real_velocity;
|
||||
section_t *section;
|
||||
bool has_initial_section;
|
||||
float update_timer;
|
||||
void (*update_func)(struct camera_t *, ship_t *, droid_t *);
|
||||
} camera_t;
|
||||
|
||||
void camera_init(camera_t *camera, section_t *section);
|
||||
vec3_t camera_forward(camera_t *camera);
|
||||
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_internal(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_internal(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_rescue(camera_t *, ship_t *camShip, droid_t *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,260 +1,260 @@
|
|||
#include "../types.h"
|
||||
#include "../mem.h"
|
||||
#include "../system.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "hud.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "image.h"
|
||||
#include "scene.h"
|
||||
#include "object.h"
|
||||
#include "game.h"
|
||||
|
||||
static Object *droid_model;
|
||||
|
||||
void droid_load(void) {
|
||||
texture_list_t droid_textures = image_get_compressed_textures("wipeout/common/rescu.cmp");
|
||||
droid_model = objects_load("wipeout/common/rescu.prm", droid_textures);
|
||||
}
|
||||
|
||||
void droid_init(droid_t *droid, ship_t *ship) {
|
||||
droid->section = g.track.sections;
|
||||
|
||||
while (flags_not(droid->section->flags, SECTION_JUMP)) {
|
||||
droid->section = droid->section->next;
|
||||
}
|
||||
|
||||
droid->position = vec3_add(ship->position, vec3(0, -200, 0));
|
||||
droid->velocity = vec3(0, 0, 0);
|
||||
droid->acceleration = vec3(0, 0, 0);
|
||||
droid->angle = vec3(0, 0, 0);
|
||||
droid->angular_velocity = vec3(0, 0, 0);
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
droid->mat = mat4_identity();
|
||||
|
||||
droid->cycle_timer = 0;
|
||||
droid->update_func = droid_update_intro;
|
||||
|
||||
droid->sfx_tractor = sfx_reserve_loop(SFX_TRACTOR);
|
||||
flags_rm(droid->sfx_tractor->flags, SFX_PLAY);
|
||||
}
|
||||
|
||||
void droid_draw(droid_t *droid) {
|
||||
droid->cycle_timer += system_tick() * M_PI * 2;
|
||||
|
||||
Prm prm = {.primitive = droid_model->primitives};
|
||||
int rf = sin(droid->cycle_timer) * 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 r, g, b;
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
if (i < 2) {
|
||||
r = 40;
|
||||
g = gf;
|
||||
b = 40;
|
||||
}
|
||||
else if (i < 6) {
|
||||
r = bf >> 1;
|
||||
b = bf;
|
||||
g = bf >> 1;
|
||||
}
|
||||
else {
|
||||
r = rf;
|
||||
b = 40;
|
||||
g = 40;
|
||||
}
|
||||
|
||||
switch (prm.f3->type) {
|
||||
case PRM_TYPE_GT3:
|
||||
prm.gt3->color[0].r = r;
|
||||
prm.gt3->color[0].g = g;
|
||||
prm.gt3->color[0].b = b;
|
||||
|
||||
prm.gt3->color[1].r = r;
|
||||
prm.gt3->color[1].g = g;
|
||||
prm.gt3->color[1].b = b;
|
||||
|
||||
prm.gt3->color[2].r = r;
|
||||
prm.gt3->color[2].g = g;
|
||||
prm.gt3->color[2].b = b;
|
||||
prm.gt3++;
|
||||
break;
|
||||
|
||||
case PRM_TYPE_GT4:
|
||||
prm.gt4->color[0].r = r;
|
||||
prm.gt4->color[0].g = g;
|
||||
prm.gt4->color[0].b = b;
|
||||
|
||||
prm.gt4->color[1].r = r;
|
||||
prm.gt4->color[1].g = g;
|
||||
prm.gt4->color[1].b = b;
|
||||
|
||||
prm.gt4->color[2].r = r;
|
||||
prm.gt4->color[2].g = g;
|
||||
prm.gt4->color[2].b = b;
|
||||
|
||||
prm.gt4->color[3].r = 40;
|
||||
prm.gt4->color[3].g = 40;
|
||||
prm.gt4->color[3].b = 40;
|
||||
prm.gt4++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mat4_set_translation(&droid->mat, droid->position);
|
||||
mat4_set_yaw_pitch_roll(&droid->mat, droid->angle);
|
||||
object_draw(droid_model, &droid->mat);
|
||||
}
|
||||
|
||||
void droid_update(droid_t *droid, ship_t *ship) {
|
||||
(droid->update_func)(droid, ship);
|
||||
|
||||
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->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_wrap_angle(droid->angle);
|
||||
|
||||
if (flags_is(droid->sfx_tractor->flags, SFX_PLAY)) {
|
||||
sfx_set_position(droid->sfx_tractor, droid->position, droid->velocity, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
void droid_update_intro(droid_t *droid, ship_t *ship) {
|
||||
droid->update_timer -= system_tick();
|
||||
|
||||
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.y = 0;
|
||||
droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.25 * 4096.0;
|
||||
droid->angular_velocity.y = 0;
|
||||
}
|
||||
|
||||
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.y = -140;
|
||||
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;
|
||||
}
|
||||
|
||||
else if (droid->update_timer < DROID_UPDATE_TIME_INTRO_1) {
|
||||
droid->acceleration.y -= 90 * system_tick();
|
||||
droid->angular_velocity.y = (8.0 / 4096.0) * M_PI * 2 * 30;
|
||||
}
|
||||
|
||||
if (droid->update_timer <= 0) {
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
droid->update_func = droid_update_idle;
|
||||
droid->position.x = droid->section->center.x;
|
||||
droid->position.y = -3000;
|
||||
droid->position.z = droid->section->center.z;
|
||||
}
|
||||
}
|
||||
|
||||
void droid_update_idle(droid_t *droid, ship_t *ship) {
|
||||
section_t *next = droid->section->next;
|
||||
|
||||
vec3_t target = vec3(
|
||||
(droid->section->center.x + next->center.x) * 0.5,
|
||||
droid->section->center.y - 3000,
|
||||
(droid->section->center.z + next->center.z) * 0.5
|
||||
);
|
||||
|
||||
vec3_t target_vector = vec3_sub(target, droid->position);
|
||||
|
||||
float target_heading = -atan2(target_vector.x, target_vector.z);
|
||||
float quickest_turn = target_heading - droid->angle.y;
|
||||
float turn;
|
||||
if (droid->angle.y < 0) {
|
||||
turn = target_heading - (droid->angle.y + M_PI*2);
|
||||
}
|
||||
else {
|
||||
turn = target_heading - (droid->angle.y - M_PI*2);
|
||||
}
|
||||
|
||||
if (fabsf(turn) < fabsf(quickest_turn)) {
|
||||
droid->angular_velocity.y = turn * 30 / 64.0;
|
||||
}
|
||||
else {
|
||||
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.y = target_vector.y / 64.0;
|
||||
droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096;
|
||||
|
||||
if (flags_is(ship->flags, SHIP_IN_RESCUE)) {
|
||||
flags_add(droid->sfx_tractor->flags, SFX_PLAY);
|
||||
|
||||
droid->update_func = droid_update_rescue;
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
|
||||
g.camera.update_func = camera_update_rescue;
|
||||
flags_add(ship->flags, SHIP_VIEW_REMOTE);
|
||||
if (flags_is(ship->section->flags, SECTION_JUMP)) {
|
||||
g.camera.section = ship->section->next;
|
||||
}
|
||||
else {
|
||||
g.camera.section = ship->section;
|
||||
}
|
||||
|
||||
// If droid is not nearby the rescue position teleport it in!
|
||||
if (droid->section != ship->section && droid->section != ship->section->prev) {
|
||||
droid->section = ship->section;
|
||||
section_t *next = droid->section->next;
|
||||
|
||||
droid->position.x = (droid->section->center.x + next->center.x) * 0.5;
|
||||
droid->position.y = droid->section->center.y - 3000;
|
||||
droid->position.z = (droid->section->center.z + next->center.z) * 0.5;
|
||||
}
|
||||
flags_rm(ship->flags, SHIP_IN_TOW);
|
||||
droid->velocity = 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});
|
||||
}
|
||||
|
||||
void droid_update_rescue(droid_t *droid, ship_t *ship) {
|
||||
droid->angular_velocity.y = 0;
|
||||
droid->angle.y = ship->angle.y;
|
||||
|
||||
vec3_t target = vec3(ship->position.x, ship->position.y - 350, ship->position.z);
|
||||
vec3_t distance = vec3_sub(target, droid->position);
|
||||
|
||||
|
||||
if (flags_is(ship->flags, SHIP_IN_TOW)) {
|
||||
droid->velocity = vec3(0,0,0);
|
||||
droid->acceleration = vec3(0,0,0);
|
||||
droid->position = target;
|
||||
}
|
||||
else if (vec3_len(distance) < 8) {
|
||||
flags_add(ship->flags, SHIP_IN_TOW);
|
||||
droid->velocity = vec3(0,0,0);
|
||||
droid->acceleration = vec3(0,0,0);
|
||||
droid->position = target;
|
||||
}
|
||||
else {
|
||||
droid->velocity = vec3_mulf(distance, 16);
|
||||
}
|
||||
|
||||
|
||||
// Are we done rescuing?
|
||||
if (flags_not(ship->flags, SHIP_IN_RESCUE)) {
|
||||
flags_rm(droid->sfx_tractor->flags, SFX_PLAY);
|
||||
droid->siren_started = false;
|
||||
droid->update_func = droid_update_idle;
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
|
||||
while (flags_not(droid->section->flags, SECTION_JUMP)) {
|
||||
droid->section = droid->section->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "../types.h"
|
||||
#include "../mem.h"
|
||||
#include "../system.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "hud.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "image.h"
|
||||
#include "scene.h"
|
||||
#include "object.h"
|
||||
#include "game.h"
|
||||
|
||||
static Object *droid_model;
|
||||
|
||||
void droid_load(void) {
|
||||
texture_list_t droid_textures = image_get_compressed_textures("wipeout/common/rescu.cmp");
|
||||
droid_model = objects_load("wipeout/common/rescu.prm", droid_textures);
|
||||
}
|
||||
|
||||
void droid_init(droid_t *droid, ship_t *ship) {
|
||||
droid->section = g.track.sections;
|
||||
|
||||
while (flags_not(droid->section->flags, SECTION_JUMP)) {
|
||||
droid->section = droid->section->next;
|
||||
}
|
||||
|
||||
droid->position = vec3_add(ship->position, vec3(0, -200, 0));
|
||||
droid->velocity = vec3(0, 0, 0);
|
||||
droid->acceleration = vec3(0, 0, 0);
|
||||
droid->angle = vec3(0, 0, 0);
|
||||
droid->angular_velocity = vec3(0, 0, 0);
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
droid->mat = mat4_identity();
|
||||
|
||||
droid->cycle_timer = 0;
|
||||
droid->update_func = droid_update_intro;
|
||||
|
||||
droid->sfx_tractor = sfx_reserve_loop(SFX_TRACTOR);
|
||||
flags_rm(droid->sfx_tractor->flags, SFX_PLAY);
|
||||
}
|
||||
|
||||
void droid_draw(droid_t *droid) {
|
||||
droid->cycle_timer += system_tick() * M_PI * 2;
|
||||
|
||||
Prm prm = {.primitive = droid_model->primitives};
|
||||
int rf = sin(droid->cycle_timer) * 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 r, g, b;
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
if (i < 2) {
|
||||
r = 40;
|
||||
g = gf;
|
||||
b = 40;
|
||||
}
|
||||
else if (i < 6) {
|
||||
r = bf >> 1;
|
||||
b = bf;
|
||||
g = bf >> 1;
|
||||
}
|
||||
else {
|
||||
r = rf;
|
||||
b = 40;
|
||||
g = 40;
|
||||
}
|
||||
|
||||
switch (prm.f3->type) {
|
||||
case PRM_TYPE_GT3:
|
||||
prm.gt3->color[0].r = r;
|
||||
prm.gt3->color[0].g = g;
|
||||
prm.gt3->color[0].b = b;
|
||||
|
||||
prm.gt3->color[1].r = r;
|
||||
prm.gt3->color[1].g = g;
|
||||
prm.gt3->color[1].b = b;
|
||||
|
||||
prm.gt3->color[2].r = r;
|
||||
prm.gt3->color[2].g = g;
|
||||
prm.gt3->color[2].b = b;
|
||||
prm.gt3++;
|
||||
break;
|
||||
|
||||
case PRM_TYPE_GT4:
|
||||
prm.gt4->color[0].r = r;
|
||||
prm.gt4->color[0].g = g;
|
||||
prm.gt4->color[0].b = b;
|
||||
|
||||
prm.gt4->color[1].r = r;
|
||||
prm.gt4->color[1].g = g;
|
||||
prm.gt4->color[1].b = b;
|
||||
|
||||
prm.gt4->color[2].r = r;
|
||||
prm.gt4->color[2].g = g;
|
||||
prm.gt4->color[2].b = b;
|
||||
|
||||
prm.gt4->color[3].r = 40;
|
||||
prm.gt4->color[3].g = 40;
|
||||
prm.gt4->color[3].b = 40;
|
||||
prm.gt4++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mat4_set_translation(&droid->mat, droid->position);
|
||||
mat4_set_yaw_pitch_roll(&droid->mat, droid->angle);
|
||||
object_draw(droid_model, &droid->mat);
|
||||
}
|
||||
|
||||
void droid_update(droid_t *droid, ship_t *ship) {
|
||||
(droid->update_func)(droid, ship);
|
||||
|
||||
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->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_wrap_angle(droid->angle);
|
||||
|
||||
if (flags_is(droid->sfx_tractor->flags, SFX_PLAY)) {
|
||||
sfx_set_position(droid->sfx_tractor, droid->position, droid->velocity, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
void droid_update_intro(droid_t *droid, ship_t *ship) {
|
||||
droid->update_timer -= system_tick();
|
||||
|
||||
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.y = 0;
|
||||
droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.25 * 4096.0;
|
||||
droid->angular_velocity.y = 0;
|
||||
}
|
||||
|
||||
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.y = -140;
|
||||
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;
|
||||
}
|
||||
|
||||
else if (droid->update_timer < DROID_UPDATE_TIME_INTRO_1) {
|
||||
droid->acceleration.y -= 90 * system_tick();
|
||||
droid->angular_velocity.y = (8.0 / 4096.0) * M_PI * 2 * 30;
|
||||
}
|
||||
|
||||
if (droid->update_timer <= 0) {
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
droid->update_func = droid_update_idle;
|
||||
droid->position.x = droid->section->center.x;
|
||||
droid->position.y = -3000;
|
||||
droid->position.z = droid->section->center.z;
|
||||
}
|
||||
}
|
||||
|
||||
void droid_update_idle(droid_t *droid, ship_t *ship) {
|
||||
section_t *next = droid->section->next;
|
||||
|
||||
vec3_t target = vec3(
|
||||
(droid->section->center.x + next->center.x) * 0.5,
|
||||
droid->section->center.y - 3000,
|
||||
(droid->section->center.z + next->center.z) * 0.5
|
||||
);
|
||||
|
||||
vec3_t target_vector = vec3_sub(target, droid->position);
|
||||
|
||||
float target_heading = -atan2(target_vector.x, target_vector.z);
|
||||
float quickest_turn = target_heading - droid->angle.y;
|
||||
float turn;
|
||||
if (droid->angle.y < 0) {
|
||||
turn = target_heading - (droid->angle.y + M_PI*2);
|
||||
}
|
||||
else {
|
||||
turn = target_heading - (droid->angle.y - M_PI*2);
|
||||
}
|
||||
|
||||
if (fabsf(turn) < fabsf(quickest_turn)) {
|
||||
droid->angular_velocity.y = turn * 30 / 64.0;
|
||||
}
|
||||
else {
|
||||
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.y = target_vector.y / 64.0;
|
||||
droid->acceleration.z = (cos(droid->angle.y) * cos(droid->angle.x)) * 0.125 * 4096;
|
||||
|
||||
if (flags_is(ship->flags, SHIP_IN_RESCUE)) {
|
||||
flags_add(droid->sfx_tractor->flags, SFX_PLAY);
|
||||
|
||||
droid->update_func = droid_update_rescue;
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
|
||||
g.camera.update_func = camera_update_rescue;
|
||||
flags_add(ship->flags, SHIP_VIEW_REMOTE);
|
||||
if (flags_is(ship->section->flags, SECTION_JUMP)) {
|
||||
g.camera.section = ship->section->next;
|
||||
}
|
||||
else {
|
||||
g.camera.section = ship->section;
|
||||
}
|
||||
|
||||
// If droid is not nearby the rescue position teleport it in!
|
||||
if (droid->section != ship->section && droid->section != ship->section->prev) {
|
||||
droid->section = ship->section;
|
||||
section_t *next = droid->section->next;
|
||||
|
||||
droid->position.x = (droid->section->center.x + next->center.x) * 0.5;
|
||||
droid->position.y = droid->section->center.y - 3000;
|
||||
droid->position.z = (droid->section->center.z + next->center.z) * 0.5;
|
||||
}
|
||||
flags_rm(ship->flags, SHIP_IN_TOW);
|
||||
droid->velocity = 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});
|
||||
}
|
||||
|
||||
void droid_update_rescue(droid_t *droid, ship_t *ship) {
|
||||
droid->angular_velocity.y = 0;
|
||||
droid->angle.y = ship->angle.y;
|
||||
|
||||
vec3_t target = vec3(ship->position.x, ship->position.y - 350, ship->position.z);
|
||||
vec3_t distance = vec3_sub(target, droid->position);
|
||||
|
||||
|
||||
if (flags_is(ship->flags, SHIP_IN_TOW)) {
|
||||
droid->velocity = vec3(0,0,0);
|
||||
droid->acceleration = vec3(0,0,0);
|
||||
droid->position = target;
|
||||
}
|
||||
else if (vec3_len(distance) < 8) {
|
||||
flags_add(ship->flags, SHIP_IN_TOW);
|
||||
droid->velocity = vec3(0,0,0);
|
||||
droid->acceleration = vec3(0,0,0);
|
||||
droid->position = target;
|
||||
}
|
||||
else {
|
||||
droid->velocity = vec3_mulf(distance, 16);
|
||||
}
|
||||
|
||||
|
||||
// Are we done rescuing?
|
||||
if (flags_not(ship->flags, SHIP_IN_RESCUE)) {
|
||||
flags_rm(droid->sfx_tractor->flags, SFX_PLAY);
|
||||
droid->siren_started = false;
|
||||
droid->update_func = droid_update_idle;
|
||||
droid->update_timer = DROID_UPDATE_TIME_INITIAL;
|
||||
|
||||
while (flags_not(droid->section->flags, SECTION_JUMP)) {
|
||||
droid->section = droid->section->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
#ifndef DROID_H
|
||||
#define DROID_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "sfx.h"
|
||||
|
||||
#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_2 (710 * (1.0/30.0))
|
||||
#define DROID_UPDATE_TIME_INTRO_3 (400 * (1.0/30.0))
|
||||
|
||||
typedef struct droid_t {
|
||||
section_t *section;
|
||||
vec3_t position;
|
||||
vec3_t velocity;
|
||||
vec3_t acceleration;
|
||||
vec3_t angle;
|
||||
vec3_t angular_velocity;
|
||||
bool siren_started;
|
||||
float cycle_timer;
|
||||
float update_timer;
|
||||
void (*update_func)(struct droid_t *, ship_t *);
|
||||
mat4_t mat;
|
||||
Object *model;
|
||||
sfx_t *sfx_tractor;
|
||||
} droid_t;
|
||||
|
||||
void droid_draw(droid_t *droid);
|
||||
|
||||
void droid_load(void);
|
||||
void droid_init(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_idle(droid_t *droid, ship_t *ship);
|
||||
void droid_update_rescue(droid_t *droid, ship_t *ship);
|
||||
|
||||
#endif
|
||||
#ifndef DROID_H
|
||||
#define DROID_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "sfx.h"
|
||||
|
||||
#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_2 (710 * (1.0/30.0))
|
||||
#define DROID_UPDATE_TIME_INTRO_3 (400 * (1.0/30.0))
|
||||
|
||||
typedef struct droid_t {
|
||||
section_t *section;
|
||||
vec3_t position;
|
||||
vec3_t velocity;
|
||||
vec3_t acceleration;
|
||||
vec3_t angle;
|
||||
vec3_t angular_velocity;
|
||||
bool siren_started;
|
||||
float cycle_timer;
|
||||
float update_timer;
|
||||
void (*update_func)(struct droid_t *, ship_t *);
|
||||
mat4_t mat;
|
||||
Object *model;
|
||||
sfx_t *sfx_tractor;
|
||||
} droid_t;
|
||||
|
||||
void droid_draw(droid_t *droid);
|
||||
|
||||
void droid_load(void);
|
||||
void droid_init(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_idle(droid_t *droid, ship_t *ship);
|
||||
void droid_update_rescue(droid_t *droid, ship_t *ship);
|
||||
|
||||
#endif
|
||||
|
|
1294
src/wipeout/game.c
1294
src/wipeout/game.c
File diff suppressed because it is too large
Load diff
|
@ -1,270 +1,270 @@
|
|||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
#include "droid.h"
|
||||
#include "ship.h"
|
||||
#include "camera.h"
|
||||
#include "track.h"
|
||||
|
||||
#define NUM_AI_OPPONENTS 7
|
||||
#define NUM_PILOTS_PER_TEAM 2
|
||||
#define NUM_NON_BONUS_CIRCUTS 6
|
||||
#define NUM_MUSIC_TRACKS 11
|
||||
#define NUM_HIGHSCORES 5
|
||||
|
||||
#define NUM_LAPS 3
|
||||
#define NUM_LIVES 3
|
||||
#define QUALIFYING_RANK 3
|
||||
#define SAVE_DATA_MAGIC 0x64736f77
|
||||
|
||||
typedef enum {
|
||||
A_UP,
|
||||
A_DOWN,
|
||||
A_LEFT,
|
||||
A_RIGHT,
|
||||
A_BRAKE_LEFT,
|
||||
A_BRAKE_RIGHT,
|
||||
A_THRUST,
|
||||
A_FIRE,
|
||||
A_CHANGE_VIEW,
|
||||
NUM_GAME_ACTIONS,
|
||||
|
||||
A_MENU_UP,
|
||||
A_MENU_DOWN,
|
||||
A_MENU_LEFT,
|
||||
A_MENU_RIGHT,
|
||||
A_MENU_BACK,
|
||||
A_MENU_SELECT,
|
||||
A_MENU_START,
|
||||
A_MENU_QUIT,
|
||||
} action_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
GAME_SCENE_INTRO,
|
||||
GAME_SCENE_TITLE,
|
||||
GAME_SCENE_MAIN_MENU,
|
||||
GAME_SCENE_HIGHSCORES,
|
||||
GAME_SCENE_RACE,
|
||||
GAME_SCENE_NONE,
|
||||
NUM_GAME_SCENES
|
||||
} game_scene_t;
|
||||
|
||||
enum race_class {
|
||||
RACE_CLASS_VENOM,
|
||||
RACE_CLASS_RAPIER,
|
||||
NUM_RACE_CLASSES
|
||||
};
|
||||
|
||||
enum race_type {
|
||||
RACE_TYPE_CHAMPIONSHIP,
|
||||
RACE_TYPE_SINGLE,
|
||||
RACE_TYPE_TIME_TRIAL,
|
||||
NUM_RACE_TYPES,
|
||||
};
|
||||
|
||||
enum highscore_tab {
|
||||
HIGHSCORE_TAB_TIME_TRIAL,
|
||||
HIGHSCORE_TAB_RACE,
|
||||
NUM_HIGHSCORE_TABS
|
||||
};
|
||||
|
||||
enum pilot {
|
||||
PILOT_JOHN_DEKKA,
|
||||
PILOT_DANIEL_CHANG,
|
||||
PILOT_ARIAL_TETSUO,
|
||||
PILOT_ANASTASIA_CHEROVOSKI,
|
||||
PILOT_KEL_SOLAAR,
|
||||
PILOT_ARIAN_TETSUO,
|
||||
PILOT_SOFIA_DE_LA_RENTE,
|
||||
PILOT_PAUL_JACKSON,
|
||||
NUM_PILOTS
|
||||
};
|
||||
|
||||
enum team {
|
||||
TEAM_AG_SYSTEMS,
|
||||
TEAM_AURICOM,
|
||||
TEAM_QIREX,
|
||||
TEAM_FEISAR,
|
||||
NUM_TEAMS
|
||||
};
|
||||
|
||||
enum circut {
|
||||
CIRCUT_ALTIMA_VII,
|
||||
CIRCUT_KARBONIS_V,
|
||||
CIRCUT_TERRAMAX,
|
||||
CIRCUT_KORODERA,
|
||||
CIRCUT_ARRIDOS_IV,
|
||||
CIRCUT_SILVERSTREAM,
|
||||
CIRCUT_FIRESTAR,
|
||||
NUM_CIRCUTS
|
||||
};
|
||||
|
||||
|
||||
// Game definitions
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
} race_class_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
} race_type_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *portrait;
|
||||
int logo_model;
|
||||
int team;
|
||||
} pilot_t;
|
||||
|
||||
typedef struct {
|
||||
float thrust_max;
|
||||
float thrust_magnitude;
|
||||
bool fight_back;
|
||||
} ai_setting_t;
|
||||
|
||||
typedef struct {
|
||||
float mass;
|
||||
float thrust_max;
|
||||
float resistance;
|
||||
float turn_rate;
|
||||
float turn_rate_max;
|
||||
float skid;
|
||||
} team_attributes_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int logo_model;
|
||||
int pilots[NUM_PILOTS_PER_TEAM];
|
||||
team_attributes_t attributes[NUM_RACE_CLASSES];
|
||||
} team_t;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
float start_line_pos;
|
||||
float behind_speed;
|
||||
float spread_base;
|
||||
float spread_factor;
|
||||
float sky_y_offset;
|
||||
} circut_settings_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
bool is_bonus_circut;
|
||||
circut_settings_t settings[NUM_RACE_CLASSES];
|
||||
} circut_t;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
char *name;
|
||||
} music_track_t;
|
||||
|
||||
typedef struct {
|
||||
race_class_t race_classes[NUM_RACE_CLASSES];
|
||||
race_type_t race_types[NUM_RACE_TYPES];
|
||||
pilot_t pilots[NUM_PILOTS];
|
||||
team_t teams[NUM_TEAMS];
|
||||
ai_setting_t ai_settings[NUM_RACE_CLASSES][NUM_AI_OPPONENTS];
|
||||
circut_t circuts[NUM_CIRCUTS];
|
||||
int ship_model_to_pilot[NUM_PILOTS];
|
||||
int race_points_for_rank[NUM_PILOTS];
|
||||
music_track_t music[NUM_MUSIC_TRACKS];
|
||||
char *credits[104];
|
||||
struct {
|
||||
char *venom[15];
|
||||
char *venom_all_circuts[19];
|
||||
char *rapier[26];
|
||||
char *rapier_all_circuts[24];
|
||||
} congratulations;
|
||||
} game_def_t;
|
||||
|
||||
|
||||
|
||||
// Running game data
|
||||
|
||||
typedef struct {
|
||||
uint16_t pilot;
|
||||
uint16_t points;
|
||||
} pilot_points_t;
|
||||
|
||||
typedef struct {
|
||||
float frame_time;
|
||||
float frame_rate;
|
||||
|
||||
int race_class;
|
||||
int race_type;
|
||||
int highscore_tab;
|
||||
int team;
|
||||
int pilot;
|
||||
int circut;
|
||||
bool is_attract_mode;
|
||||
bool show_credits;
|
||||
|
||||
bool is_new_lap_record;
|
||||
bool is_new_race_record;
|
||||
float best_lap;
|
||||
float race_time;
|
||||
int lives;
|
||||
int race_position;
|
||||
|
||||
float lap_times[NUM_PILOTS][NUM_LAPS];
|
||||
pilot_points_t race_ranks[NUM_PILOTS];
|
||||
pilot_points_t championship_ranks[NUM_PILOTS];
|
||||
|
||||
camera_t camera;
|
||||
droid_t droid;
|
||||
ship_t ships[NUM_PILOTS];
|
||||
track_t track;
|
||||
} game_t;
|
||||
|
||||
|
||||
|
||||
// Save Data
|
||||
|
||||
typedef struct {
|
||||
char name[4];
|
||||
float time;
|
||||
} highscores_entry_t;
|
||||
|
||||
typedef struct {
|
||||
highscores_entry_t entries[NUM_HIGHSCORES];
|
||||
float lap_record;
|
||||
} highscores_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
bool is_dirty;
|
||||
|
||||
float sfx_volume;
|
||||
float music_volume;
|
||||
uint8_t ui_scale;
|
||||
bool show_fps;
|
||||
bool fullscreen;
|
||||
int screen_res;
|
||||
int post_effect;
|
||||
|
||||
uint32_t has_rapier_class;
|
||||
uint32_t has_bonus_circuts;
|
||||
|
||||
uint8_t buttons[NUM_GAME_ACTIONS][2];
|
||||
|
||||
char highscores_name[4];
|
||||
highscores_t highscores[NUM_RACE_CLASSES][NUM_CIRCUTS][NUM_HIGHSCORE_TABS];
|
||||
} save_t;
|
||||
|
||||
|
||||
|
||||
|
||||
extern const game_def_t def;
|
||||
extern game_t g;
|
||||
extern save_t save;
|
||||
|
||||
void game_init(void);
|
||||
void game_set_scene(game_scene_t scene);
|
||||
void game_reset_championship(void);
|
||||
void game_update(void);
|
||||
|
||||
#endif
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
#include "droid.h"
|
||||
#include "ship.h"
|
||||
#include "camera.h"
|
||||
#include "track.h"
|
||||
|
||||
#define NUM_AI_OPPONENTS 7
|
||||
#define NUM_PILOTS_PER_TEAM 2
|
||||
#define NUM_NON_BONUS_CIRCUTS 6
|
||||
#define NUM_MUSIC_TRACKS 11
|
||||
#define NUM_HIGHSCORES 5
|
||||
|
||||
#define NUM_LAPS 3
|
||||
#define NUM_LIVES 3
|
||||
#define QUALIFYING_RANK 3
|
||||
#define SAVE_DATA_MAGIC 0x64736f77
|
||||
|
||||
typedef enum {
|
||||
A_UP,
|
||||
A_DOWN,
|
||||
A_LEFT,
|
||||
A_RIGHT,
|
||||
A_BRAKE_LEFT,
|
||||
A_BRAKE_RIGHT,
|
||||
A_THRUST,
|
||||
A_FIRE,
|
||||
A_CHANGE_VIEW,
|
||||
NUM_GAME_ACTIONS,
|
||||
|
||||
A_MENU_UP,
|
||||
A_MENU_DOWN,
|
||||
A_MENU_LEFT,
|
||||
A_MENU_RIGHT,
|
||||
A_MENU_BACK,
|
||||
A_MENU_SELECT,
|
||||
A_MENU_START,
|
||||
A_MENU_QUIT,
|
||||
} action_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
GAME_SCENE_INTRO,
|
||||
GAME_SCENE_TITLE,
|
||||
GAME_SCENE_MAIN_MENU,
|
||||
GAME_SCENE_HIGHSCORES,
|
||||
GAME_SCENE_RACE,
|
||||
GAME_SCENE_NONE,
|
||||
NUM_GAME_SCENES
|
||||
} game_scene_t;
|
||||
|
||||
enum race_class {
|
||||
RACE_CLASS_VENOM,
|
||||
RACE_CLASS_RAPIER,
|
||||
NUM_RACE_CLASSES
|
||||
};
|
||||
|
||||
enum race_type {
|
||||
RACE_TYPE_CHAMPIONSHIP,
|
||||
RACE_TYPE_SINGLE,
|
||||
RACE_TYPE_TIME_TRIAL,
|
||||
NUM_RACE_TYPES,
|
||||
};
|
||||
|
||||
enum highscore_tab {
|
||||
HIGHSCORE_TAB_TIME_TRIAL,
|
||||
HIGHSCORE_TAB_RACE,
|
||||
NUM_HIGHSCORE_TABS
|
||||
};
|
||||
|
||||
enum pilot {
|
||||
PILOT_JOHN_DEKKA,
|
||||
PILOT_DANIEL_CHANG,
|
||||
PILOT_ARIAL_TETSUO,
|
||||
PILOT_ANASTASIA_CHEROVOSKI,
|
||||
PILOT_KEL_SOLAAR,
|
||||
PILOT_ARIAN_TETSUO,
|
||||
PILOT_SOFIA_DE_LA_RENTE,
|
||||
PILOT_PAUL_JACKSON,
|
||||
NUM_PILOTS
|
||||
};
|
||||
|
||||
enum team {
|
||||
TEAM_AG_SYSTEMS,
|
||||
TEAM_AURICOM,
|
||||
TEAM_QIREX,
|
||||
TEAM_FEISAR,
|
||||
NUM_TEAMS
|
||||
};
|
||||
|
||||
enum circut {
|
||||
CIRCUT_ALTIMA_VII,
|
||||
CIRCUT_KARBONIS_V,
|
||||
CIRCUT_TERRAMAX,
|
||||
CIRCUT_KORODERA,
|
||||
CIRCUT_ARRIDOS_IV,
|
||||
CIRCUT_SILVERSTREAM,
|
||||
CIRCUT_FIRESTAR,
|
||||
NUM_CIRCUTS
|
||||
};
|
||||
|
||||
|
||||
// Game definitions
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
} race_class_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
} race_type_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *portrait;
|
||||
int logo_model;
|
||||
int team;
|
||||
} pilot_t;
|
||||
|
||||
typedef struct {
|
||||
float thrust_max;
|
||||
float thrust_magnitude;
|
||||
bool fight_back;
|
||||
} ai_setting_t;
|
||||
|
||||
typedef struct {
|
||||
float mass;
|
||||
float thrust_max;
|
||||
float resistance;
|
||||
float turn_rate;
|
||||
float turn_rate_max;
|
||||
float skid;
|
||||
} team_attributes_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int logo_model;
|
||||
int pilots[NUM_PILOTS_PER_TEAM];
|
||||
team_attributes_t attributes[NUM_RACE_CLASSES];
|
||||
} team_t;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
float start_line_pos;
|
||||
float behind_speed;
|
||||
float spread_base;
|
||||
float spread_factor;
|
||||
float sky_y_offset;
|
||||
} circut_settings_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
bool is_bonus_circut;
|
||||
circut_settings_t settings[NUM_RACE_CLASSES];
|
||||
} circut_t;
|
||||
|
||||
typedef struct {
|
||||
char *path;
|
||||
char *name;
|
||||
} music_track_t;
|
||||
|
||||
typedef struct {
|
||||
race_class_t race_classes[NUM_RACE_CLASSES];
|
||||
race_type_t race_types[NUM_RACE_TYPES];
|
||||
pilot_t pilots[NUM_PILOTS];
|
||||
team_t teams[NUM_TEAMS];
|
||||
ai_setting_t ai_settings[NUM_RACE_CLASSES][NUM_AI_OPPONENTS];
|
||||
circut_t circuts[NUM_CIRCUTS];
|
||||
int ship_model_to_pilot[NUM_PILOTS];
|
||||
int race_points_for_rank[NUM_PILOTS];
|
||||
music_track_t music[NUM_MUSIC_TRACKS];
|
||||
char *credits[104];
|
||||
struct {
|
||||
char *venom[15];
|
||||
char *venom_all_circuts[19];
|
||||
char *rapier[26];
|
||||
char *rapier_all_circuts[24];
|
||||
} congratulations;
|
||||
} game_def_t;
|
||||
|
||||
|
||||
|
||||
// Running game data
|
||||
|
||||
typedef struct {
|
||||
uint16_t pilot;
|
||||
uint16_t points;
|
||||
} pilot_points_t;
|
||||
|
||||
typedef struct {
|
||||
float frame_time;
|
||||
float frame_rate;
|
||||
|
||||
int race_class;
|
||||
int race_type;
|
||||
int highscore_tab;
|
||||
int team;
|
||||
int pilot;
|
||||
int circut;
|
||||
bool is_attract_mode;
|
||||
bool show_credits;
|
||||
|
||||
bool is_new_lap_record;
|
||||
bool is_new_race_record;
|
||||
float best_lap;
|
||||
float race_time;
|
||||
int lives;
|
||||
int race_position;
|
||||
|
||||
float lap_times[NUM_PILOTS][NUM_LAPS];
|
||||
pilot_points_t race_ranks[NUM_PILOTS];
|
||||
pilot_points_t championship_ranks[NUM_PILOTS];
|
||||
|
||||
camera_t camera;
|
||||
droid_t droid;
|
||||
ship_t ships[NUM_PILOTS];
|
||||
track_t track;
|
||||
} game_t;
|
||||
|
||||
|
||||
|
||||
// Save Data
|
||||
|
||||
typedef struct {
|
||||
char name[4];
|
||||
float time;
|
||||
} highscores_entry_t;
|
||||
|
||||
typedef struct {
|
||||
highscores_entry_t entries[NUM_HIGHSCORES];
|
||||
float lap_record;
|
||||
} highscores_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
bool is_dirty;
|
||||
|
||||
float sfx_volume;
|
||||
float music_volume;
|
||||
uint8_t ui_scale;
|
||||
bool show_fps;
|
||||
bool fullscreen;
|
||||
int screen_res;
|
||||
int post_effect;
|
||||
|
||||
uint32_t has_rapier_class;
|
||||
uint32_t has_bonus_circuts;
|
||||
|
||||
uint8_t buttons[NUM_GAME_ACTIONS][2];
|
||||
|
||||
char highscores_name[4];
|
||||
highscores_t highscores[NUM_RACE_CLASSES][NUM_CIRCUTS][NUM_HIGHSCORE_TABS];
|
||||
} save_t;
|
||||
|
||||
|
||||
|
||||
|
||||
extern const game_def_t def;
|
||||
extern game_t g;
|
||||
extern save_t save;
|
||||
|
||||
void game_init(void);
|
||||
void game_set_scene(game_scene_t scene);
|
||||
void game_reset_championship(void);
|
||||
void game_update(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,253 +1,253 @@
|
|||
#include "../types.h"
|
||||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../system.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "hud.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "image.h"
|
||||
#include "ship_ai.h"
|
||||
#include "game.h"
|
||||
#include "ui.h"
|
||||
|
||||
static texture_list_t weapon_icon_textures;
|
||||
static uint16_t target_reticle;
|
||||
|
||||
typedef struct {
|
||||
vec2i_t offset;
|
||||
uint16_t height;
|
||||
rgba_t color;
|
||||
} speedo_bar_t;
|
||||
|
||||
const struct {
|
||||
uint16_t width;
|
||||
uint16_t skew;
|
||||
speedo_bar_t bars[13];
|
||||
} speedo = {
|
||||
.width = 121,
|
||||
.skew = 2,
|
||||
.bars = {
|
||||
{{.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 = 20, .y = 12}, .height = 10, .color = rgba(132, 58, 164, 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 = 41, .y = 12}, .height = 10, .color = rgba( 66, 173, 115, 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 = 69, .y = 5}, .height = 13, .color = rgba(247, 140, 33, 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 = 110, .y = 1}, .height = 16, .color = rgba(255, 239, 181, 255)},
|
||||
{{.x = 126, .y = 1}, .height = 16, .color = rgba(255, 255, 255, 255)}
|
||||
}
|
||||
};
|
||||
|
||||
static uint16_t speedo_facia_texture;
|
||||
|
||||
void hud_load(void) {
|
||||
speedo_facia_texture = image_get_texture("wipeout/textures/speedo.tim");
|
||||
target_reticle = image_get_texture_semi_trans("wipeout/textures/target2.tim");
|
||||
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) {
|
||||
rgba_t left_color, right_color;
|
||||
if (color_override.a > 0) {
|
||||
left_color = color_override;
|
||||
right_color = color_override;
|
||||
}
|
||||
else {
|
||||
left_color = a->color;
|
||||
right_color = rgba(
|
||||
lerp(a->color.r, b->color.r, f),
|
||||
lerp(a->color.g, b->color.g, f),
|
||||
lerp(a->color.b, b->color.b, f),
|
||||
lerp(a->color.a, b->color.a, f)
|
||||
);
|
||||
}
|
||||
|
||||
float right_h = lerp(a->height, b->height, f);
|
||||
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 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);
|
||||
|
||||
top_left = ui_scaled(top_left);
|
||||
bottom_left = ui_scaled(bottom_left);
|
||||
top_right = ui_scaled(top_right);
|
||||
bottom_right = ui_scaled(bottom_right);
|
||||
|
||||
render_push_tris((tris_t) {
|
||||
.vertices = {
|
||||
{
|
||||
.pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = left_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + top_right.x, pos->y + top_right.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = right_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + top_left.x, pos->y + top_left.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = left_color
|
||||
},
|
||||
}
|
||||
}, RENDER_NO_TEXTURE);
|
||||
|
||||
render_push_tris((tris_t) {
|
||||
.vertices = {
|
||||
{
|
||||
.pos = {pos->x + bottom_right.x, pos->y + bottom_right.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = right_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + top_right.x, pos->y + top_right.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = right_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = left_color
|
||||
},
|
||||
}
|
||||
}, RENDER_NO_TEXTURE);
|
||||
}
|
||||
|
||||
static void hud_draw_speedo_bars(vec2i_t *pos, float f, rgba_t color_override) {
|
||||
if (f <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (f - floor(f) > 0.9) {
|
||||
f = ceil(f);
|
||||
}
|
||||
if (f > 13) {
|
||||
f = 13;
|
||||
}
|
||||
|
||||
int bars = f;
|
||||
for (int i = 1; i < bars; i++) {
|
||||
hud_draw_speedo_bar(pos, &speedo.bars[i - 1], &speedo.bars[i], 1, color_override);
|
||||
}
|
||||
|
||||
if (bars > 12) {
|
||||
return;
|
||||
}
|
||||
|
||||
float last_bar_fraction = f - bars + 0.1;
|
||||
if (last_bar_fraction <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_bar_fraction > 1) {
|
||||
last_bar_fraction = 1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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 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, 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);
|
||||
}
|
||||
|
||||
static void hud_draw_target_icon(vec3_t position) {
|
||||
vec2i_t screen_size = render_size();
|
||||
vec2i_t size = ui_scaled(render_texture_size(target_reticle));
|
||||
vec3_t projected = render_transform(position);
|
||||
|
||||
// Not on screen?
|
||||
if (
|
||||
projected.x < -1 || projected.x > 1 ||
|
||||
projected.y < -1 || projected.y > 1 ||
|
||||
projected.z >= 1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec2i_t pos = vec2i(
|
||||
(( projected.x + 1.0) / 2.0) * screen_size.x - size.x / 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);
|
||||
}
|
||||
|
||||
void hud_draw(ship_t *ship) {
|
||||
// Current lap time
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Current Lap
|
||||
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_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);
|
||||
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);
|
||||
|
||||
// Race Position
|
||||
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_number(ship->position_rank, ui_scaled_pos(UI_POS_TOP | UI_POS_RIGHT, vec2i(-60, 19)), UI_SIZE_16, UI_COLOR_DEFAULT);
|
||||
}
|
||||
|
||||
// Framerate
|
||||
if (save.show_fps) {
|
||||
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);
|
||||
}
|
||||
|
||||
// Lap Record
|
||||
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);
|
||||
|
||||
// Wrong way
|
||||
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);
|
||||
}
|
||||
|
||||
// Speedo
|
||||
int speedo_speed = (g.camera.update_func == camera_update_attract_internal)
|
||||
? ship->speed * 7
|
||||
: ship->speed;
|
||||
hud_draw_speedo(speedo_speed, ship->thrust_mag);
|
||||
|
||||
// Weapon icon
|
||||
if (ship->weapon_type != WEAPON_TYPE_NONE) {
|
||||
vec2i_t pos = ui_scaled_pos(UI_POS_TOP | UI_POS_CENTER, vec2i(-16, 20));
|
||||
vec2i_t size = ui_scaled(vec2i(32, 32));
|
||||
uint16_t icon = texture_from_list(weapon_icon_textures, ship->weapon_type-1);
|
||||
render_push_2d(pos, size, rgba(128,128,128,255), icon);
|
||||
}
|
||||
|
||||
// Lives
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Weapon target reticle
|
||||
if (ship->weapon_target) {
|
||||
hud_draw_target_icon(ship->weapon_target->position);
|
||||
}
|
||||
}
|
||||
#include "../types.h"
|
||||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../system.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "hud.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "image.h"
|
||||
#include "ship_ai.h"
|
||||
#include "game.h"
|
||||
#include "ui.h"
|
||||
|
||||
static texture_list_t weapon_icon_textures;
|
||||
static uint16_t target_reticle;
|
||||
|
||||
typedef struct {
|
||||
vec2i_t offset;
|
||||
uint16_t height;
|
||||
rgba_t color;
|
||||
} speedo_bar_t;
|
||||
|
||||
const struct {
|
||||
uint16_t width;
|
||||
uint16_t skew;
|
||||
speedo_bar_t bars[13];
|
||||
} speedo = {
|
||||
.width = 121,
|
||||
.skew = 2,
|
||||
.bars = {
|
||||
{{.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 = 20, .y = 12}, .height = 10, .color = rgba(132, 58, 164, 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 = 41, .y = 12}, .height = 10, .color = rgba( 66, 173, 115, 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 = 69, .y = 5}, .height = 13, .color = rgba(247, 140, 33, 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 = 110, .y = 1}, .height = 16, .color = rgba(255, 239, 181, 255)},
|
||||
{{.x = 126, .y = 1}, .height = 16, .color = rgba(255, 255, 255, 255)}
|
||||
}
|
||||
};
|
||||
|
||||
static uint16_t speedo_facia_texture;
|
||||
|
||||
void hud_load(void) {
|
||||
speedo_facia_texture = image_get_texture("wipeout/textures/speedo.tim");
|
||||
target_reticle = image_get_texture_semi_trans("wipeout/textures/target2.tim");
|
||||
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) {
|
||||
rgba_t left_color, right_color;
|
||||
if (color_override.a > 0) {
|
||||
left_color = color_override;
|
||||
right_color = color_override;
|
||||
}
|
||||
else {
|
||||
left_color = a->color;
|
||||
right_color = rgba(
|
||||
lerp(a->color.r, b->color.r, f),
|
||||
lerp(a->color.g, b->color.g, f),
|
||||
lerp(a->color.b, b->color.b, f),
|
||||
lerp(a->color.a, b->color.a, f)
|
||||
);
|
||||
}
|
||||
|
||||
float right_h = lerp(a->height, b->height, f);
|
||||
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 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);
|
||||
|
||||
top_left = ui_scaled(top_left);
|
||||
bottom_left = ui_scaled(bottom_left);
|
||||
top_right = ui_scaled(top_right);
|
||||
bottom_right = ui_scaled(bottom_right);
|
||||
|
||||
render_push_tris((tris_t) {
|
||||
.vertices = {
|
||||
{
|
||||
.pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = left_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + top_right.x, pos->y + top_right.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = right_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + top_left.x, pos->y + top_left.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = left_color
|
||||
},
|
||||
}
|
||||
}, RENDER_NO_TEXTURE);
|
||||
|
||||
render_push_tris((tris_t) {
|
||||
.vertices = {
|
||||
{
|
||||
.pos = {pos->x + bottom_right.x, pos->y + bottom_right.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = right_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + top_right.x, pos->y + top_right.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = right_color
|
||||
},
|
||||
{
|
||||
.pos = {pos->x + bottom_left.x, pos->y + bottom_left.y, 0},
|
||||
.uv = {0, 0},
|
||||
.color = left_color
|
||||
},
|
||||
}
|
||||
}, RENDER_NO_TEXTURE);
|
||||
}
|
||||
|
||||
static void hud_draw_speedo_bars(vec2i_t *pos, float f, rgba_t color_override) {
|
||||
if (f <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (f - floor(f) > 0.9) {
|
||||
f = ceil(f);
|
||||
}
|
||||
if (f > 13) {
|
||||
f = 13;
|
||||
}
|
||||
|
||||
int bars = f;
|
||||
for (int i = 1; i < bars; i++) {
|
||||
hud_draw_speedo_bar(pos, &speedo.bars[i - 1], &speedo.bars[i], 1, color_override);
|
||||
}
|
||||
|
||||
if (bars > 12) {
|
||||
return;
|
||||
}
|
||||
|
||||
float last_bar_fraction = f - bars + 0.1;
|
||||
if (last_bar_fraction <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_bar_fraction > 1) {
|
||||
last_bar_fraction = 1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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 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, 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);
|
||||
}
|
||||
|
||||
static void hud_draw_target_icon(vec3_t position) {
|
||||
vec2i_t screen_size = render_size();
|
||||
vec2i_t size = ui_scaled(render_texture_size(target_reticle));
|
||||
vec3_t projected = render_transform(position);
|
||||
|
||||
// Not on screen?
|
||||
if (
|
||||
projected.x < -1 || projected.x > 1 ||
|
||||
projected.y < -1 || projected.y > 1 ||
|
||||
projected.z >= 1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec2i_t pos = vec2i(
|
||||
(( projected.x + 1.0) / 2.0) * screen_size.x - size.x / 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);
|
||||
}
|
||||
|
||||
void hud_draw(ship_t *ship) {
|
||||
// Current lap time
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Current Lap
|
||||
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_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);
|
||||
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);
|
||||
|
||||
// Race Position
|
||||
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_number(ship->position_rank, ui_scaled_pos(UI_POS_TOP | UI_POS_RIGHT, vec2i(-60, 19)), UI_SIZE_16, UI_COLOR_DEFAULT);
|
||||
}
|
||||
|
||||
// Framerate
|
||||
if (save.show_fps) {
|
||||
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);
|
||||
}
|
||||
|
||||
// Lap Record
|
||||
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);
|
||||
|
||||
// Wrong way
|
||||
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);
|
||||
}
|
||||
|
||||
// Speedo
|
||||
int speedo_speed = (g.camera.update_func == camera_update_attract_internal)
|
||||
? ship->speed * 7
|
||||
: ship->speed;
|
||||
hud_draw_speedo(speedo_speed, ship->thrust_mag);
|
||||
|
||||
// Weapon icon
|
||||
if (ship->weapon_type != WEAPON_TYPE_NONE) {
|
||||
vec2i_t pos = ui_scaled_pos(UI_POS_TOP | UI_POS_CENTER, vec2i(-16, 20));
|
||||
vec2i_t size = ui_scaled(vec2i(32, 32));
|
||||
uint16_t icon = texture_from_list(weapon_icon_textures, ship->weapon_type-1);
|
||||
render_push_2d(pos, size, rgba(128,128,128,255), icon);
|
||||
}
|
||||
|
||||
// Lives
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Weapon target reticle
|
||||
if (ship->weapon_target) {
|
||||
hud_draw_target_icon(ship->weapon_target->position);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#ifndef HUD_H
|
||||
#define HUD_H
|
||||
|
||||
#include "ship.h"
|
||||
|
||||
void hud_load(void);
|
||||
void hud_draw(ship_t *ship);
|
||||
|
||||
#endif
|
||||
#ifndef HUD_H
|
||||
#define HUD_H
|
||||
|
||||
#include "ship.h"
|
||||
|
||||
void hud_load(void);
|
||||
void hud_draw(ship_t *ship);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,322 +1,322 @@
|
|||
#include "../types.h"
|
||||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../platform.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "scene.h"
|
||||
#include "game.h"
|
||||
#include "hud.h"
|
||||
#include "image.h"
|
||||
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb_image_write.h>
|
||||
|
||||
|
||||
#define TIM_TYPE_PALETTED_4_BPP 0x08
|
||||
#define TIM_TYPE_PALETTED_8_BPP 0x09
|
||||
#define TIM_TYPE_TRUE_COLOR_16_BPP 0x02
|
||||
|
||||
static inline rgba_t tim_16bit_to_rgba(uint16_t c, bool transparent_bit) {
|
||||
return rgba(
|
||||
((c >> 0) & 0x1f) << 3,
|
||||
((c >> 5) & 0x1f) << 3,
|
||||
((c >> 10) & 0x1f) << 3,
|
||||
(c == 0
|
||||
? 0x00
|
||||
: transparent_bit && (c & 0x7fff) == 0 ? 0x00 : 0xff
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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->width = width;
|
||||
image->height = height;
|
||||
image->pixels = (rgba_t *)(((uint8_t *)image) + sizeof(image_t));
|
||||
return image;
|
||||
}
|
||||
|
||||
image_t *image_load_from_bytes(uint8_t *bytes, bool transparent) {
|
||||
uint32_t p = 0;
|
||||
|
||||
uint32_t magic = get_i32_le(bytes, &p);
|
||||
uint32_t type = get_i32_le(bytes, &p);
|
||||
rgba_t palette[256];
|
||||
|
||||
if (
|
||||
type == TIM_TYPE_PALETTED_4_BPP ||
|
||||
type == TIM_TYPE_PALETTED_8_BPP
|
||||
) {
|
||||
uint32_t header_length = get_i32_le(bytes, &p);
|
||||
uint16_t palette_x = 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 palettes = get_i16_le(bytes, &p);
|
||||
for (int i = 0; i < palette_colors; i++) {
|
||||
palette[i] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t data_size = get_i32_le(bytes, &p);
|
||||
|
||||
int32_t pixels_per_16bit = 1;
|
||||
if (type == TIM_TYPE_PALETTED_8_BPP) {
|
||||
pixels_per_16bit = 2;
|
||||
}
|
||||
else if (type == TIM_TYPE_PALETTED_4_BPP) {
|
||||
pixels_per_16bit = 4;
|
||||
}
|
||||
|
||||
uint16_t skip_x = 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 rows = get_i16_le(bytes, &p);
|
||||
|
||||
int32_t width = entries_per_row * pixels_per_16bit;
|
||||
int32_t height = rows;
|
||||
int32_t entries = entries_per_row * rows;
|
||||
|
||||
image_t *image = image_alloc(width, height);
|
||||
int32_t pixel_pos = 0;
|
||||
|
||||
if (type == TIM_TYPE_TRUE_COLOR_16_BPP) {
|
||||
for (int i = 0; i < entries; i++) {
|
||||
image->pixels[pixel_pos++] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent);
|
||||
}
|
||||
}
|
||||
else if (type == TIM_TYPE_PALETTED_8_BPP) {
|
||||
for (int i = 0; i < entries; i++) {
|
||||
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 >> 8) & 0xff];
|
||||
}
|
||||
}
|
||||
else if (type == TIM_TYPE_PALETTED_4_BPP) {
|
||||
for (int i = 0; i < entries; i++) {
|
||||
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 >> 4) & 0xf];
|
||||
image->pixels[pixel_pos++] = palette[(palette_pos >> 8) & 0xf];
|
||||
image->pixels[pixel_pos++] = palette[(palette_pos >> 12) & 0xf];
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
#define LZSS_INDEX_BIT_COUNT 13
|
||||
#define LZSS_LENGTH_BIT_COUNT 4
|
||||
#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_END_OF_STREAM 0
|
||||
#define LZSS_MOD_WINDOW(a) ((a) & (LZSS_WINDOW_SIZE - 1))
|
||||
|
||||
void lzss_decompress(uint8_t *in_data, uint8_t *out_data) {
|
||||
int16_t i;
|
||||
int16_t current_position;
|
||||
uint8_t cc;
|
||||
int16_t match_length;
|
||||
int16_t match_position;
|
||||
uint32_t mask;
|
||||
uint32_t return_value;
|
||||
uint8_t in_bfile_mask;
|
||||
int16_t in_bfile_rack;
|
||||
int16_t value;
|
||||
uint8_t window[LZSS_WINDOW_SIZE];
|
||||
|
||||
in_bfile_rack = 0;
|
||||
in_bfile_mask = 0x80;
|
||||
|
||||
current_position = 1;
|
||||
while (true) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
value = in_bfile_rack & in_bfile_mask;
|
||||
in_bfile_mask >>= 1;
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
mask = 1L << (8 - 1);
|
||||
return_value = 0;
|
||||
while (mask != 0) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
if (in_bfile_rack & in_bfile_mask) {
|
||||
return_value |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
in_bfile_mask >>= 1;
|
||||
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
}
|
||||
cc = (uint8_t) return_value;
|
||||
*out_data++ = cc;
|
||||
window[ current_position ] = cc;
|
||||
current_position = LZSS_MOD_WINDOW(current_position + 1);
|
||||
}
|
||||
else {
|
||||
mask = 1L << (LZSS_INDEX_BIT_COUNT - 1);
|
||||
return_value = 0;
|
||||
while (mask != 0) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
if (in_bfile_rack & in_bfile_mask) {
|
||||
return_value |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
in_bfile_mask >>= 1;
|
||||
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
}
|
||||
match_position = (int16_t) return_value;
|
||||
|
||||
if (match_position == LZSS_END_OF_STREAM) {
|
||||
break;
|
||||
}
|
||||
|
||||
mask = 1L << (LZSS_LENGTH_BIT_COUNT - 1);
|
||||
return_value = 0;
|
||||
while (mask != 0) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
if (in_bfile_rack & in_bfile_mask) {
|
||||
return_value |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
in_bfile_mask >>= 1;
|
||||
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
}
|
||||
match_length = (int16_t) return_value;
|
||||
|
||||
match_length += LZSS_BREAK_EVEN;
|
||||
|
||||
for (i = 0 ; i <= match_length ; i++) {
|
||||
cc = window[LZSS_MOD_WINDOW(match_position + i)];
|
||||
*out_data++ = cc;
|
||||
window[current_position] = cc;
|
||||
current_position = LZSS_MOD_WINDOW(current_position + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmp_t *image_load_compressed(char *name) {
|
||||
printf("load cmp %s\n", name);
|
||||
uint32_t compressed_size;
|
||||
uint8_t *compressed_bytes = platform_load_asset(name, &compressed_size);
|
||||
|
||||
uint32_t p = 0;
|
||||
int32_t decompressed_size = 0;
|
||||
int32_t image_count = get_i32_le(compressed_bytes, &p);
|
||||
|
||||
// Calculate the total uncompressed size
|
||||
for (int i = 0; i < image_count; i++) {
|
||||
decompressed_size += get_i32_le(compressed_bytes, &p);
|
||||
}
|
||||
|
||||
uint32_t struct_size = sizeof(cmp_t) + sizeof(uint8_t *) * image_count;
|
||||
cmp_t *cmp = mem_temp_alloc(struct_size + decompressed_size);
|
||||
cmp->len = image_count;
|
||||
|
||||
uint8_t *decompressed_bytes = ((uint8_t *)cmp) + struct_size;
|
||||
|
||||
// Rewind and load all offsets
|
||||
p = 4;
|
||||
uint32_t offset = 0;
|
||||
for (int i = 0; i < image_count; i++) {
|
||||
cmp->entries[i] = decompressed_bytes + offset;
|
||||
offset += get_i32_le(compressed_bytes, &p);
|
||||
}
|
||||
|
||||
lzss_decompress(compressed_bytes + p, decompressed_bytes);
|
||||
mem_temp_free(compressed_bytes);
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
uint16_t image_get_texture(char *name) {
|
||||
printf("load: %s\n", name);
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(name, &size);
|
||||
image_t *image = image_load_from_bytes(bytes, false);
|
||||
uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels);
|
||||
mem_temp_free(image);
|
||||
mem_temp_free(bytes);
|
||||
|
||||
return texture_index;
|
||||
}
|
||||
|
||||
uint16_t image_get_texture_semi_trans(char *name) {
|
||||
printf("load: %s\n", name);
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(name, &size);
|
||||
image_t *image = image_load_from_bytes(bytes, true);
|
||||
uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels);
|
||||
mem_temp_free(image);
|
||||
mem_temp_free(bytes);
|
||||
|
||||
return texture_index;
|
||||
}
|
||||
|
||||
texture_list_t image_get_compressed_textures(char *name) {
|
||||
cmp_t *cmp = image_load_compressed(name);
|
||||
texture_list_t list = {.start = render_textures_len(), .len = cmp->len};
|
||||
|
||||
for (int i = 0; i < cmp->len; i++) {
|
||||
int32_t width, height;
|
||||
image_t *image = image_load_from_bytes(cmp->entries[i], false);
|
||||
|
||||
// char png_name[1024] = {0};
|
||||
// sprintf(png_name, "%s.%d.png", name, i);
|
||||
// stbi_write_png(png_name, image->width, image->height, 4, image->pixels, 0);
|
||||
|
||||
render_texture_create(image->width, image->height, image->pixels);
|
||||
mem_temp_free(image);
|
||||
}
|
||||
|
||||
mem_temp_free(cmp);
|
||||
return list;
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
rgba_t *src_pixels = src->pixels + sy * src->width + sx;
|
||||
rgba_t *dst_pixels = dst->pixels + dy * dst->width + dx;
|
||||
for (uint32_t y = 0; y < sh; y++) {
|
||||
for (uint32_t x = 0; x < sw; x++) {
|
||||
*(dst_pixels++) = *(src_pixels++);
|
||||
}
|
||||
src_pixels += src->width - sw;
|
||||
dst_pixels += dst->width - sw;
|
||||
}
|
||||
}
|
||||
|
||||
#include "../types.h"
|
||||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../platform.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "scene.h"
|
||||
#include "game.h"
|
||||
#include "hud.h"
|
||||
#include "image.h"
|
||||
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb_image_write.h>
|
||||
|
||||
|
||||
#define TIM_TYPE_PALETTED_4_BPP 0x08
|
||||
#define TIM_TYPE_PALETTED_8_BPP 0x09
|
||||
#define TIM_TYPE_TRUE_COLOR_16_BPP 0x02
|
||||
|
||||
static inline rgba_t tim_16bit_to_rgba(uint16_t c, bool transparent_bit) {
|
||||
return rgba(
|
||||
((c >> 0) & 0x1f) << 3,
|
||||
((c >> 5) & 0x1f) << 3,
|
||||
((c >> 10) & 0x1f) << 3,
|
||||
(c == 0
|
||||
? 0x00
|
||||
: transparent_bit && (c & 0x7fff) == 0 ? 0x00 : 0xff
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
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->width = width;
|
||||
image->height = height;
|
||||
image->pixels = (rgba_t *)(((uint8_t *)image) + sizeof(image_t));
|
||||
return image;
|
||||
}
|
||||
|
||||
image_t *image_load_from_bytes(uint8_t *bytes, bool transparent) {
|
||||
uint32_t p = 0;
|
||||
|
||||
uint32_t magic = get_i32_le(bytes, &p);
|
||||
uint32_t type = get_i32_le(bytes, &p);
|
||||
rgba_t palette[256];
|
||||
|
||||
if (
|
||||
type == TIM_TYPE_PALETTED_4_BPP ||
|
||||
type == TIM_TYPE_PALETTED_8_BPP
|
||||
) {
|
||||
uint32_t header_length = get_i32_le(bytes, &p);
|
||||
uint16_t palette_x = 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 palettes = get_i16_le(bytes, &p);
|
||||
for (int i = 0; i < palette_colors; i++) {
|
||||
palette[i] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t data_size = get_i32_le(bytes, &p);
|
||||
|
||||
int32_t pixels_per_16bit = 1;
|
||||
if (type == TIM_TYPE_PALETTED_8_BPP) {
|
||||
pixels_per_16bit = 2;
|
||||
}
|
||||
else if (type == TIM_TYPE_PALETTED_4_BPP) {
|
||||
pixels_per_16bit = 4;
|
||||
}
|
||||
|
||||
uint16_t skip_x = 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 rows = get_i16_le(bytes, &p);
|
||||
|
||||
int32_t width = entries_per_row * pixels_per_16bit;
|
||||
int32_t height = rows;
|
||||
int32_t entries = entries_per_row * rows;
|
||||
|
||||
image_t *image = image_alloc(width, height);
|
||||
int32_t pixel_pos = 0;
|
||||
|
||||
if (type == TIM_TYPE_TRUE_COLOR_16_BPP) {
|
||||
for (int i = 0; i < entries; i++) {
|
||||
image->pixels[pixel_pos++] = tim_16bit_to_rgba(get_u16_le(bytes, &p), transparent);
|
||||
}
|
||||
}
|
||||
else if (type == TIM_TYPE_PALETTED_8_BPP) {
|
||||
for (int i = 0; i < entries; i++) {
|
||||
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 >> 8) & 0xff];
|
||||
}
|
||||
}
|
||||
else if (type == TIM_TYPE_PALETTED_4_BPP) {
|
||||
for (int i = 0; i < entries; i++) {
|
||||
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 >> 4) & 0xf];
|
||||
image->pixels[pixel_pos++] = palette[(palette_pos >> 8) & 0xf];
|
||||
image->pixels[pixel_pos++] = palette[(palette_pos >> 12) & 0xf];
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
#define LZSS_INDEX_BIT_COUNT 13
|
||||
#define LZSS_LENGTH_BIT_COUNT 4
|
||||
#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_END_OF_STREAM 0
|
||||
#define LZSS_MOD_WINDOW(a) ((a) & (LZSS_WINDOW_SIZE - 1))
|
||||
|
||||
void lzss_decompress(uint8_t *in_data, uint8_t *out_data) {
|
||||
int16_t i;
|
||||
int16_t current_position;
|
||||
uint8_t cc;
|
||||
int16_t match_length;
|
||||
int16_t match_position;
|
||||
uint32_t mask;
|
||||
uint32_t return_value;
|
||||
uint8_t in_bfile_mask;
|
||||
int16_t in_bfile_rack;
|
||||
int16_t value;
|
||||
uint8_t window[LZSS_WINDOW_SIZE];
|
||||
|
||||
in_bfile_rack = 0;
|
||||
in_bfile_mask = 0x80;
|
||||
|
||||
current_position = 1;
|
||||
while (true) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
value = in_bfile_rack & in_bfile_mask;
|
||||
in_bfile_mask >>= 1;
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
mask = 1L << (8 - 1);
|
||||
return_value = 0;
|
||||
while (mask != 0) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
if (in_bfile_rack & in_bfile_mask) {
|
||||
return_value |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
in_bfile_mask >>= 1;
|
||||
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
}
|
||||
cc = (uint8_t) return_value;
|
||||
*out_data++ = cc;
|
||||
window[ current_position ] = cc;
|
||||
current_position = LZSS_MOD_WINDOW(current_position + 1);
|
||||
}
|
||||
else {
|
||||
mask = 1L << (LZSS_INDEX_BIT_COUNT - 1);
|
||||
return_value = 0;
|
||||
while (mask != 0) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
if (in_bfile_rack & in_bfile_mask) {
|
||||
return_value |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
in_bfile_mask >>= 1;
|
||||
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
}
|
||||
match_position = (int16_t) return_value;
|
||||
|
||||
if (match_position == LZSS_END_OF_STREAM) {
|
||||
break;
|
||||
}
|
||||
|
||||
mask = 1L << (LZSS_LENGTH_BIT_COUNT - 1);
|
||||
return_value = 0;
|
||||
while (mask != 0) {
|
||||
if (in_bfile_mask == 0x80) {
|
||||
in_bfile_rack = (int16_t) * in_data++;
|
||||
}
|
||||
|
||||
if (in_bfile_rack & in_bfile_mask) {
|
||||
return_value |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
in_bfile_mask >>= 1;
|
||||
|
||||
if (in_bfile_mask == 0) {
|
||||
in_bfile_mask = 0x80;
|
||||
}
|
||||
}
|
||||
match_length = (int16_t) return_value;
|
||||
|
||||
match_length += LZSS_BREAK_EVEN;
|
||||
|
||||
for (i = 0 ; i <= match_length ; i++) {
|
||||
cc = window[LZSS_MOD_WINDOW(match_position + i)];
|
||||
*out_data++ = cc;
|
||||
window[current_position] = cc;
|
||||
current_position = LZSS_MOD_WINDOW(current_position + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmp_t *image_load_compressed(char *name) {
|
||||
printf("load cmp %s\n", name);
|
||||
uint32_t compressed_size;
|
||||
uint8_t *compressed_bytes = platform_load_asset(name, &compressed_size);
|
||||
|
||||
uint32_t p = 0;
|
||||
int32_t decompressed_size = 0;
|
||||
int32_t image_count = get_i32_le(compressed_bytes, &p);
|
||||
|
||||
// Calculate the total uncompressed size
|
||||
for (int i = 0; i < image_count; i++) {
|
||||
decompressed_size += get_i32_le(compressed_bytes, &p);
|
||||
}
|
||||
|
||||
uint32_t struct_size = sizeof(cmp_t) + sizeof(uint8_t *) * image_count;
|
||||
cmp_t *cmp = mem_temp_alloc(struct_size + decompressed_size);
|
||||
cmp->len = image_count;
|
||||
|
||||
uint8_t *decompressed_bytes = ((uint8_t *)cmp) + struct_size;
|
||||
|
||||
// Rewind and load all offsets
|
||||
p = 4;
|
||||
uint32_t offset = 0;
|
||||
for (int i = 0; i < image_count; i++) {
|
||||
cmp->entries[i] = decompressed_bytes + offset;
|
||||
offset += get_i32_le(compressed_bytes, &p);
|
||||
}
|
||||
|
||||
lzss_decompress(compressed_bytes + p, decompressed_bytes);
|
||||
mem_temp_free(compressed_bytes);
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
uint16_t image_get_texture(char *name) {
|
||||
printf("load: %s\n", name);
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(name, &size);
|
||||
image_t *image = image_load_from_bytes(bytes, false);
|
||||
uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels);
|
||||
mem_temp_free(image);
|
||||
mem_temp_free(bytes);
|
||||
|
||||
return texture_index;
|
||||
}
|
||||
|
||||
uint16_t image_get_texture_semi_trans(char *name) {
|
||||
printf("load: %s\n", name);
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(name, &size);
|
||||
image_t *image = image_load_from_bytes(bytes, true);
|
||||
uint32_t texture_index = render_texture_create(image->width, image->height, image->pixels);
|
||||
mem_temp_free(image);
|
||||
mem_temp_free(bytes);
|
||||
|
||||
return texture_index;
|
||||
}
|
||||
|
||||
texture_list_t image_get_compressed_textures(char *name) {
|
||||
cmp_t *cmp = image_load_compressed(name);
|
||||
texture_list_t list = {.start = render_textures_len(), .len = cmp->len};
|
||||
|
||||
for (int i = 0; i < cmp->len; i++) {
|
||||
int32_t width, height;
|
||||
image_t *image = image_load_from_bytes(cmp->entries[i], false);
|
||||
|
||||
// char png_name[1024] = {0};
|
||||
// sprintf(png_name, "%s.%d.png", name, i);
|
||||
// stbi_write_png(png_name, image->width, image->height, 4, image->pixels, 0);
|
||||
|
||||
render_texture_create(image->width, image->height, image->pixels);
|
||||
mem_temp_free(image);
|
||||
}
|
||||
|
||||
mem_temp_free(cmp);
|
||||
return list;
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
rgba_t *src_pixels = src->pixels + sy * src->width + sx;
|
||||
rgba_t *dst_pixels = dst->pixels + dy * dst->width + dx;
|
||||
for (uint32_t y = 0; y < sh; y++) {
|
||||
for (uint32_t x = 0; x < sw; x++) {
|
||||
*(dst_pixels++) = *(src_pixels++);
|
||||
}
|
||||
src_pixels += src->width - sw;
|
||||
dst_pixels += dst->width - sw;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
#ifndef INIT_H
|
||||
#define INIT_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
typedef struct {
|
||||
uint16_t start;
|
||||
uint16_t len;
|
||||
} texture_list_t;
|
||||
|
||||
#define texture_list_empty() ((texture_list_t){0, 0})
|
||||
|
||||
typedef struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
rgba_t *pixels;
|
||||
} image_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t len;
|
||||
uint8_t *entries[];
|
||||
} cmp_t;
|
||||
|
||||
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);
|
||||
image_t *image_load_from_bytes(uint8_t *bytes, bool transparent);
|
||||
cmp_t *image_load_compressed(char *name);
|
||||
|
||||
uint16_t image_get_texture(char *name);
|
||||
uint16_t image_get_texture_semi_trans(char *name);
|
||||
texture_list_t image_get_compressed_textures(char *name);
|
||||
uint16_t texture_from_list(texture_list_t tl, uint16_t index);
|
||||
|
||||
#ifndef INIT_H
|
||||
#define INIT_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
typedef struct {
|
||||
uint16_t start;
|
||||
uint16_t len;
|
||||
} texture_list_t;
|
||||
|
||||
#define texture_list_empty() ((texture_list_t){0, 0})
|
||||
|
||||
typedef struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
rgba_t *pixels;
|
||||
} image_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t len;
|
||||
uint8_t *entries[];
|
||||
} cmp_t;
|
||||
|
||||
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);
|
||||
image_t *image_load_from_bytes(uint8_t *bytes, bool transparent);
|
||||
cmp_t *image_load_compressed(char *name);
|
||||
|
||||
uint16_t image_get_texture(char *name);
|
||||
uint16_t image_get_texture_semi_trans(char *name);
|
||||
texture_list_t image_get_compressed_textures(char *name);
|
||||
uint16_t texture_from_list(texture_list_t tl, uint16_t index);
|
||||
|
||||
#endif
|
|
@ -1,486 +1,486 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "../input.h"
|
||||
#include "../system.h"
|
||||
#include "../utils.h"
|
||||
#include "../mem.h"
|
||||
|
||||
#include "menu.h"
|
||||
#include "ingame_menus.h"
|
||||
#include "game.h"
|
||||
#include "image.h"
|
||||
#include "ui.h"
|
||||
#include "race.h"
|
||||
|
||||
static void page_race_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 texture_list_t pilot_portraits;
|
||||
static menu_t *ingame_menu;
|
||||
|
||||
void ingame_menus_load(void) {
|
||||
pilot_portraits = image_get_compressed_textures(def.pilots[g.pilot].portrait);
|
||||
ingame_menu = mem_bump(sizeof(menu_t));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Pause Menu
|
||||
|
||||
static void button_continue(menu_t *menu, int data) {
|
||||
race_unpause();
|
||||
}
|
||||
|
||||
static void button_restart_confirm(menu_t *menu, int data) {
|
||||
if (data) {
|
||||
race_restart();
|
||||
}
|
||||
else {
|
||||
menu_pop(menu);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_restart_or_quit(menu_t *menu, int data) {
|
||||
if (data) {
|
||||
race_restart();
|
||||
}
|
||||
else {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_restart(menu_t *menu, int data) {
|
||||
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) {
|
||||
if (data) {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
else {
|
||||
menu_pop(menu);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_quit(menu_t *menu, int data) {
|
||||
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) {
|
||||
sfx_music_play(data);
|
||||
sfx_music_mode(SFX_MUSIC_LOOP);
|
||||
}
|
||||
|
||||
static void button_music_random(menu_t *menu, int data) {
|
||||
sfx_music_play(rand_int(0, len(def.music)));
|
||||
sfx_music_mode(SFX_MUSIC_RANDOM);
|
||||
}
|
||||
|
||||
static void button_music(menu_t *menu, int data) {
|
||||
menu_page_t *page = menu_push(menu, "MUSIC", NULL);
|
||||
|
||||
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, 0, "RANDOM", button_music_random);
|
||||
}
|
||||
|
||||
menu_t *pause_menu_init(void) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
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, "RESTART", button_restart);
|
||||
menu_page_add_button(page, 0, "QUIT", button_quit);
|
||||
menu_page_add_button(page, 0, "MUSIC", button_music);
|
||||
return ingame_menu;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Game Over
|
||||
|
||||
menu_t *game_over_menu_init(void) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
menu_page_t *page = menu_push(ingame_menu, "GAME OVER", NULL);
|
||||
menu_page_add_button(page, 1, "", button_quit_confirm);
|
||||
return ingame_menu;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Race Stats
|
||||
|
||||
static void button_qualify_confirm(menu_t *menu, int data) {
|
||||
if (data) {
|
||||
race_restart();
|
||||
}
|
||||
else {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_race_stats_continue(menu_t *menu, int data) {
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
if (g.race_position <= QUALIFYING_RANK) {
|
||||
page_race_points_init(menu);
|
||||
}
|
||||
else {
|
||||
menu_page_t *page = menu_confirm(menu, "CONTINUE QUALIFYING OR QUIT", "", "QUALIFY", "QUIT", button_qualify_confirm);
|
||||
page->index = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (g.is_new_race_record) {
|
||||
page_hall_of_fame_init(menu);
|
||||
}
|
||||
else {
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void page_race_stats_draw(menu_t *menu, int data) {
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 140;
|
||||
pos.y += 32;
|
||||
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
|
||||
// Pilot portrait and race position - only for championship or single race
|
||||
if (g.race_type != RACE_TYPE_TIME_TRIAL) {
|
||||
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);
|
||||
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_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);
|
||||
}
|
||||
|
||||
pos.y += 32;
|
||||
|
||||
ui_draw_text("RACE STATISTICS", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
pos.y += 16;
|
||||
|
||||
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_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);
|
||||
pos.y+= 12;
|
||||
}
|
||||
pos.y += 32;
|
||||
|
||||
ui_draw_text("RACE TIME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
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);
|
||||
pos.y += 12;
|
||||
|
||||
ui_draw_text("BEST LAP", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
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);
|
||||
pos.y += 12;
|
||||
}
|
||||
|
||||
menu_t *race_stats_menu_init(void) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
char *title;
|
||||
if (g.race_type == RACE_TYPE_TIME_TRIAL) {
|
||||
title = "";
|
||||
}
|
||||
else if (g.race_position <= QUALIFYING_RANK) {
|
||||
title = "CONGRATULATIONS";
|
||||
}
|
||||
else {
|
||||
title = "FAILED TO QUALIFY";
|
||||
}
|
||||
menu_page_t *page = menu_push(ingame_menu, title, page_race_stats_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
menu_page_add_button(page, 1, "", button_race_stats_continue);
|
||||
return ingame_menu;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Race Table
|
||||
|
||||
static void button_race_points_continue(menu_t *menu, int data) {
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
page_championship_points_init(menu);
|
||||
}
|
||||
else if (g.is_new_race_record) {
|
||||
page_hall_of_fame_init(menu);
|
||||
}
|
||||
else {
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
|
||||
}
|
||||
}
|
||||
|
||||
static void page_race_points_draw(menu_t *menu, int data) {
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 140;
|
||||
pos.y += 32;
|
||||
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("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
|
||||
pos.y += 24;
|
||||
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void page_race_points_init(menu_t *menu) {
|
||||
menu_page_t *page = menu_push(menu, "RACE POINTS", page_race_points_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
menu_page_add_button(page, 1, "", button_race_points_continue);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Championship Table
|
||||
|
||||
static void button_championship_points_continue(menu_t *menu, int data) {
|
||||
if (g.is_new_race_record) {
|
||||
page_hall_of_fame_init(menu);
|
||||
}
|
||||
else if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
race_next();
|
||||
}
|
||||
else {
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_quit_confirm);
|
||||
}
|
||||
}
|
||||
|
||||
static void page_championship_points_draw(menu_t *menu, int data) {
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 140;
|
||||
pos.y += 32;
|
||||
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("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
|
||||
pos.y += 24;
|
||||
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void page_championship_points_init(menu_t *menu) {
|
||||
menu_page_t *page = menu_push(menu, "CHAMPIONSHIP TABLE", page_championship_points_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
menu_page_add_button(page, 1, "", button_championship_points_continue);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Hall of Fame
|
||||
|
||||
static highscores_entry_t hs_new_entry = {
|
||||
.time = 0,
|
||||
.name = ""
|
||||
};
|
||||
static const char *hs_charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
static int hs_char_index = 0;
|
||||
static bool hs_entry_complete = false;
|
||||
|
||||
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_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));
|
||||
int c_first = 0;
|
||||
int c_last = 38;
|
||||
if (entry_len == 0) {
|
||||
c_last = 37;
|
||||
}
|
||||
else if (entry_len == 3) {
|
||||
c_first = 36;
|
||||
}
|
||||
|
||||
if (input_pressed(A_MENU_UP)) {
|
||||
hs_char_index++;
|
||||
}
|
||||
else if (input_pressed(A_MENU_DOWN)) {
|
||||
hs_char_index--;
|
||||
}
|
||||
|
||||
if (hs_char_index < c_first) {
|
||||
hs_char_index = c_last-1;
|
||||
}
|
||||
if (hs_char_index >= c_last) {
|
||||
hs_char_index = c_first;
|
||||
}
|
||||
|
||||
// DEL
|
||||
if (hs_char_index == 36) {
|
||||
ui_draw_icon(UI_ICON_DEL, c_pos, UI_COLOR_ACCENT);
|
||||
if (input_pressed(A_MENU_SELECT)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
if (entry_len > 0) {
|
||||
hs_new_entry.name[entry_len-1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END
|
||||
else if (hs_char_index == 37) {
|
||||
ui_draw_icon(UI_ICON_END, c_pos, UI_COLOR_ACCENT);
|
||||
if (input_pressed(A_MENU_SELECT)) {
|
||||
hs_entry_complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
// A-Z, 0-9
|
||||
else {
|
||||
char selector[2] = {hs_charset[hs_char_index], '\0'};
|
||||
ui_draw_text(selector, c_pos, UI_SIZE_16, UI_COLOR_ACCENT);
|
||||
|
||||
if (input_pressed(A_MENU_SELECT)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
hs_new_entry.name[entry_len] = hs_charset[hs_char_index];
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
// complications
|
||||
|
||||
highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab];
|
||||
|
||||
if (hs_entry_complete) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
strncpy(save.highscores_name, hs_new_entry.name, 4);
|
||||
save.is_dirty = true;
|
||||
|
||||
// Insert new highscore entry into the save struct
|
||||
highscores_entry_t temp_entry = hs->entries[0];
|
||||
for (int i = 0; i < NUM_HIGHSCORES; i++) {
|
||||
if (hs_new_entry.time < hs->entries[i].time) {
|
||||
for (int j = NUM_HIGHSCORES - 2; j >= i; j--) {
|
||||
hs->entries[j+1] = hs->entries[j];
|
||||
}
|
||||
hs->entries[i] = hs_new_entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
save.is_dirty = true;
|
||||
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
race_next();
|
||||
}
|
||||
else {
|
||||
menu_reset(menu); // Can't go back!
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 120;
|
||||
pos.y += 48;
|
||||
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
|
||||
bool has_shown_new_entry = false;
|
||||
for (int i = 0, j = 0; i < NUM_HIGHSCORES; i++, j++) {
|
||||
if (!has_shown_new_entry && hs_new_entry.time < hs->entries[i].time) {
|
||||
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);
|
||||
has_shown_new_entry = true;
|
||||
j--;
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
pos.y += 24;
|
||||
}
|
||||
}
|
||||
|
||||
static void page_hall_of_fame_init(menu_t *menu) {
|
||||
menu_reset(menu); // Can't go back!
|
||||
menu_page_t *page = menu_push(menu, "HALL OF FAME", page_hall_of_fame_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
|
||||
hs_new_entry.time = g.race_time;
|
||||
strncpy(hs_new_entry.name, save.highscores_name, 4);
|
||||
hs_char_index = 0;
|
||||
hs_entry_complete = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Text scroller
|
||||
|
||||
static char * const *text_scroll_lines;
|
||||
static int text_scroll_lines_len;
|
||||
static double text_scroll_start_time;
|
||||
|
||||
static void text_scroll_menu_draw(menu_t *menu, int data) {
|
||||
double time = system_time() - text_scroll_start_time;
|
||||
int scale = ui_get_scale();
|
||||
int speed = 32;
|
||||
vec2i_t screen = render_size();
|
||||
vec2i_t pos = vec2i(screen.x / 2, screen.y - time * scale * speed);
|
||||
|
||||
for (int i = 0; i < text_scroll_lines_len; i++) {
|
||||
const char *line = text_scroll_lines[i];
|
||||
|
||||
if (line[0] == '#') {
|
||||
pos.y += 48 * scale;
|
||||
ui_draw_text_centered(line + 1, pos, UI_SIZE_16, UI_COLOR_ACCENT);
|
||||
pos.y += 32 * scale;
|
||||
}
|
||||
else {
|
||||
ui_draw_text_centered(line, pos, UI_SIZE_8, UI_COLOR_DEFAULT);
|
||||
pos.y += 12 * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu_t *text_scroll_menu_init(char * const *lines, int len) {
|
||||
text_scroll_lines = lines;
|
||||
text_scroll_lines_len = len;
|
||||
text_scroll_start_time = system_time();
|
||||
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
menu_page_t *page = menu_push(ingame_menu, "", text_scroll_menu_draw);
|
||||
menu_page_add_button(page, 1, "", button_quit_confirm);
|
||||
return ingame_menu;
|
||||
}
|
||||
#include <string.h>
|
||||
|
||||
#include "../input.h"
|
||||
#include "../system.h"
|
||||
#include "../utils.h"
|
||||
#include "../mem.h"
|
||||
|
||||
#include "menu.h"
|
||||
#include "ingame_menus.h"
|
||||
#include "game.h"
|
||||
#include "image.h"
|
||||
#include "ui.h"
|
||||
#include "race.h"
|
||||
|
||||
static void page_race_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 texture_list_t pilot_portraits;
|
||||
static menu_t *ingame_menu;
|
||||
|
||||
void ingame_menus_load(void) {
|
||||
pilot_portraits = image_get_compressed_textures(def.pilots[g.pilot].portrait);
|
||||
ingame_menu = mem_bump(sizeof(menu_t));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Pause Menu
|
||||
|
||||
static void button_continue(menu_t *menu, int data) {
|
||||
race_unpause();
|
||||
}
|
||||
|
||||
static void button_restart_confirm(menu_t *menu, int data) {
|
||||
if (data) {
|
||||
race_restart();
|
||||
}
|
||||
else {
|
||||
menu_pop(menu);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_restart_or_quit(menu_t *menu, int data) {
|
||||
if (data) {
|
||||
race_restart();
|
||||
}
|
||||
else {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_restart(menu_t *menu, int data) {
|
||||
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) {
|
||||
if (data) {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
else {
|
||||
menu_pop(menu);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_quit(menu_t *menu, int data) {
|
||||
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) {
|
||||
sfx_music_play(data);
|
||||
sfx_music_mode(SFX_MUSIC_LOOP);
|
||||
}
|
||||
|
||||
static void button_music_random(menu_t *menu, int data) {
|
||||
sfx_music_play(rand_int(0, len(def.music)));
|
||||
sfx_music_mode(SFX_MUSIC_RANDOM);
|
||||
}
|
||||
|
||||
static void button_music(menu_t *menu, int data) {
|
||||
menu_page_t *page = menu_push(menu, "MUSIC", NULL);
|
||||
|
||||
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, 0, "RANDOM", button_music_random);
|
||||
}
|
||||
|
||||
menu_t *pause_menu_init(void) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
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, "RESTART", button_restart);
|
||||
menu_page_add_button(page, 0, "QUIT", button_quit);
|
||||
menu_page_add_button(page, 0, "MUSIC", button_music);
|
||||
return ingame_menu;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Game Over
|
||||
|
||||
menu_t *game_over_menu_init(void) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
menu_page_t *page = menu_push(ingame_menu, "GAME OVER", NULL);
|
||||
menu_page_add_button(page, 1, "", button_quit_confirm);
|
||||
return ingame_menu;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Race Stats
|
||||
|
||||
static void button_qualify_confirm(menu_t *menu, int data) {
|
||||
if (data) {
|
||||
race_restart();
|
||||
}
|
||||
else {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_race_stats_continue(menu_t *menu, int data) {
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
if (g.race_position <= QUALIFYING_RANK) {
|
||||
page_race_points_init(menu);
|
||||
}
|
||||
else {
|
||||
menu_page_t *page = menu_confirm(menu, "CONTINUE QUALIFYING OR QUIT", "", "QUALIFY", "QUIT", button_qualify_confirm);
|
||||
page->index = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (g.is_new_race_record) {
|
||||
page_hall_of_fame_init(menu);
|
||||
}
|
||||
else {
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void page_race_stats_draw(menu_t *menu, int data) {
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 140;
|
||||
pos.y += 32;
|
||||
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
|
||||
// Pilot portrait and race position - only for championship or single race
|
||||
if (g.race_type != RACE_TYPE_TIME_TRIAL) {
|
||||
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);
|
||||
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_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);
|
||||
}
|
||||
|
||||
pos.y += 32;
|
||||
|
||||
ui_draw_text("RACE STATISTICS", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
pos.y += 16;
|
||||
|
||||
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_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);
|
||||
pos.y+= 12;
|
||||
}
|
||||
pos.y += 32;
|
||||
|
||||
ui_draw_text("RACE TIME", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
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);
|
||||
pos.y += 12;
|
||||
|
||||
ui_draw_text("BEST LAP", ui_scaled_pos(anchor, pos), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
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);
|
||||
pos.y += 12;
|
||||
}
|
||||
|
||||
menu_t *race_stats_menu_init(void) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
char *title;
|
||||
if (g.race_type == RACE_TYPE_TIME_TRIAL) {
|
||||
title = "";
|
||||
}
|
||||
else if (g.race_position <= QUALIFYING_RANK) {
|
||||
title = "CONGRATULATIONS";
|
||||
}
|
||||
else {
|
||||
title = "FAILED TO QUALIFY";
|
||||
}
|
||||
menu_page_t *page = menu_push(ingame_menu, title, page_race_stats_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
menu_page_add_button(page, 1, "", button_race_stats_continue);
|
||||
return ingame_menu;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Race Table
|
||||
|
||||
static void button_race_points_continue(menu_t *menu, int data) {
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
page_championship_points_init(menu);
|
||||
}
|
||||
else if (g.is_new_race_record) {
|
||||
page_hall_of_fame_init(menu);
|
||||
}
|
||||
else {
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
|
||||
}
|
||||
}
|
||||
|
||||
static void page_race_points_draw(menu_t *menu, int data) {
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 140;
|
||||
pos.y += 32;
|
||||
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("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
|
||||
pos.y += 24;
|
||||
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void page_race_points_init(menu_t *menu) {
|
||||
menu_page_t *page = menu_push(menu, "RACE POINTS", page_race_points_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
menu_page_add_button(page, 1, "", button_race_points_continue);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Championship Table
|
||||
|
||||
static void button_championship_points_continue(menu_t *menu, int data) {
|
||||
if (g.is_new_race_record) {
|
||||
page_hall_of_fame_init(menu);
|
||||
}
|
||||
else if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
race_next();
|
||||
}
|
||||
else {
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_quit_confirm);
|
||||
}
|
||||
}
|
||||
|
||||
static void page_championship_points_draw(menu_t *menu, int data) {
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 140;
|
||||
pos.y += 32;
|
||||
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("POINTS", ui_scaled_pos(anchor, vec2i(pos.x + 222, pos.y)), UI_SIZE_8, UI_COLOR_ACCENT);
|
||||
|
||||
pos.y += 24;
|
||||
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void page_championship_points_init(menu_t *menu) {
|
||||
menu_page_t *page = menu_push(menu, "CHAMPIONSHIP TABLE", page_championship_points_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
menu_page_add_button(page, 1, "", button_championship_points_continue);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Hall of Fame
|
||||
|
||||
static highscores_entry_t hs_new_entry = {
|
||||
.time = 0,
|
||||
.name = ""
|
||||
};
|
||||
static const char *hs_charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
static int hs_char_index = 0;
|
||||
static bool hs_entry_complete = false;
|
||||
|
||||
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_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));
|
||||
int c_first = 0;
|
||||
int c_last = 38;
|
||||
if (entry_len == 0) {
|
||||
c_last = 37;
|
||||
}
|
||||
else if (entry_len == 3) {
|
||||
c_first = 36;
|
||||
}
|
||||
|
||||
if (input_pressed(A_MENU_UP)) {
|
||||
hs_char_index++;
|
||||
}
|
||||
else if (input_pressed(A_MENU_DOWN)) {
|
||||
hs_char_index--;
|
||||
}
|
||||
|
||||
if (hs_char_index < c_first) {
|
||||
hs_char_index = c_last-1;
|
||||
}
|
||||
if (hs_char_index >= c_last) {
|
||||
hs_char_index = c_first;
|
||||
}
|
||||
|
||||
// DEL
|
||||
if (hs_char_index == 36) {
|
||||
ui_draw_icon(UI_ICON_DEL, c_pos, UI_COLOR_ACCENT);
|
||||
if (input_pressed(A_MENU_SELECT)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
if (entry_len > 0) {
|
||||
hs_new_entry.name[entry_len-1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END
|
||||
else if (hs_char_index == 37) {
|
||||
ui_draw_icon(UI_ICON_END, c_pos, UI_COLOR_ACCENT);
|
||||
if (input_pressed(A_MENU_SELECT)) {
|
||||
hs_entry_complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
// A-Z, 0-9
|
||||
else {
|
||||
char selector[2] = {hs_charset[hs_char_index], '\0'};
|
||||
ui_draw_text(selector, c_pos, UI_SIZE_16, UI_COLOR_ACCENT);
|
||||
|
||||
if (input_pressed(A_MENU_SELECT)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
hs_new_entry.name[entry_len] = hs_charset[hs_char_index];
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
// complications
|
||||
|
||||
highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab];
|
||||
|
||||
if (hs_entry_complete) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
strncpy(save.highscores_name, hs_new_entry.name, 4);
|
||||
save.is_dirty = true;
|
||||
|
||||
// Insert new highscore entry into the save struct
|
||||
highscores_entry_t temp_entry = hs->entries[0];
|
||||
for (int i = 0; i < NUM_HIGHSCORES; i++) {
|
||||
if (hs_new_entry.time < hs->entries[i].time) {
|
||||
for (int j = NUM_HIGHSCORES - 2; j >= i; j--) {
|
||||
hs->entries[j+1] = hs->entries[j];
|
||||
}
|
||||
hs->entries[i] = hs_new_entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
save.is_dirty = true;
|
||||
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
race_next();
|
||||
}
|
||||
else {
|
||||
menu_reset(menu); // Can't go back!
|
||||
menu_confirm(menu, "", "RESTART RACE", "RESTART", "QUIT", button_restart_or_quit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
vec2i_t pos = page->title_pos;
|
||||
pos.x -= 120;
|
||||
pos.y += 48;
|
||||
ui_pos_t anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
|
||||
bool has_shown_new_entry = false;
|
||||
for (int i = 0, j = 0; i < NUM_HIGHSCORES; i++, j++) {
|
||||
if (!has_shown_new_entry && hs_new_entry.time < hs->entries[i].time) {
|
||||
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);
|
||||
has_shown_new_entry = true;
|
||||
j--;
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
pos.y += 24;
|
||||
}
|
||||
}
|
||||
|
||||
static void page_hall_of_fame_init(menu_t *menu) {
|
||||
menu_reset(menu); // Can't go back!
|
||||
menu_page_t *page = menu_push(menu, "HALL OF FAME", page_hall_of_fame_draw);
|
||||
flags_add(page->layout_flags, MENU_FIXED);
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->title_pos = vec2i(0, -100);
|
||||
|
||||
hs_new_entry.time = g.race_time;
|
||||
strncpy(hs_new_entry.name, save.highscores_name, 4);
|
||||
hs_char_index = 0;
|
||||
hs_entry_complete = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Text scroller
|
||||
|
||||
static char * const *text_scroll_lines;
|
||||
static int text_scroll_lines_len;
|
||||
static double text_scroll_start_time;
|
||||
|
||||
static void text_scroll_menu_draw(menu_t *menu, int data) {
|
||||
double time = system_time() - text_scroll_start_time;
|
||||
int scale = ui_get_scale();
|
||||
int speed = 32;
|
||||
vec2i_t screen = render_size();
|
||||
vec2i_t pos = vec2i(screen.x / 2, screen.y - time * scale * speed);
|
||||
|
||||
for (int i = 0; i < text_scroll_lines_len; i++) {
|
||||
const char *line = text_scroll_lines[i];
|
||||
|
||||
if (line[0] == '#') {
|
||||
pos.y += 48 * scale;
|
||||
ui_draw_text_centered(line + 1, pos, UI_SIZE_16, UI_COLOR_ACCENT);
|
||||
pos.y += 32 * scale;
|
||||
}
|
||||
else {
|
||||
ui_draw_text_centered(line, pos, UI_SIZE_8, UI_COLOR_DEFAULT);
|
||||
pos.y += 12 * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu_t *text_scroll_menu_init(char * const *lines, int len) {
|
||||
text_scroll_lines = lines;
|
||||
text_scroll_lines_len = len;
|
||||
text_scroll_start_time = system_time();
|
||||
|
||||
menu_reset(ingame_menu);
|
||||
|
||||
menu_page_t *page = menu_push(ingame_menu, "", text_scroll_menu_draw);
|
||||
menu_page_add_button(page, 1, "", button_quit_confirm);
|
||||
return ingame_menu;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#ifndef PAUSE_MENU_H
|
||||
#define PAUSE_MENU_H
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
void ingame_menus_load(void);
|
||||
|
||||
menu_t *pause_menu_init(void);
|
||||
menu_t *game_over_menu_init(void);
|
||||
menu_t *race_stats_menu_init(void);
|
||||
menu_t *text_scroll_menu_init(char * const *lines, int len);
|
||||
|
||||
#endif
|
||||
#ifndef PAUSE_MENU_H
|
||||
#define PAUSE_MENU_H
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
void ingame_menus_load(void);
|
||||
|
||||
menu_t *pause_menu_init(void);
|
||||
menu_t *game_over_menu_init(void);
|
||||
menu_t *race_stats_menu_init(void);
|
||||
menu_t *text_scroll_menu_init(char * const *lines, int len);
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
#ifndef MAIN_MENU_H
|
||||
#define MAIN_MENU_H
|
||||
|
||||
void main_menu_init(void);
|
||||
void main_menu_update(void);
|
||||
|
||||
#endif
|
||||
#ifndef MAIN_MENU_H
|
||||
#define MAIN_MENU_H
|
||||
|
||||
void main_menu_init(void);
|
||||
void main_menu_update(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,245 +1,245 @@
|
|||
#include "../system.h"
|
||||
#include "../input.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include "game.h"
|
||||
#include "menu.h"
|
||||
#include "ui.h"
|
||||
#include "sfx.h"
|
||||
|
||||
bool blink(void) {
|
||||
// blink 30 times per second
|
||||
return fmod(system_cycle_time(), 1.0/15.0) < 1.0/30.0;
|
||||
}
|
||||
|
||||
void menu_reset(menu_t *menu) {
|
||||
menu->index = -1;
|
||||
}
|
||||
|
||||
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");
|
||||
menu_page_t *page = &menu->pages[++menu->index];
|
||||
page->layout_flags = MENU_VERTICAL | MENU_ALIGN_CENTER;
|
||||
page->block_width = 320;
|
||||
page->title = title;
|
||||
page->subtitle = NULL;
|
||||
page->draw_func = draw_func;
|
||||
page->entries_len = 0;
|
||||
page->index = 0;
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
return page;
|
||||
}
|
||||
|
||||
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");
|
||||
menu_page_t *page = &menu->pages[++menu->index];
|
||||
page->layout_flags = MENU_HORIZONTAL;
|
||||
page->title = title;
|
||||
page->subtitle = subtitle;
|
||||
page->draw_func = NULL;
|
||||
page->entries_len = 0;
|
||||
menu_page_add_button(page, 1, yes, confirm_func);
|
||||
menu_page_add_button(page, 0, no, confirm_func);
|
||||
page->index = 1;
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
return page;
|
||||
}
|
||||
|
||||
void menu_pop(menu_t *menu) {
|
||||
if (menu->index == 0) {
|
||||
return;
|
||||
}
|
||||
menu->index--;
|
||||
}
|
||||
|
||||
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");
|
||||
menu_entry_t *entry = &page->entries[page->entries_len++];
|
||||
entry->data = data;
|
||||
entry->text = text;
|
||||
entry->select_func = select_func;
|
||||
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)) {
|
||||
error_if(page->entries_len >= MENU_ENTRIES_MAX-1, "MENU_ENTRIES_MAX exceeded");
|
||||
menu_entry_t *entry = &page->entries[page->entries_len++];
|
||||
entry->data = data;
|
||||
entry->text = text;
|
||||
entry->select_func = select_func;
|
||||
entry->type = MENU_ENTRY_TOGGLE;
|
||||
entry->options = options;
|
||||
entry->options_len = len;
|
||||
}
|
||||
|
||||
|
||||
void menu_update(menu_t *menu) {
|
||||
render_set_view_2d();
|
||||
|
||||
error_if(menu->index < 0, "Attempt to update menu without a page");
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
|
||||
// Handle menu entry selecting
|
||||
int last_index = page->index;
|
||||
int selected_data = 0;
|
||||
if (page->entries_len > 0) {
|
||||
if (flags_is(page->layout_flags, MENU_HORIZONTAL)) {
|
||||
if (input_pressed(A_MENU_LEFT)) {
|
||||
page->index--;
|
||||
}
|
||||
else if (input_pressed(A_MENU_RIGHT)) {
|
||||
page->index++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (input_pressed(A_MENU_UP)) {
|
||||
page->index--;
|
||||
}
|
||||
if (input_pressed(A_MENU_DOWN)) {
|
||||
page->index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (page->index >= page->entries_len) {
|
||||
page->index = 0;
|
||||
}
|
||||
if (page->index < 0) {
|
||||
page->index = page->entries_len - 1;
|
||||
}
|
||||
|
||||
if (last_index != page->index) {
|
||||
sfx_play(SFX_MENU_MOVE);
|
||||
}
|
||||
selected_data = page->entries[page->index].data;
|
||||
}
|
||||
|
||||
if (page->draw_func) {
|
||||
page->draw_func(menu, selected_data);
|
||||
}
|
||||
|
||||
render_set_view_2d();
|
||||
|
||||
// Draw Horizontal (confirm)
|
||||
if (flags_is(page->layout_flags, MENU_HORIZONTAL)) {
|
||||
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);
|
||||
if (page->subtitle) {
|
||||
pos.y += 12;
|
||||
ui_draw_text_centered(page->subtitle, ui_scaled_pos(page->title_anchor, pos), UI_SIZE_8, UI_COLOR_DEFAULT);
|
||||
}
|
||||
pos.y += 16;
|
||||
|
||||
page = &menu->pages[menu->index];
|
||||
pos.x = -50;
|
||||
for (int i = 0; i < page->entries_len; i++) {
|
||||
menu_entry_t *entry = &page->entries[i];
|
||||
rgba_t text_color;
|
||||
if (i == page->index && blink()) {
|
||||
text_color = UI_COLOR_ACCENT;
|
||||
}
|
||||
else {
|
||||
text_color = UI_COLOR_DEFAULT;
|
||||
}
|
||||
ui_draw_text_centered(entry->text, ui_scaled_pos(page->items_anchor, pos), UI_SIZE_16, text_color);
|
||||
pos.x = 60;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Vertical
|
||||
else {
|
||||
vec2i_t title_pos, items_pos;
|
||||
if (flags_not(page->layout_flags, MENU_FIXED)) {
|
||||
int height = 20 + page->entries_len * 12;
|
||||
title_pos = vec2i(0, -height/2);
|
||||
items_pos = vec2i(0, -height/2 + 20);
|
||||
}
|
||||
else {
|
||||
title_pos = page->title_pos;
|
||||
items_pos = page->items_pos;
|
||||
}
|
||||
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);
|
||||
}
|
||||
else {
|
||||
ui_draw_text(page->title, ui_scaled_pos(page->title_anchor, title_pos), UI_SIZE_12, UI_COLOR_ACCENT);
|
||||
}
|
||||
|
||||
page = &menu->pages[menu->index];
|
||||
for (int i = 0; i < page->entries_len; i++) {
|
||||
menu_entry_t *entry = &page->entries[i];
|
||||
rgba_t text_color;
|
||||
if (i == page->index && blink()) {
|
||||
text_color = UI_COLOR_ACCENT;
|
||||
}
|
||||
else {
|
||||
text_color = UI_COLOR_DEFAULT;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else {
|
||||
ui_draw_text(entry->text, ui_scaled_pos(page->items_anchor, items_pos), UI_SIZE_8, text_color);
|
||||
}
|
||||
|
||||
if (entry->type == MENU_ENTRY_TOGGLE) {
|
||||
vec2i_t toggle_pos = items_pos;
|
||||
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);
|
||||
}
|
||||
items_pos.y += 12;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle back buttons
|
||||
if (input_pressed(A_MENU_BACK) || input_pressed(A_MENU_QUIT)) {
|
||||
if (menu->index != 0) {
|
||||
menu_pop(menu);
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (page->entries_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Handle toggle entries
|
||||
menu_entry_t *entry = &page->entries[page->index];
|
||||
|
||||
if (entry->type == MENU_ENTRY_TOGGLE) {
|
||||
if (input_pressed(A_MENU_LEFT)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
entry->data--;
|
||||
if (entry->data < 0) {
|
||||
entry->data = entry->options_len-1;
|
||||
}
|
||||
if (entry->select_func) {
|
||||
entry->select_func(menu, entry->data);
|
||||
}
|
||||
}
|
||||
else if (input_pressed(A_MENU_RIGHT) || input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
entry->data = (entry->data + 1) % entry->options_len;
|
||||
if (entry->select_func) {
|
||||
entry->select_func(menu, entry->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle buttons
|
||||
else {
|
||||
if (input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) {
|
||||
if (entry->select_func) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
if (entry->type == MENU_ENTRY_TOGGLE) {
|
||||
entry->data = (entry->data + 1) % entry->options_len;
|
||||
}
|
||||
entry->select_func(menu, entry->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "../system.h"
|
||||
#include "../input.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include "game.h"
|
||||
#include "menu.h"
|
||||
#include "ui.h"
|
||||
#include "sfx.h"
|
||||
|
||||
bool blink(void) {
|
||||
// blink 30 times per second
|
||||
return fmod(system_cycle_time(), 1.0/15.0) < 1.0/30.0;
|
||||
}
|
||||
|
||||
void menu_reset(menu_t *menu) {
|
||||
menu->index = -1;
|
||||
}
|
||||
|
||||
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");
|
||||
menu_page_t *page = &menu->pages[++menu->index];
|
||||
page->layout_flags = MENU_VERTICAL | MENU_ALIGN_CENTER;
|
||||
page->block_width = 320;
|
||||
page->title = title;
|
||||
page->subtitle = NULL;
|
||||
page->draw_func = draw_func;
|
||||
page->entries_len = 0;
|
||||
page->index = 0;
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
return page;
|
||||
}
|
||||
|
||||
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");
|
||||
menu_page_t *page = &menu->pages[++menu->index];
|
||||
page->layout_flags = MENU_HORIZONTAL;
|
||||
page->title = title;
|
||||
page->subtitle = subtitle;
|
||||
page->draw_func = NULL;
|
||||
page->entries_len = 0;
|
||||
menu_page_add_button(page, 1, yes, confirm_func);
|
||||
menu_page_add_button(page, 0, no, confirm_func);
|
||||
page->index = 1;
|
||||
page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER;
|
||||
return page;
|
||||
}
|
||||
|
||||
void menu_pop(menu_t *menu) {
|
||||
if (menu->index == 0) {
|
||||
return;
|
||||
}
|
||||
menu->index--;
|
||||
}
|
||||
|
||||
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");
|
||||
menu_entry_t *entry = &page->entries[page->entries_len++];
|
||||
entry->data = data;
|
||||
entry->text = text;
|
||||
entry->select_func = select_func;
|
||||
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)) {
|
||||
error_if(page->entries_len >= MENU_ENTRIES_MAX-1, "MENU_ENTRIES_MAX exceeded");
|
||||
menu_entry_t *entry = &page->entries[page->entries_len++];
|
||||
entry->data = data;
|
||||
entry->text = text;
|
||||
entry->select_func = select_func;
|
||||
entry->type = MENU_ENTRY_TOGGLE;
|
||||
entry->options = options;
|
||||
entry->options_len = len;
|
||||
}
|
||||
|
||||
|
||||
void menu_update(menu_t *menu) {
|
||||
render_set_view_2d();
|
||||
|
||||
error_if(menu->index < 0, "Attempt to update menu without a page");
|
||||
menu_page_t *page = &menu->pages[menu->index];
|
||||
|
||||
// Handle menu entry selecting
|
||||
int last_index = page->index;
|
||||
int selected_data = 0;
|
||||
if (page->entries_len > 0) {
|
||||
if (flags_is(page->layout_flags, MENU_HORIZONTAL)) {
|
||||
if (input_pressed(A_MENU_LEFT)) {
|
||||
page->index--;
|
||||
}
|
||||
else if (input_pressed(A_MENU_RIGHT)) {
|
||||
page->index++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (input_pressed(A_MENU_UP)) {
|
||||
page->index--;
|
||||
}
|
||||
if (input_pressed(A_MENU_DOWN)) {
|
||||
page->index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (page->index >= page->entries_len) {
|
||||
page->index = 0;
|
||||
}
|
||||
if (page->index < 0) {
|
||||
page->index = page->entries_len - 1;
|
||||
}
|
||||
|
||||
if (last_index != page->index) {
|
||||
sfx_play(SFX_MENU_MOVE);
|
||||
}
|
||||
selected_data = page->entries[page->index].data;
|
||||
}
|
||||
|
||||
if (page->draw_func) {
|
||||
page->draw_func(menu, selected_data);
|
||||
}
|
||||
|
||||
render_set_view_2d();
|
||||
|
||||
// Draw Horizontal (confirm)
|
||||
if (flags_is(page->layout_flags, MENU_HORIZONTAL)) {
|
||||
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);
|
||||
if (page->subtitle) {
|
||||
pos.y += 12;
|
||||
ui_draw_text_centered(page->subtitle, ui_scaled_pos(page->title_anchor, pos), UI_SIZE_8, UI_COLOR_DEFAULT);
|
||||
}
|
||||
pos.y += 16;
|
||||
|
||||
page = &menu->pages[menu->index];
|
||||
pos.x = -50;
|
||||
for (int i = 0; i < page->entries_len; i++) {
|
||||
menu_entry_t *entry = &page->entries[i];
|
||||
rgba_t text_color;
|
||||
if (i == page->index && blink()) {
|
||||
text_color = UI_COLOR_ACCENT;
|
||||
}
|
||||
else {
|
||||
text_color = UI_COLOR_DEFAULT;
|
||||
}
|
||||
ui_draw_text_centered(entry->text, ui_scaled_pos(page->items_anchor, pos), UI_SIZE_16, text_color);
|
||||
pos.x = 60;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Vertical
|
||||
else {
|
||||
vec2i_t title_pos, items_pos;
|
||||
if (flags_not(page->layout_flags, MENU_FIXED)) {
|
||||
int height = 20 + page->entries_len * 12;
|
||||
title_pos = vec2i(0, -height/2);
|
||||
items_pos = vec2i(0, -height/2 + 20);
|
||||
}
|
||||
else {
|
||||
title_pos = page->title_pos;
|
||||
items_pos = page->items_pos;
|
||||
}
|
||||
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);
|
||||
}
|
||||
else {
|
||||
ui_draw_text(page->title, ui_scaled_pos(page->title_anchor, title_pos), UI_SIZE_12, UI_COLOR_ACCENT);
|
||||
}
|
||||
|
||||
page = &menu->pages[menu->index];
|
||||
for (int i = 0; i < page->entries_len; i++) {
|
||||
menu_entry_t *entry = &page->entries[i];
|
||||
rgba_t text_color;
|
||||
if (i == page->index && blink()) {
|
||||
text_color = UI_COLOR_ACCENT;
|
||||
}
|
||||
else {
|
||||
text_color = UI_COLOR_DEFAULT;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else {
|
||||
ui_draw_text(entry->text, ui_scaled_pos(page->items_anchor, items_pos), UI_SIZE_8, text_color);
|
||||
}
|
||||
|
||||
if (entry->type == MENU_ENTRY_TOGGLE) {
|
||||
vec2i_t toggle_pos = items_pos;
|
||||
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);
|
||||
}
|
||||
items_pos.y += 12;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle back buttons
|
||||
if (input_pressed(A_MENU_BACK) || input_pressed(A_MENU_QUIT)) {
|
||||
if (menu->index != 0) {
|
||||
menu_pop(menu);
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (page->entries_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Handle toggle entries
|
||||
menu_entry_t *entry = &page->entries[page->index];
|
||||
|
||||
if (entry->type == MENU_ENTRY_TOGGLE) {
|
||||
if (input_pressed(A_MENU_LEFT)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
entry->data--;
|
||||
if (entry->data < 0) {
|
||||
entry->data = entry->options_len-1;
|
||||
}
|
||||
if (entry->select_func) {
|
||||
entry->select_func(menu, entry->data);
|
||||
}
|
||||
}
|
||||
else if (input_pressed(A_MENU_RIGHT) || input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
entry->data = (entry->data + 1) % entry->options_len;
|
||||
if (entry->select_func) {
|
||||
entry->select_func(menu, entry->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle buttons
|
||||
else {
|
||||
if (input_pressed(A_MENU_SELECT) || input_pressed(A_MENU_START)) {
|
||||
if (entry->select_func) {
|
||||
sfx_play(SFX_MENU_SELECT);
|
||||
if (entry->type == MENU_ENTRY_TOGGLE) {
|
||||
entry->data = (entry->data + 1) % entry->options_len;
|
||||
}
|
||||
entry->select_func(menu, entry->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,65 +1,65 @@
|
|||
#ifndef MENU_H
|
||||
#define MENU_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "ui.h"
|
||||
|
||||
#define MENU_PAGES_MAX 8
|
||||
#define MENU_ENTRIES_MAX 16
|
||||
|
||||
typedef enum {
|
||||
MENU_ENTRY_BUTTON,
|
||||
MENU_ENTRY_TOGGLE
|
||||
} menu_entry_type_t;
|
||||
|
||||
typedef enum {
|
||||
MENU_VERTICAL = (1<<0),
|
||||
MENU_HORIZONTAL = (1<<1),
|
||||
MENU_FIXED = (1<<2),
|
||||
MENU_ALIGN_CENTER = (1<<3),
|
||||
MENU_ALIGN_BLOCK = (1<<4)
|
||||
} menu_page_layout_t;
|
||||
|
||||
typedef struct menu_t menu_t;
|
||||
typedef struct menu_page_t menu_page_t;
|
||||
typedef struct menu_entry_t menu_entry_t;
|
||||
typedef struct menu_entry_options_t menu_entry_options_t;
|
||||
|
||||
struct menu_entry_t {
|
||||
menu_entry_type_t type;
|
||||
int data;
|
||||
char *text;
|
||||
void (*select_func)(menu_t *, int);
|
||||
const char **options;
|
||||
int options_len;
|
||||
};
|
||||
|
||||
struct menu_page_t {
|
||||
char *title, *subtitle;
|
||||
menu_page_layout_t layout_flags;
|
||||
void (*draw_func)(menu_t *, int);
|
||||
menu_entry_t entries[MENU_ENTRIES_MAX];
|
||||
int entries_len;
|
||||
int index;
|
||||
int block_width;
|
||||
vec2i_t title_pos;
|
||||
ui_pos_t title_anchor;
|
||||
vec2i_t items_pos;
|
||||
ui_pos_t items_anchor;
|
||||
};
|
||||
|
||||
struct menu_t {
|
||||
menu_page_t pages[MENU_PAGES_MAX];
|
||||
int index;
|
||||
};
|
||||
|
||||
|
||||
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_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_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_update(menu_t *menu);
|
||||
|
||||
#endif
|
||||
#ifndef MENU_H
|
||||
#define MENU_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "ui.h"
|
||||
|
||||
#define MENU_PAGES_MAX 8
|
||||
#define MENU_ENTRIES_MAX 16
|
||||
|
||||
typedef enum {
|
||||
MENU_ENTRY_BUTTON,
|
||||
MENU_ENTRY_TOGGLE
|
||||
} menu_entry_type_t;
|
||||
|
||||
typedef enum {
|
||||
MENU_VERTICAL = (1<<0),
|
||||
MENU_HORIZONTAL = (1<<1),
|
||||
MENU_FIXED = (1<<2),
|
||||
MENU_ALIGN_CENTER = (1<<3),
|
||||
MENU_ALIGN_BLOCK = (1<<4)
|
||||
} menu_page_layout_t;
|
||||
|
||||
typedef struct menu_t menu_t;
|
||||
typedef struct menu_page_t menu_page_t;
|
||||
typedef struct menu_entry_t menu_entry_t;
|
||||
typedef struct menu_entry_options_t menu_entry_options_t;
|
||||
|
||||
struct menu_entry_t {
|
||||
menu_entry_type_t type;
|
||||
int data;
|
||||
char *text;
|
||||
void (*select_func)(menu_t *, int);
|
||||
const char **options;
|
||||
int options_len;
|
||||
};
|
||||
|
||||
struct menu_page_t {
|
||||
char *title, *subtitle;
|
||||
menu_page_layout_t layout_flags;
|
||||
void (*draw_func)(menu_t *, int);
|
||||
menu_entry_t entries[MENU_ENTRIES_MAX];
|
||||
int entries_len;
|
||||
int index;
|
||||
int block_width;
|
||||
vec2i_t title_pos;
|
||||
ui_pos_t title_anchor;
|
||||
vec2i_t items_pos;
|
||||
ui_pos_t items_anchor;
|
||||
};
|
||||
|
||||
struct menu_t {
|
||||
menu_page_t pages[MENU_PAGES_MAX];
|
||||
int index;
|
||||
};
|
||||
|
||||
|
||||
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_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_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_update(menu_t *menu);
|
||||
|
||||
#endif
|
||||
|
|
1564
src/wipeout/object.c
1564
src/wipeout/object.c
File diff suppressed because it is too large
Load diff
|
@ -1,384 +1,384 @@
|
|||
#ifndef OBJECT_H
|
||||
#define OBJECT_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "../render.h"
|
||||
#include "../utils.h"
|
||||
#include "image.h"
|
||||
|
||||
// Primitive Structure Stub ( Structure varies with primitive type )
|
||||
|
||||
typedef struct Primitive {
|
||||
int16_t type; // Type of Primitive
|
||||
} Primitive;
|
||||
|
||||
|
||||
typedef struct F3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} F3;
|
||||
|
||||
typedef struct FT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} FT3;
|
||||
|
||||
typedef struct F4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
rgba_t color;
|
||||
} F4;
|
||||
|
||||
typedef struct FT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} FT4;
|
||||
|
||||
typedef struct G3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t pad1;
|
||||
rgba_t color[3];
|
||||
} G3;
|
||||
|
||||
typedef struct GT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
int16_t pad1;
|
||||
rgba_t color[3];
|
||||
} GT3;
|
||||
|
||||
typedef struct G4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
rgba_t color[4];
|
||||
} G4;
|
||||
|
||||
typedef struct GT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
int16_t pad1;
|
||||
rgba_t color[4];
|
||||
} GT4;
|
||||
|
||||
|
||||
|
||||
|
||||
/* LIGHT SOURCED POLYGONS
|
||||
*/
|
||||
|
||||
typedef struct LSF3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
rgba_t color;
|
||||
} LSF3;
|
||||
|
||||
typedef struct LSFT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
rgba_t color;
|
||||
} LSFT3;
|
||||
|
||||
typedef struct LSF4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} LSF4;
|
||||
|
||||
typedef struct LSFT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
rgba_t color;
|
||||
} LSFT4;
|
||||
|
||||
typedef struct LSG3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normals[3]; // Indices of the normals
|
||||
rgba_t color[3];
|
||||
} LSG3;
|
||||
|
||||
typedef struct LSGT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normals[3]; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
rgba_t color[3];
|
||||
} LSGT3;
|
||||
|
||||
typedef struct LSG4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normals[4]; // Indices of the normals
|
||||
rgba_t color[4];
|
||||
} LSG4;
|
||||
|
||||
typedef struct LSGT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normals[4]; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
int16_t pad1;
|
||||
rgba_t color[4];
|
||||
} LSGT4;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* OTHER PRIMITIVE TYPES
|
||||
*/
|
||||
typedef struct SPR {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
int16_t coord;
|
||||
int16_t width;
|
||||
int16_t height;
|
||||
int16_t texture;
|
||||
rgba_t color;
|
||||
} SPR;
|
||||
|
||||
|
||||
typedef struct Spline {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
vec3_t control1;
|
||||
vec3_t position;
|
||||
vec3_t control2;
|
||||
rgba_t color;
|
||||
} Spline;
|
||||
|
||||
|
||||
typedef struct PointLight {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
vec3_t position;
|
||||
rgba_t color;
|
||||
int16_t startFalloff;
|
||||
int16_t endFalloff;
|
||||
} PointLight;
|
||||
|
||||
|
||||
typedef struct SpotLight {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
vec3_t position;
|
||||
vec3_t direction;
|
||||
rgba_t color;
|
||||
int16_t startFalloff;
|
||||
int16_t endFalloff;
|
||||
int16_t coneAngle;
|
||||
int16_t spreadAngle;
|
||||
} SpotLight;
|
||||
|
||||
|
||||
typedef struct InfiniteLight {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
vec3_t direction;
|
||||
rgba_t color;
|
||||
} InfiniteLight;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// PRIMITIVE FLAGS
|
||||
|
||||
#define PRM_SINGLE_SIDED 0x0001
|
||||
#define PRM_SHIP_ENGINE 0x0002
|
||||
#define PRM_TRANSLUCENT 0x0004
|
||||
|
||||
|
||||
|
||||
#define PRM_TYPE_F3 1
|
||||
#define PRM_TYPE_FT3 2
|
||||
#define PRM_TYPE_F4 3
|
||||
#define PRM_TYPE_FT4 4
|
||||
#define PRM_TYPE_G3 5
|
||||
#define PRM_TYPE_GT3 6
|
||||
#define PRM_TYPE_G4 7
|
||||
#define PRM_TYPE_GT4 8
|
||||
|
||||
#define PRM_TYPE_LF2 9
|
||||
#define PRM_TYPE_TSPR 10
|
||||
#define PRM_TYPE_BSPR 11
|
||||
|
||||
#define PRM_TYPE_LSF3 12
|
||||
#define PRM_TYPE_LSFT3 13
|
||||
#define PRM_TYPE_LSF4 14
|
||||
#define PRM_TYPE_LSFT4 15
|
||||
#define PRM_TYPE_LSG3 16
|
||||
#define PRM_TYPE_LSGT3 17
|
||||
#define PRM_TYPE_LSG4 18
|
||||
#define PRM_TYPE_LSGT4 19
|
||||
|
||||
#define PRM_TYPE_SPLINE 20
|
||||
|
||||
#define PRM_TYPE_INFINITE_LIGHT 21
|
||||
#define PRM_TYPE_POINT_LIGHT 22
|
||||
#define PRM_TYPE_SPOT_LIGHT 23
|
||||
|
||||
|
||||
typedef struct Object {
|
||||
char name[16];
|
||||
|
||||
mat4_t mat;
|
||||
int16_t vertices_len; // Number of Vertices
|
||||
vec3_t *vertices; // Pointer to 3D Points
|
||||
|
||||
int16_t normals_len; // Number of Normals
|
||||
vec3_t *normals; // Pointer to 3D Normals
|
||||
|
||||
int16_t primitives_len; // Number of Primitives
|
||||
Primitive *primitives; // Pointer to Z Sort Primitives
|
||||
|
||||
vec3_t origin;
|
||||
int32_t extent; // Flags for object characteristics
|
||||
int16_t flags; // Next object in list
|
||||
float radius;
|
||||
struct Object *next; // Next object in list
|
||||
} Object;
|
||||
|
||||
typedef union Prm {
|
||||
uint8_t *ptr;
|
||||
int16_t *sptr;
|
||||
int32_t *lptr;
|
||||
Object *object;
|
||||
Primitive *primitive;
|
||||
|
||||
F3 *f3;
|
||||
FT3 *ft3;
|
||||
F4 *f4;
|
||||
FT4 *ft4;
|
||||
G3 *g3;
|
||||
GT3 *gt3;
|
||||
G4 *g4;
|
||||
GT4 *gt4;
|
||||
SPR *spr;
|
||||
Spline *spline;
|
||||
PointLight *pointLight;
|
||||
SpotLight *spotLight;
|
||||
InfiniteLight *infiniteLight;
|
||||
|
||||
LSF3 *lsf3;
|
||||
LSFT3 *lsft3;
|
||||
LSF4 *lsf4;
|
||||
LSFT4 *lsft4;
|
||||
LSG3 *lsg3;
|
||||
LSGT3 *lsgt3;
|
||||
LSG4 *lsg4;
|
||||
LSGT4 *lsgt4;
|
||||
} Prm;
|
||||
|
||||
Object *objects_load(char *name, texture_list_t tl);
|
||||
void object_draw(Object *object, mat4_t *mat);
|
||||
|
||||
#ifndef OBJECT_H
|
||||
#define OBJECT_H
|
||||
|
||||
#include "../types.h"
|
||||
#include "../render.h"
|
||||
#include "../utils.h"
|
||||
#include "image.h"
|
||||
|
||||
// Primitive Structure Stub ( Structure varies with primitive type )
|
||||
|
||||
typedef struct Primitive {
|
||||
int16_t type; // Type of Primitive
|
||||
} Primitive;
|
||||
|
||||
|
||||
typedef struct F3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} F3;
|
||||
|
||||
typedef struct FT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} FT3;
|
||||
|
||||
typedef struct F4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
rgba_t color;
|
||||
} F4;
|
||||
|
||||
typedef struct FT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} FT4;
|
||||
|
||||
typedef struct G3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t pad1;
|
||||
rgba_t color[3];
|
||||
} G3;
|
||||
|
||||
typedef struct GT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
int16_t pad1;
|
||||
rgba_t color[3];
|
||||
} GT3;
|
||||
|
||||
typedef struct G4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
rgba_t color[4];
|
||||
} G4;
|
||||
|
||||
typedef struct GT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
int16_t pad1;
|
||||
rgba_t color[4];
|
||||
} GT4;
|
||||
|
||||
|
||||
|
||||
|
||||
/* LIGHT SOURCED POLYGONS
|
||||
*/
|
||||
|
||||
typedef struct LSF3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
rgba_t color;
|
||||
} LSF3;
|
||||
|
||||
typedef struct LSFT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
rgba_t color;
|
||||
} LSFT3;
|
||||
|
||||
typedef struct LSF4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
int16_t pad1;
|
||||
rgba_t color;
|
||||
} LSF4;
|
||||
|
||||
typedef struct LSFT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normal; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
rgba_t color;
|
||||
} LSFT4;
|
||||
|
||||
typedef struct LSG3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normals[3]; // Indices of the normals
|
||||
rgba_t color[3];
|
||||
} LSG3;
|
||||
|
||||
typedef struct LSGT3 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[3]; // Indices of the coords
|
||||
int16_t normals[3]; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
rgba_t color[3];
|
||||
} LSGT3;
|
||||
|
||||
typedef struct LSG4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normals[4]; // Indices of the normals
|
||||
rgba_t color[4];
|
||||
} LSG4;
|
||||
|
||||
typedef struct LSGT4 {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
int16_t coords[4]; // Indices of the coords
|
||||
int16_t normals[4]; // Indices of the normals
|
||||
int16_t texture;
|
||||
int16_t cba;
|
||||
int16_t tsb;
|
||||
uint8_t u0;
|
||||
uint8_t v0;
|
||||
uint8_t u1;
|
||||
uint8_t v1;
|
||||
uint8_t u2;
|
||||
uint8_t v2;
|
||||
uint8_t u3;
|
||||
uint8_t v3;
|
||||
int16_t pad1;
|
||||
rgba_t color[4];
|
||||
} LSGT4;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* OTHER PRIMITIVE TYPES
|
||||
*/
|
||||
typedef struct SPR {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
int16_t coord;
|
||||
int16_t width;
|
||||
int16_t height;
|
||||
int16_t texture;
|
||||
rgba_t color;
|
||||
} SPR;
|
||||
|
||||
|
||||
typedef struct Spline {
|
||||
int16_t type; // Type of primitive
|
||||
int16_t flag;
|
||||
vec3_t control1;
|
||||
vec3_t position;
|
||||
vec3_t control2;
|
||||
rgba_t color;
|
||||
} Spline;
|
||||
|
||||
|
||||
typedef struct PointLight {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
vec3_t position;
|
||||
rgba_t color;
|
||||
int16_t startFalloff;
|
||||
int16_t endFalloff;
|
||||
} PointLight;
|
||||
|
||||
|
||||
typedef struct SpotLight {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
vec3_t position;
|
||||
vec3_t direction;
|
||||
rgba_t color;
|
||||
int16_t startFalloff;
|
||||
int16_t endFalloff;
|
||||
int16_t coneAngle;
|
||||
int16_t spreadAngle;
|
||||
} SpotLight;
|
||||
|
||||
|
||||
typedef struct InfiniteLight {
|
||||
int16_t type;
|
||||
int16_t flag;
|
||||
vec3_t direction;
|
||||
rgba_t color;
|
||||
} InfiniteLight;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// PRIMITIVE FLAGS
|
||||
|
||||
#define PRM_SINGLE_SIDED 0x0001
|
||||
#define PRM_SHIP_ENGINE 0x0002
|
||||
#define PRM_TRANSLUCENT 0x0004
|
||||
|
||||
|
||||
|
||||
#define PRM_TYPE_F3 1
|
||||
#define PRM_TYPE_FT3 2
|
||||
#define PRM_TYPE_F4 3
|
||||
#define PRM_TYPE_FT4 4
|
||||
#define PRM_TYPE_G3 5
|
||||
#define PRM_TYPE_GT3 6
|
||||
#define PRM_TYPE_G4 7
|
||||
#define PRM_TYPE_GT4 8
|
||||
|
||||
#define PRM_TYPE_LF2 9
|
||||
#define PRM_TYPE_TSPR 10
|
||||
#define PRM_TYPE_BSPR 11
|
||||
|
||||
#define PRM_TYPE_LSF3 12
|
||||
#define PRM_TYPE_LSFT3 13
|
||||
#define PRM_TYPE_LSF4 14
|
||||
#define PRM_TYPE_LSFT4 15
|
||||
#define PRM_TYPE_LSG3 16
|
||||
#define PRM_TYPE_LSGT3 17
|
||||
#define PRM_TYPE_LSG4 18
|
||||
#define PRM_TYPE_LSGT4 19
|
||||
|
||||
#define PRM_TYPE_SPLINE 20
|
||||
|
||||
#define PRM_TYPE_INFINITE_LIGHT 21
|
||||
#define PRM_TYPE_POINT_LIGHT 22
|
||||
#define PRM_TYPE_SPOT_LIGHT 23
|
||||
|
||||
|
||||
typedef struct Object {
|
||||
char name[16];
|
||||
|
||||
mat4_t mat;
|
||||
int16_t vertices_len; // Number of Vertices
|
||||
vec3_t *vertices; // Pointer to 3D Points
|
||||
|
||||
int16_t normals_len; // Number of Normals
|
||||
vec3_t *normals; // Pointer to 3D Normals
|
||||
|
||||
int16_t primitives_len; // Number of Primitives
|
||||
Primitive *primitives; // Pointer to Z Sort Primitives
|
||||
|
||||
vec3_t origin;
|
||||
int32_t extent; // Flags for object characteristics
|
||||
int16_t flags; // Next object in list
|
||||
float radius;
|
||||
struct Object *next; // Next object in list
|
||||
} Object;
|
||||
|
||||
typedef union Prm {
|
||||
uint8_t *ptr;
|
||||
int16_t *sptr;
|
||||
int32_t *lptr;
|
||||
Object *object;
|
||||
Primitive *primitive;
|
||||
|
||||
F3 *f3;
|
||||
FT3 *ft3;
|
||||
F4 *f4;
|
||||
FT4 *ft4;
|
||||
G3 *g3;
|
||||
GT3 *gt3;
|
||||
G4 *g4;
|
||||
GT4 *gt4;
|
||||
SPR *spr;
|
||||
Spline *spline;
|
||||
PointLight *pointLight;
|
||||
SpotLight *spotLight;
|
||||
InfiniteLight *infiniteLight;
|
||||
|
||||
LSF3 *lsf3;
|
||||
LSFT3 *lsft3;
|
||||
LSF4 *lsf4;
|
||||
LSFT4 *lsft4;
|
||||
LSG3 *lsg3;
|
||||
LSGT3 *lsgt3;
|
||||
LSG4 *lsg4;
|
||||
LSGT4 *lsgt4;
|
||||
} Prm;
|
||||
|
||||
Object *objects_load(char *name, texture_list_t tl);
|
||||
void object_draw(Object *object, mat4_t *mat);
|
||||
|
||||
#endif
|
|
@ -1,279 +1,279 @@
|
|||
#include "../mem.h"
|
||||
#include "../input.h"
|
||||
#include "../platform.h"
|
||||
#include "../system.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "scene.h"
|
||||
#include "game.h"
|
||||
#include "hud.h"
|
||||
#include "sfx.h"
|
||||
#include "race.h"
|
||||
#include "particle.h"
|
||||
#include "menu.h"
|
||||
#include "ship_ai.h"
|
||||
#include "ingame_menus.h"
|
||||
|
||||
#define ATTRACT_DURATION 60.0
|
||||
|
||||
static bool is_paused = false;
|
||||
static bool menu_is_scroll_text = false;
|
||||
static bool has_show_credits = false;
|
||||
static float attract_start_time;
|
||||
static menu_t *active_menu = NULL;
|
||||
|
||||
void race_init(void) {
|
||||
ingame_menus_load();
|
||||
menu_is_scroll_text = false;
|
||||
|
||||
const circut_settings_t *cs = &def.circuts[g.circut].settings[g.race_class];
|
||||
track_load(cs->path);
|
||||
scene_load(cs->path, cs->sky_y_offset);
|
||||
|
||||
if (g.circut == CIRCUT_SILVERSTREAM && g.race_class == RACE_CLASS_RAPIER) {
|
||||
scene_init_aurora_borealis();
|
||||
}
|
||||
|
||||
race_start();
|
||||
// render_textures_dump("texture_atlas.png");
|
||||
|
||||
if (g.is_attract_mode) {
|
||||
attract_start_time = system_time();
|
||||
for (int i = 0; i < len(g.ships); i++) {
|
||||
// FIXME: this is needed to initializes the engine sound. Should
|
||||
// maybe be done in a separate step?
|
||||
ship_ai_update_intro(&g.ships[i]);
|
||||
|
||||
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_RACING);
|
||||
}
|
||||
g.pilot = rand_int(0, len(def.pilots));
|
||||
g.camera.update_func = camera_update_attract_random;
|
||||
if (!has_show_credits || rand_int(0, 10) == 0) {
|
||||
active_menu = text_scroll_menu_init(def.credits, len(def.credits));
|
||||
menu_is_scroll_text = true;
|
||||
has_show_credits = true;
|
||||
}
|
||||
}
|
||||
|
||||
is_paused = false;
|
||||
}
|
||||
|
||||
void race_update(void) {
|
||||
if (is_paused) {
|
||||
if (!active_menu) {
|
||||
active_menu = pause_menu_init();
|
||||
}
|
||||
if (input_pressed(A_MENU_QUIT)) {
|
||||
race_unpause();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ships_update();
|
||||
droid_update(&g.droid, &g.ships[g.pilot]);
|
||||
camera_update(&g.camera, &g.ships[g.pilot], &g.droid);
|
||||
weapons_update();
|
||||
particles_update();
|
||||
scene_update();
|
||||
if (g.race_type != RACE_TYPE_TIME_TRIAL) {
|
||||
track_cycle_pickups();
|
||||
}
|
||||
|
||||
if (g.is_attract_mode) {
|
||||
if (input_pressed(A_MENU_START) || input_pressed(A_MENU_SELECT)) {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
float duration = system_time() - attract_start_time;
|
||||
if ((!active_menu && duration > 30) || duration > 120) {
|
||||
game_set_scene(GAME_SCENE_TITLE);
|
||||
}
|
||||
}
|
||||
else if (active_menu == NULL && (input_pressed(A_MENU_START) || input_pressed(A_MENU_QUIT))) {
|
||||
race_pause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Draw 3D
|
||||
render_set_view(g.camera.position, g.camera.angle);
|
||||
|
||||
render_set_cull_backface(false);
|
||||
scene_draw(&g.camera);
|
||||
track_draw(&g.camera);
|
||||
render_set_cull_backface(true);
|
||||
|
||||
ships_draw();
|
||||
droid_draw(&g.droid);
|
||||
weapons_draw();
|
||||
particles_draw();
|
||||
|
||||
// Draw 2d
|
||||
render_set_view_2d();
|
||||
|
||||
if (flags_is(g.ships[g.pilot].flags, SHIP_RACING)) {
|
||||
hud_draw(&g.ships[g.pilot]);
|
||||
}
|
||||
|
||||
if (active_menu) {
|
||||
if (!menu_is_scroll_text) {
|
||||
vec2i_t size = render_size();
|
||||
render_push_2d(vec2i(0, 0), size, rgba(0, 0, 0, 128), RENDER_NO_TEXTURE);
|
||||
}
|
||||
menu_update(active_menu);
|
||||
}
|
||||
}
|
||||
|
||||
void race_start(void) {
|
||||
active_menu = NULL;
|
||||
sfx_reset();
|
||||
scene_init();
|
||||
camera_init(&g.camera, g.track.sections);
|
||||
g.camera.update_func = camera_update_race_intro;
|
||||
ships_init(g.track.sections);
|
||||
droid_init(&g.droid, &g.ships[g.pilot]);
|
||||
particles_init();
|
||||
weapons_init();
|
||||
|
||||
for (int i = 0; i < len(g.race_ranks); i++) {
|
||||
g.race_ranks[i].points = 0;
|
||||
g.race_ranks[i].pilot = i;
|
||||
}
|
||||
for (int i = 0; i < len(g.lap_times); i++) {
|
||||
for (int j = 0; j < len(g.lap_times[i]); j++) {
|
||||
g.lap_times[i][j] = 0;
|
||||
}
|
||||
}
|
||||
g.is_new_race_record = false;
|
||||
g.is_new_lap_record = false;
|
||||
g.best_lap = 0;
|
||||
g.race_time = 0;
|
||||
}
|
||||
|
||||
void race_restart(void) {
|
||||
race_unpause();
|
||||
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
g.lives--;
|
||||
if (g.lives == 0) {
|
||||
race_release_control();
|
||||
active_menu = game_over_menu_init();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
race_start();
|
||||
}
|
||||
|
||||
static bool sort_points_compare(pilot_points_t *pa, pilot_points_t *pb) {
|
||||
return (pa->points < pb->points);
|
||||
}
|
||||
|
||||
void race_end(void) {
|
||||
race_release_control();
|
||||
|
||||
g.race_position = g.ships[g.pilot].position_rank;
|
||||
|
||||
g.race_time = 0;
|
||||
g.best_lap = g.lap_times[g.pilot][0];
|
||||
for (int i = 0; i < NUM_LAPS; i++) {
|
||||
g.race_time += g.lap_times[g.pilot][i];
|
||||
if (g.lap_times[g.pilot][i] < g.best_lap) {
|
||||
g.best_lap = g.lap_times[g.pilot][i];
|
||||
}
|
||||
}
|
||||
|
||||
highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab];
|
||||
if (g.best_lap < hs->lap_record) {
|
||||
hs->lap_record = g.best_lap;
|
||||
g.is_new_lap_record = true;
|
||||
save.is_dirty = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_HIGHSCORES; i++) {
|
||||
if (g.race_time < hs->entries[i].time) {
|
||||
g.is_new_race_record = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
for (int i = 0; i < len(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
|
||||
for (int j = 0; j < len(g.championship_ranks); j++) {
|
||||
if (g.race_ranks[i].pilot == g.championship_ranks[j].pilot) {
|
||||
g.championship_ranks[j].points += def.race_points_for_rank[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort(g.championship_ranks, len(g.championship_ranks), sort_points_compare);
|
||||
}
|
||||
|
||||
active_menu = race_stats_menu_init();
|
||||
}
|
||||
|
||||
void race_next(void) {
|
||||
int next_circut = g.circut + 1;
|
||||
|
||||
// Championship complete
|
||||
if (
|
||||
(save.has_bonus_circuts && next_circut >= NUM_CIRCUTS) ||
|
||||
(!save.has_bonus_circuts && next_circut >= NUM_NON_BONUS_CIRCUTS)
|
||||
) {
|
||||
if (g.race_class == RACE_CLASS_RAPIER) {
|
||||
if (save.has_bonus_circuts) {
|
||||
active_menu = text_scroll_menu_init(def.congratulations.rapier_all_circuts, len(def.congratulations.rapier_all_circuts));
|
||||
}
|
||||
else {
|
||||
save.has_bonus_circuts = true;
|
||||
active_menu = text_scroll_menu_init(def.congratulations.rapier, len(def.congratulations.rapier));
|
||||
}
|
||||
}
|
||||
else {
|
||||
save.has_rapier_class = true;
|
||||
if (save.has_bonus_circuts) {
|
||||
active_menu = text_scroll_menu_init(def.congratulations.venom_all_circuts, len(def.congratulations.venom_all_circuts));
|
||||
}
|
||||
else {
|
||||
active_menu = text_scroll_menu_init(def.congratulations.venom, len(def.congratulations.venom));
|
||||
}
|
||||
}
|
||||
save.is_dirty = true;
|
||||
menu_is_scroll_text = true;
|
||||
}
|
||||
|
||||
// Next track
|
||||
else {
|
||||
g.circut = next_circut;
|
||||
game_set_scene(GAME_SCENE_RACE);
|
||||
}
|
||||
}
|
||||
|
||||
void race_release_control(void) {
|
||||
flags_rm(g.ships[g.pilot].flags, SHIP_RACING);
|
||||
g.ships[g.pilot].remote_thrust_max = 3160;
|
||||
g.ships[g.pilot].remote_thrust_mag = 32;
|
||||
g.ships[g.pilot].speed = 3160;
|
||||
g.camera.update_func = camera_update_attract_random;
|
||||
}
|
||||
|
||||
void race_pause(void) {
|
||||
sfx_pause();
|
||||
is_paused = true;
|
||||
}
|
||||
|
||||
void race_unpause(void) {
|
||||
sfx_unpause();
|
||||
is_paused = false;
|
||||
active_menu = NULL;
|
||||
}
|
||||
#include "../mem.h"
|
||||
#include "../input.h"
|
||||
#include "../platform.h"
|
||||
#include "../system.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "scene.h"
|
||||
#include "game.h"
|
||||
#include "hud.h"
|
||||
#include "sfx.h"
|
||||
#include "race.h"
|
||||
#include "particle.h"
|
||||
#include "menu.h"
|
||||
#include "ship_ai.h"
|
||||
#include "ingame_menus.h"
|
||||
|
||||
#define ATTRACT_DURATION 60.0
|
||||
|
||||
static bool is_paused = false;
|
||||
static bool menu_is_scroll_text = false;
|
||||
static bool has_show_credits = false;
|
||||
static float attract_start_time;
|
||||
static menu_t *active_menu = NULL;
|
||||
|
||||
void race_init(void) {
|
||||
ingame_menus_load();
|
||||
menu_is_scroll_text = false;
|
||||
|
||||
const circut_settings_t *cs = &def.circuts[g.circut].settings[g.race_class];
|
||||
track_load(cs->path);
|
||||
scene_load(cs->path, cs->sky_y_offset);
|
||||
|
||||
if (g.circut == CIRCUT_SILVERSTREAM && g.race_class == RACE_CLASS_RAPIER) {
|
||||
scene_init_aurora_borealis();
|
||||
}
|
||||
|
||||
race_start();
|
||||
// render_textures_dump("texture_atlas.png");
|
||||
|
||||
if (g.is_attract_mode) {
|
||||
attract_start_time = system_time();
|
||||
for (int i = 0; i < len(g.ships); i++) {
|
||||
// FIXME: this is needed to initializes the engine sound. Should
|
||||
// maybe be done in a separate step?
|
||||
ship_ai_update_intro(&g.ships[i]);
|
||||
|
||||
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_RACING);
|
||||
}
|
||||
g.pilot = rand_int(0, len(def.pilots));
|
||||
g.camera.update_func = camera_update_attract_random;
|
||||
if (!has_show_credits || rand_int(0, 10) == 0) {
|
||||
active_menu = text_scroll_menu_init(def.credits, len(def.credits));
|
||||
menu_is_scroll_text = true;
|
||||
has_show_credits = true;
|
||||
}
|
||||
}
|
||||
|
||||
is_paused = false;
|
||||
}
|
||||
|
||||
void race_update(void) {
|
||||
if (is_paused) {
|
||||
if (!active_menu) {
|
||||
active_menu = pause_menu_init();
|
||||
}
|
||||
if (input_pressed(A_MENU_QUIT)) {
|
||||
race_unpause();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ships_update();
|
||||
droid_update(&g.droid, &g.ships[g.pilot]);
|
||||
camera_update(&g.camera, &g.ships[g.pilot], &g.droid);
|
||||
weapons_update();
|
||||
particles_update();
|
||||
scene_update();
|
||||
if (g.race_type != RACE_TYPE_TIME_TRIAL) {
|
||||
track_cycle_pickups();
|
||||
}
|
||||
|
||||
if (g.is_attract_mode) {
|
||||
if (input_pressed(A_MENU_START) || input_pressed(A_MENU_SELECT)) {
|
||||
game_set_scene(GAME_SCENE_MAIN_MENU);
|
||||
}
|
||||
float duration = system_time() - attract_start_time;
|
||||
if ((!active_menu && duration > 30) || duration > 120) {
|
||||
game_set_scene(GAME_SCENE_TITLE);
|
||||
}
|
||||
}
|
||||
else if (active_menu == NULL && (input_pressed(A_MENU_START) || input_pressed(A_MENU_QUIT))) {
|
||||
race_pause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Draw 3D
|
||||
render_set_view(g.camera.position, g.camera.angle);
|
||||
|
||||
render_set_cull_backface(false);
|
||||
scene_draw(&g.camera);
|
||||
track_draw(&g.camera);
|
||||
render_set_cull_backface(true);
|
||||
|
||||
ships_draw();
|
||||
droid_draw(&g.droid);
|
||||
weapons_draw();
|
||||
particles_draw();
|
||||
|
||||
// Draw 2d
|
||||
render_set_view_2d();
|
||||
|
||||
if (flags_is(g.ships[g.pilot].flags, SHIP_RACING)) {
|
||||
hud_draw(&g.ships[g.pilot]);
|
||||
}
|
||||
|
||||
if (active_menu) {
|
||||
if (!menu_is_scroll_text) {
|
||||
vec2i_t size = render_size();
|
||||
render_push_2d(vec2i(0, 0), size, rgba(0, 0, 0, 128), RENDER_NO_TEXTURE);
|
||||
}
|
||||
menu_update(active_menu);
|
||||
}
|
||||
}
|
||||
|
||||
void race_start(void) {
|
||||
active_menu = NULL;
|
||||
sfx_reset();
|
||||
scene_init();
|
||||
camera_init(&g.camera, g.track.sections);
|
||||
g.camera.update_func = camera_update_race_intro;
|
||||
ships_init(g.track.sections);
|
||||
droid_init(&g.droid, &g.ships[g.pilot]);
|
||||
particles_init();
|
||||
weapons_init();
|
||||
|
||||
for (int i = 0; i < len(g.race_ranks); i++) {
|
||||
g.race_ranks[i].points = 0;
|
||||
g.race_ranks[i].pilot = i;
|
||||
}
|
||||
for (int i = 0; i < len(g.lap_times); i++) {
|
||||
for (int j = 0; j < len(g.lap_times[i]); j++) {
|
||||
g.lap_times[i][j] = 0;
|
||||
}
|
||||
}
|
||||
g.is_new_race_record = false;
|
||||
g.is_new_lap_record = false;
|
||||
g.best_lap = 0;
|
||||
g.race_time = 0;
|
||||
}
|
||||
|
||||
void race_restart(void) {
|
||||
race_unpause();
|
||||
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
g.lives--;
|
||||
if (g.lives == 0) {
|
||||
race_release_control();
|
||||
active_menu = game_over_menu_init();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
race_start();
|
||||
}
|
||||
|
||||
static bool sort_points_compare(pilot_points_t *pa, pilot_points_t *pb) {
|
||||
return (pa->points < pb->points);
|
||||
}
|
||||
|
||||
void race_end(void) {
|
||||
race_release_control();
|
||||
|
||||
g.race_position = g.ships[g.pilot].position_rank;
|
||||
|
||||
g.race_time = 0;
|
||||
g.best_lap = g.lap_times[g.pilot][0];
|
||||
for (int i = 0; i < NUM_LAPS; i++) {
|
||||
g.race_time += g.lap_times[g.pilot][i];
|
||||
if (g.lap_times[g.pilot][i] < g.best_lap) {
|
||||
g.best_lap = g.lap_times[g.pilot][i];
|
||||
}
|
||||
}
|
||||
|
||||
highscores_t *hs = &save.highscores[g.race_class][g.circut][g.highscore_tab];
|
||||
if (g.best_lap < hs->lap_record) {
|
||||
hs->lap_record = g.best_lap;
|
||||
g.is_new_lap_record = true;
|
||||
save.is_dirty = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_HIGHSCORES; i++) {
|
||||
if (g.race_time < hs->entries[i].time) {
|
||||
g.is_new_race_record = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g.race_type == RACE_TYPE_CHAMPIONSHIP) {
|
||||
for (int i = 0; i < len(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
|
||||
for (int j = 0; j < len(g.championship_ranks); j++) {
|
||||
if (g.race_ranks[i].pilot == g.championship_ranks[j].pilot) {
|
||||
g.championship_ranks[j].points += def.race_points_for_rank[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort(g.championship_ranks, len(g.championship_ranks), sort_points_compare);
|
||||
}
|
||||
|
||||
active_menu = race_stats_menu_init();
|
||||
}
|
||||
|
||||
void race_next(void) {
|
||||
int next_circut = g.circut + 1;
|
||||
|
||||
// Championship complete
|
||||
if (
|
||||
(save.has_bonus_circuts && next_circut >= NUM_CIRCUTS) ||
|
||||
(!save.has_bonus_circuts && next_circut >= NUM_NON_BONUS_CIRCUTS)
|
||||
) {
|
||||
if (g.race_class == RACE_CLASS_RAPIER) {
|
||||
if (save.has_bonus_circuts) {
|
||||
active_menu = text_scroll_menu_init(def.congratulations.rapier_all_circuts, len(def.congratulations.rapier_all_circuts));
|
||||
}
|
||||
else {
|
||||
save.has_bonus_circuts = true;
|
||||
active_menu = text_scroll_menu_init(def.congratulations.rapier, len(def.congratulations.rapier));
|
||||
}
|
||||
}
|
||||
else {
|
||||
save.has_rapier_class = true;
|
||||
if (save.has_bonus_circuts) {
|
||||
active_menu = text_scroll_menu_init(def.congratulations.venom_all_circuts, len(def.congratulations.venom_all_circuts));
|
||||
}
|
||||
else {
|
||||
active_menu = text_scroll_menu_init(def.congratulations.venom, len(def.congratulations.venom));
|
||||
}
|
||||
}
|
||||
save.is_dirty = true;
|
||||
menu_is_scroll_text = true;
|
||||
}
|
||||
|
||||
// Next track
|
||||
else {
|
||||
g.circut = next_circut;
|
||||
game_set_scene(GAME_SCENE_RACE);
|
||||
}
|
||||
}
|
||||
|
||||
void race_release_control(void) {
|
||||
flags_rm(g.ships[g.pilot].flags, SHIP_RACING);
|
||||
g.ships[g.pilot].remote_thrust_max = 3160;
|
||||
g.ships[g.pilot].remote_thrust_mag = 32;
|
||||
g.ships[g.pilot].speed = 3160;
|
||||
g.camera.update_func = camera_update_attract_random;
|
||||
}
|
||||
|
||||
void race_pause(void) {
|
||||
sfx_pause();
|
||||
is_paused = true;
|
||||
}
|
||||
|
||||
void race_unpause(void) {
|
||||
sfx_unpause();
|
||||
is_paused = false;
|
||||
active_menu = NULL;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#ifndef RACE_H
|
||||
#define RACE_H
|
||||
|
||||
void race_init(void);
|
||||
void race_update(void);
|
||||
void race_start(void);
|
||||
void race_restart(void);
|
||||
void race_pause(void);
|
||||
void race_unpause(void);
|
||||
void race_end(void);
|
||||
void race_next(void);
|
||||
void race_release_control(void);
|
||||
|
||||
#endif
|
||||
#ifndef RACE_H
|
||||
#define RACE_H
|
||||
|
||||
void race_init(void);
|
||||
void race_update(void);
|
||||
void race_start(void);
|
||||
void race_restart(void);
|
||||
void race_pause(void);
|
||||
void race_unpause(void);
|
||||
void race_end(void);
|
||||
void race_next(void);
|
||||
void race_release_control(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,266 +1,266 @@
|
|||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../system.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "scene.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "game.h"
|
||||
|
||||
|
||||
#define SCENE_START_BOOMS_MAX 4
|
||||
#define SCENE_OIL_PUMPS_MAX 2
|
||||
#define SCENE_RED_LIGHTS_MAX 4
|
||||
#define SCENE_STANDS_MAX 20
|
||||
|
||||
static Object *scene_objects;
|
||||
static Object *sky_object;
|
||||
static vec3_t sky_offset;
|
||||
|
||||
static Object *start_booms[SCENE_START_BOOMS_MAX];
|
||||
static int start_booms_len;
|
||||
|
||||
static Object *oil_pumps[SCENE_OIL_PUMPS_MAX];
|
||||
static int oil_pumps_len;
|
||||
|
||||
static Object *red_lights[SCENE_RED_LIGHTS_MAX];
|
||||
static int red_lights_len;
|
||||
|
||||
typedef struct {
|
||||
sfx_t *sfx;
|
||||
vec3_t pos;
|
||||
} scene_stand_t;
|
||||
static scene_stand_t stands[SCENE_STANDS_MAX];
|
||||
static int stands_len;
|
||||
|
||||
static struct {
|
||||
bool enabled;
|
||||
GT4 *primitives[80];
|
||||
int16_t *coords[80];
|
||||
int16_t grey_coords[80];
|
||||
} aurora_borealis;
|
||||
|
||||
void scene_pulsate_red_light(Object *obj);
|
||||
void scene_move_oil_pump(Object *obj);
|
||||
void scene_update_aurora_borealis(void);
|
||||
|
||||
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"));
|
||||
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"));
|
||||
sky_object = objects_load(get_path(base_path, "sky.prm") , sky_textures);
|
||||
sky_offset = vec3(0, sky_y_offset, 0);
|
||||
|
||||
// Collect all objects that need to be updated each frame
|
||||
start_booms_len = 0;
|
||||
oil_pumps_len = 0;
|
||||
red_lights_len = 0;
|
||||
stands_len = 0;
|
||||
|
||||
Object *obj = scene_objects;
|
||||
while (obj) {
|
||||
mat4_set_translation(&obj->mat, obj->origin);
|
||||
|
||||
if (str_starts_with(obj->name, "start")) {
|
||||
error_if(start_booms_len >= SCENE_START_BOOMS_MAX, "SCENE_START_BOOMS_MAX reached");
|
||||
start_booms[start_booms_len++] = obj;
|
||||
}
|
||||
else if (str_starts_with(obj->name, "redl")) {
|
||||
error_if(red_lights_len >= SCENE_RED_LIGHTS_MAX, "SCENE_RED_LIGHTS_MAX reached");
|
||||
red_lights[red_lights_len++] = obj;
|
||||
}
|
||||
else if (str_starts_with(obj->name, "donkey")) {
|
||||
error_if(oil_pumps_len >= SCENE_OIL_PUMPS_MAX, "SCENE_OIL_PUMPS_MAX reached");
|
||||
oil_pumps[oil_pumps_len++] = obj;
|
||||
}
|
||||
else if (
|
||||
str_starts_with(obj->name, "lostad") ||
|
||||
str_starts_with(obj->name, "stad_") ||
|
||||
str_starts_with(obj->name, "newstad_")
|
||||
) {
|
||||
error_if(stands_len >= SCENE_STANDS_MAX, "SCENE_STANDS_MAX reached");
|
||||
stands[stands_len++] = (scene_stand_t){.sfx = NULL, .pos = obj->origin};
|
||||
}
|
||||
obj = obj->next;
|
||||
}
|
||||
|
||||
aurora_borealis.enabled = false;
|
||||
}
|
||||
|
||||
void scene_init(void) {
|
||||
scene_set_start_booms(0);
|
||||
for (int i = 0; i < stands_len; i++) {
|
||||
stands[i].sfx = sfx_reserve_loop(SFX_CROWD);
|
||||
}
|
||||
}
|
||||
|
||||
void scene_update(void) {
|
||||
for (int i = 0; i < red_lights_len; i++) {
|
||||
scene_pulsate_red_light(red_lights[i]);
|
||||
}
|
||||
for (int i = 0; i < oil_pumps_len; i++) {
|
||||
scene_move_oil_pump(oil_pumps[i]);
|
||||
}
|
||||
for (int i = 0; i < stands_len; i++) {
|
||||
sfx_set_position(stands[i].sfx, stands[i].pos, vec3(0, 0, 0), 0.4);
|
||||
}
|
||||
|
||||
if (aurora_borealis.enabled) {
|
||||
scene_update_aurora_borealis();
|
||||
}
|
||||
}
|
||||
|
||||
void scene_draw(camera_t *camera) {
|
||||
// Sky
|
||||
render_set_depth_write(false);
|
||||
mat4_set_translation(&sky_object->mat, vec3_add(camera->position, sky_offset));
|
||||
object_draw(sky_object, &sky_object->mat);
|
||||
render_set_depth_write(true);
|
||||
|
||||
// Objects
|
||||
|
||||
// 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.
|
||||
vec3_t cam_pos = camera->position;
|
||||
vec3_t cam_dir = camera_forward(camera);
|
||||
Object *object = scene_objects;
|
||||
|
||||
while (object) {
|
||||
vec3_t diff = vec3_sub(cam_pos, object->origin);
|
||||
float cam_dot = vec3_dot(diff, cam_dir);
|
||||
float dist_sq = vec3_dot(diff, diff);
|
||||
if (
|
||||
cam_dot < object->radius &&
|
||||
dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR)
|
||||
) {
|
||||
object_draw(object, &object->mat);
|
||||
}
|
||||
object = object->next;
|
||||
}
|
||||
}
|
||||
|
||||
void scene_set_start_booms(int light_index) {
|
||||
|
||||
int lights_len = 1;
|
||||
rgba_t color = rgba(0, 0, 0, 0);
|
||||
|
||||
if (light_index == 0) { // reset all 3
|
||||
lights_len = 3;
|
||||
color = rgba(0x20, 0x20, 0x20, 0xff);
|
||||
}
|
||||
else if (light_index == 1) {
|
||||
color = rgba(0xff, 0x00, 0x00, 0xff);
|
||||
}
|
||||
else if (light_index == 2) {
|
||||
color = rgba(0xff, 0x80, 0x00, 0xff);
|
||||
}
|
||||
else if (light_index == 3) {
|
||||
color = rgba(0x00, 0xff, 0x00, 0xff);
|
||||
}
|
||||
|
||||
for (int i = 0; i < start_booms_len; i++) {
|
||||
Prm libPoly = {.primitive = start_booms[i]->primitives};
|
||||
|
||||
for (int j = 1; j < light_index; j++) {
|
||||
libPoly.gt4 += 1;
|
||||
}
|
||||
|
||||
for (int j = 0; j < lights_len; j++) {
|
||||
for (int v = 0; v < 4; v++) {
|
||||
libPoly.gt4->color[v].r = color.r;
|
||||
libPoly.gt4->color[v].g = color.g;
|
||||
libPoly.gt4->color[v].b = color.b;
|
||||
}
|
||||
libPoly.gt4 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void scene_pulsate_red_light(Object *obj) {
|
||||
uint8_t r = clamp(sin(system_cycle_time() * M_PI * 2) * 128 + 128, 0, 255);
|
||||
Prm libPoly = {.primitive = obj->primitives};
|
||||
|
||||
for (int v = 0; v < 4; v++) {
|
||||
libPoly.gt4->color[v].r = r;
|
||||
libPoly.gt4->color[v].g = 0x00;
|
||||
libPoly.gt4->color[v].b = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void scene_init_aurora_borealis(void) {
|
||||
aurora_borealis.enabled = true;
|
||||
clear(aurora_borealis.grey_coords);
|
||||
|
||||
int count = 0;
|
||||
int16_t *coords;
|
||||
float y;
|
||||
|
||||
Prm poly = {.primitive = sky_object->primitives};
|
||||
for (int i = 0; i < sky_object->primitives_len; i++) {
|
||||
switch (poly.primitive->type) {
|
||||
case PRM_TYPE_GT3:
|
||||
poly.gt3 += 1;
|
||||
break;
|
||||
case PRM_TYPE_GT4:
|
||||
coords = poly.gt4->coords;
|
||||
y = sky_object->vertices[coords[0]].y;
|
||||
if (y < -6000) { // -8000
|
||||
aurora_borealis.primitives[count] = poly.gt4;
|
||||
if (y > -6800) {
|
||||
aurora_borealis.coords[count] = poly.gt4->coords;
|
||||
aurora_borealis.grey_coords[count] = -1;
|
||||
}
|
||||
else if (y < -11000) {
|
||||
aurora_borealis.coords[count] = poly.gt4->coords;
|
||||
aurora_borealis.grey_coords[count] = -2;
|
||||
}
|
||||
else {
|
||||
aurora_borealis.coords[count] = poly.gt4->coords;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
poly.gt4 += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scene_update_aurora_borealis(void) {
|
||||
float phase = system_time() / 30.0;
|
||||
for (int i = 0; i < 80; i++) {
|
||||
int16_t *coords = aurora_borealis.coords[i];
|
||||
|
||||
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].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;
|
||||
}
|
||||
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].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;
|
||||
}
|
||||
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].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;
|
||||
}
|
||||
|
||||
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].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;
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../system.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "ship.h"
|
||||
#include "weapon.h"
|
||||
#include "scene.h"
|
||||
#include "droid.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "game.h"
|
||||
|
||||
|
||||
#define SCENE_START_BOOMS_MAX 4
|
||||
#define SCENE_OIL_PUMPS_MAX 2
|
||||
#define SCENE_RED_LIGHTS_MAX 4
|
||||
#define SCENE_STANDS_MAX 20
|
||||
|
||||
static Object *scene_objects;
|
||||
static Object *sky_object;
|
||||
static vec3_t sky_offset;
|
||||
|
||||
static Object *start_booms[SCENE_START_BOOMS_MAX];
|
||||
static int start_booms_len;
|
||||
|
||||
static Object *oil_pumps[SCENE_OIL_PUMPS_MAX];
|
||||
static int oil_pumps_len;
|
||||
|
||||
static Object *red_lights[SCENE_RED_LIGHTS_MAX];
|
||||
static int red_lights_len;
|
||||
|
||||
typedef struct {
|
||||
sfx_t *sfx;
|
||||
vec3_t pos;
|
||||
} scene_stand_t;
|
||||
static scene_stand_t stands[SCENE_STANDS_MAX];
|
||||
static int stands_len;
|
||||
|
||||
static struct {
|
||||
bool enabled;
|
||||
GT4 *primitives[80];
|
||||
int16_t *coords[80];
|
||||
int16_t grey_coords[80];
|
||||
} aurora_borealis;
|
||||
|
||||
void scene_pulsate_red_light(Object *obj);
|
||||
void scene_move_oil_pump(Object *obj);
|
||||
void scene_update_aurora_borealis(void);
|
||||
|
||||
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"));
|
||||
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"));
|
||||
sky_object = objects_load(get_path(base_path, "sky.prm") , sky_textures);
|
||||
sky_offset = vec3(0, sky_y_offset, 0);
|
||||
|
||||
// Collect all objects that need to be updated each frame
|
||||
start_booms_len = 0;
|
||||
oil_pumps_len = 0;
|
||||
red_lights_len = 0;
|
||||
stands_len = 0;
|
||||
|
||||
Object *obj = scene_objects;
|
||||
while (obj) {
|
||||
mat4_set_translation(&obj->mat, obj->origin);
|
||||
|
||||
if (str_starts_with(obj->name, "start")) {
|
||||
error_if(start_booms_len >= SCENE_START_BOOMS_MAX, "SCENE_START_BOOMS_MAX reached");
|
||||
start_booms[start_booms_len++] = obj;
|
||||
}
|
||||
else if (str_starts_with(obj->name, "redl")) {
|
||||
error_if(red_lights_len >= SCENE_RED_LIGHTS_MAX, "SCENE_RED_LIGHTS_MAX reached");
|
||||
red_lights[red_lights_len++] = obj;
|
||||
}
|
||||
else if (str_starts_with(obj->name, "donkey")) {
|
||||
error_if(oil_pumps_len >= SCENE_OIL_PUMPS_MAX, "SCENE_OIL_PUMPS_MAX reached");
|
||||
oil_pumps[oil_pumps_len++] = obj;
|
||||
}
|
||||
else if (
|
||||
str_starts_with(obj->name, "lostad") ||
|
||||
str_starts_with(obj->name, "stad_") ||
|
||||
str_starts_with(obj->name, "newstad_")
|
||||
) {
|
||||
error_if(stands_len >= SCENE_STANDS_MAX, "SCENE_STANDS_MAX reached");
|
||||
stands[stands_len++] = (scene_stand_t){.sfx = NULL, .pos = obj->origin};
|
||||
}
|
||||
obj = obj->next;
|
||||
}
|
||||
|
||||
aurora_borealis.enabled = false;
|
||||
}
|
||||
|
||||
void scene_init(void) {
|
||||
scene_set_start_booms(0);
|
||||
for (int i = 0; i < stands_len; i++) {
|
||||
stands[i].sfx = sfx_reserve_loop(SFX_CROWD);
|
||||
}
|
||||
}
|
||||
|
||||
void scene_update(void) {
|
||||
for (int i = 0; i < red_lights_len; i++) {
|
||||
scene_pulsate_red_light(red_lights[i]);
|
||||
}
|
||||
for (int i = 0; i < oil_pumps_len; i++) {
|
||||
scene_move_oil_pump(oil_pumps[i]);
|
||||
}
|
||||
for (int i = 0; i < stands_len; i++) {
|
||||
sfx_set_position(stands[i].sfx, stands[i].pos, vec3(0, 0, 0), 0.4);
|
||||
}
|
||||
|
||||
if (aurora_borealis.enabled) {
|
||||
scene_update_aurora_borealis();
|
||||
}
|
||||
}
|
||||
|
||||
void scene_draw(camera_t *camera) {
|
||||
// Sky
|
||||
render_set_depth_write(false);
|
||||
mat4_set_translation(&sky_object->mat, vec3_add(camera->position, sky_offset));
|
||||
object_draw(sky_object, &sky_object->mat);
|
||||
render_set_depth_write(true);
|
||||
|
||||
// Objects
|
||||
|
||||
// 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.
|
||||
vec3_t cam_pos = camera->position;
|
||||
vec3_t cam_dir = camera_forward(camera);
|
||||
Object *object = scene_objects;
|
||||
|
||||
while (object) {
|
||||
vec3_t diff = vec3_sub(cam_pos, object->origin);
|
||||
float cam_dot = vec3_dot(diff, cam_dir);
|
||||
float dist_sq = vec3_dot(diff, diff);
|
||||
if (
|
||||
cam_dot < object->radius &&
|
||||
dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR)
|
||||
) {
|
||||
object_draw(object, &object->mat);
|
||||
}
|
||||
object = object->next;
|
||||
}
|
||||
}
|
||||
|
||||
void scene_set_start_booms(int light_index) {
|
||||
|
||||
int lights_len = 1;
|
||||
rgba_t color = rgba(0, 0, 0, 0);
|
||||
|
||||
if (light_index == 0) { // reset all 3
|
||||
lights_len = 3;
|
||||
color = rgba(0x20, 0x20, 0x20, 0xff);
|
||||
}
|
||||
else if (light_index == 1) {
|
||||
color = rgba(0xff, 0x00, 0x00, 0xff);
|
||||
}
|
||||
else if (light_index == 2) {
|
||||
color = rgba(0xff, 0x80, 0x00, 0xff);
|
||||
}
|
||||
else if (light_index == 3) {
|
||||
color = rgba(0x00, 0xff, 0x00, 0xff);
|
||||
}
|
||||
|
||||
for (int i = 0; i < start_booms_len; i++) {
|
||||
Prm libPoly = {.primitive = start_booms[i]->primitives};
|
||||
|
||||
for (int j = 1; j < light_index; j++) {
|
||||
libPoly.gt4 += 1;
|
||||
}
|
||||
|
||||
for (int j = 0; j < lights_len; j++) {
|
||||
for (int v = 0; v < 4; v++) {
|
||||
libPoly.gt4->color[v].r = color.r;
|
||||
libPoly.gt4->color[v].g = color.g;
|
||||
libPoly.gt4->color[v].b = color.b;
|
||||
}
|
||||
libPoly.gt4 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void scene_pulsate_red_light(Object *obj) {
|
||||
uint8_t r = clamp(sin(system_cycle_time() * M_PI * 2) * 128 + 128, 0, 255);
|
||||
Prm libPoly = {.primitive = obj->primitives};
|
||||
|
||||
for (int v = 0; v < 4; v++) {
|
||||
libPoly.gt4->color[v].r = r;
|
||||
libPoly.gt4->color[v].g = 0x00;
|
||||
libPoly.gt4->color[v].b = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void scene_init_aurora_borealis(void) {
|
||||
aurora_borealis.enabled = true;
|
||||
clear(aurora_borealis.grey_coords);
|
||||
|
||||
int count = 0;
|
||||
int16_t *coords;
|
||||
float y;
|
||||
|
||||
Prm poly = {.primitive = sky_object->primitives};
|
||||
for (int i = 0; i < sky_object->primitives_len; i++) {
|
||||
switch (poly.primitive->type) {
|
||||
case PRM_TYPE_GT3:
|
||||
poly.gt3 += 1;
|
||||
break;
|
||||
case PRM_TYPE_GT4:
|
||||
coords = poly.gt4->coords;
|
||||
y = sky_object->vertices[coords[0]].y;
|
||||
if (y < -6000) { // -8000
|
||||
aurora_borealis.primitives[count] = poly.gt4;
|
||||
if (y > -6800) {
|
||||
aurora_borealis.coords[count] = poly.gt4->coords;
|
||||
aurora_borealis.grey_coords[count] = -1;
|
||||
}
|
||||
else if (y < -11000) {
|
||||
aurora_borealis.coords[count] = poly.gt4->coords;
|
||||
aurora_borealis.grey_coords[count] = -2;
|
||||
}
|
||||
else {
|
||||
aurora_borealis.coords[count] = poly.gt4->coords;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
poly.gt4 += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scene_update_aurora_borealis(void) {
|
||||
float phase = system_time() / 30.0;
|
||||
for (int i = 0; i < 80; i++) {
|
||||
int16_t *coords = aurora_borealis.coords[i];
|
||||
|
||||
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].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;
|
||||
}
|
||||
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].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;
|
||||
}
|
||||
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].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;
|
||||
}
|
||||
|
||||
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].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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#ifndef SCENE_H
|
||||
#define SCENE_H
|
||||
|
||||
#include "image.h"
|
||||
#include "camera.h"
|
||||
|
||||
void scene_load(const char *path, float sky_y_offset);
|
||||
void scene_draw(camera_t *camera);
|
||||
void scene_init(void);
|
||||
void scene_set_start_booms(int num_lights);
|
||||
void scene_init_aurora_borealis(void);
|
||||
void scene_update(void);
|
||||
|
||||
#endif
|
||||
#ifndef SCENE_H
|
||||
#define SCENE_H
|
||||
|
||||
#include "image.h"
|
||||
#include "camera.h"
|
||||
|
||||
void scene_load(const char *path, float sky_y_offset);
|
||||
void scene_draw(camera_t *camera);
|
||||
void scene_init(void);
|
||||
void scene_set_start_booms(int num_lights);
|
||||
void scene_init_aurora_borealis(void);
|
||||
void scene_update(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,374 +1,374 @@
|
|||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../render.h"
|
||||
#include "../system.h"
|
||||
#include "../platform.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "game.h"
|
||||
|
||||
void track_load(const char *base_path) {
|
||||
// Load and assemble high res track tiles
|
||||
|
||||
g.track.textures.start = render_textures_len();
|
||||
g.track.textures.len = 0;
|
||||
|
||||
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"));
|
||||
|
||||
image_t *temp_tile = image_alloc(128, 128);
|
||||
for (int i = 0; i < ttf->len; i++) {
|
||||
for (int tx = 0; tx < 4; tx++) {
|
||||
for (int ty = 0; ty < 4; ty++) {
|
||||
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_copy(sub_tile, temp_tile, 0, 0, 32, 32, tx * 32, ty * 32);
|
||||
mem_temp_free(sub_tile);
|
||||
}
|
||||
}
|
||||
render_texture_create(temp_tile->width, temp_tile->height, temp_tile->pixels);
|
||||
g.track.textures.len++;
|
||||
}
|
||||
|
||||
mem_temp_free(temp_tile);
|
||||
mem_temp_free(cmp);
|
||||
mem_temp_free(ttf);
|
||||
|
||||
vec3_t *vertices = track_load_vertices(get_path(base_path, "track.trv"));
|
||||
track_load_faces(get_path(base_path, "track.trf"), vertices);
|
||||
mem_temp_free(vertices);
|
||||
|
||||
track_load_sections(get_path(base_path, "track.trs"));
|
||||
|
||||
g.track.pickups_len = 0;
|
||||
section_t *s = g.track.sections;
|
||||
section_t *j = NULL;
|
||||
|
||||
// Nummerate all sections; take care to give both stretches at a junction
|
||||
// the same numbers.
|
||||
int num = 0;
|
||||
do {
|
||||
s->num = num++;
|
||||
if (s->junction) { // start junction
|
||||
j = s->junction;
|
||||
do {
|
||||
j->num = num++;
|
||||
j = j->next;
|
||||
} while (!j->junction); // end junction
|
||||
num = s->num;
|
||||
}
|
||||
s = s->next;
|
||||
} while (s != g.track.sections);
|
||||
g.track.total_section_nums = num;
|
||||
|
||||
g.track.pickups = mem_mark();
|
||||
for (int i = 0; i < g.track.section_count; i++) {
|
||||
track_face_t *face = track_section_get_base_face(&g.track.sections[i]);
|
||||
|
||||
for (int f = 0; f < 2; f++) {
|
||||
if (flags_any(face->flags, FACE_PICKUP_RIGHT | FACE_PICKUP_LEFT)) {
|
||||
mem_bump(sizeof(track_pickup_t));
|
||||
g.track.pickups[g.track.pickups_len].face = face;
|
||||
g.track.pickups[g.track.pickups_len].cooldown_timer = 0;
|
||||
g.track.pickups_len++;
|
||||
}
|
||||
|
||||
if (flags_is(face->flags, FACE_BOOST)) {
|
||||
track_face_set_color(face, rgba(0, 0, 255, 255));
|
||||
}
|
||||
face++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttf_t *track_load_tile_format(char *ttf_name) {
|
||||
uint32_t ttf_size;
|
||||
uint8_t *ttf_bytes = platform_load_asset(ttf_name, &ttf_size);
|
||||
|
||||
uint32_t p = 0;
|
||||
uint32_t num_tiles = ttf_size / 42;
|
||||
|
||||
ttf_t *ttf = mem_temp_alloc(sizeof(ttf_t) + sizeof(ttf_tile_t) * num_tiles);
|
||||
ttf->len = num_tiles;
|
||||
|
||||
for (int t = 0; t < num_tiles; t++) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
ttf->tiles[t].near[i] = get_i16(ttf_bytes, &p);
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ttf->tiles[t].med[i] = get_i16(ttf_bytes, &p);
|
||||
}
|
||||
ttf->tiles[t].far = get_i16(ttf_bytes, &p);
|
||||
}
|
||||
mem_temp_free(ttf_bytes);
|
||||
|
||||
return ttf;
|
||||
}
|
||||
|
||||
bool track_collect_pickups(track_face_t *face) {
|
||||
if (flags_is(face->flags, FACE_PICKUP_ACTIVE)) {
|
||||
flags_rm(face->flags, FACE_PICKUP_ACTIVE);
|
||||
flags_add(face->flags, FACE_PICKUP_COLLECTED);
|
||||
track_face_set_color(face, rgba(255, 255, 255, 255));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vec3_t *track_load_vertices(char *file_name) {
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(file_name, &size);
|
||||
|
||||
g.track.vertex_count = size / 16; // VECTOR_SIZE
|
||||
vec3_t *vertices = mem_temp_alloc(sizeof(vec3_t) * g.track.vertex_count);
|
||||
|
||||
uint32_t p = 0;
|
||||
for (int i = 0; i < g.track.vertex_count; i++) {
|
||||
vertices[i].x = get_i32(bytes, &p);
|
||||
vertices[i].y = get_i32(bytes, &p);
|
||||
vertices[i].z = get_i32(bytes, &p);
|
||||
p += 4; // padding
|
||||
}
|
||||
|
||||
mem_temp_free(bytes);
|
||||
return vertices;
|
||||
}
|
||||
|
||||
static const vec2_t track_uv[2][4] = {
|
||||
{{128, 0}, { 0, 0}, { 0, 128}, {128, 128}},
|
||||
{{ 0, 0}, {128, 0}, {128, 128}, { 0, 128}}
|
||||
};
|
||||
|
||||
void track_load_faces(char *file_name, vec3_t *vertices) {
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(file_name, &size);
|
||||
|
||||
g.track.face_count = size / 20; // TRACK_FACE_DATA_SIZE
|
||||
g.track.faces = mem_bump(sizeof(track_face_t) * g.track.face_count);
|
||||
|
||||
uint32_t p = 0;
|
||||
track_face_t *tf = g.track.faces;
|
||||
|
||||
|
||||
for (int i = 0; i < g.track.face_count; i++) {
|
||||
|
||||
vec3_t v0 = vertices[get_i16(bytes, &p)];
|
||||
vec3_t v1 = vertices[get_i16(bytes, &p)];
|
||||
vec3_t v2 = 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.y = (float)get_i16(bytes, &p) / 4096.0;
|
||||
tf->normal.z = (float)get_i16(bytes, &p) / 4096.0;
|
||||
|
||||
tf->texture = get_i8(bytes, &p);
|
||||
tf->flags = get_i8(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];
|
||||
|
||||
tf->tris[0] = (tris_t){
|
||||
.vertices = {
|
||||
{.pos = v0, .uv = uv[0], .color = color},
|
||||
{.pos = v1, .uv = uv[1], .color = color},
|
||||
{.pos = v2, .uv = uv[2], .color = color},
|
||||
}
|
||||
};
|
||||
tf->tris[1] = (tris_t){
|
||||
.vertices = {
|
||||
{.pos = v3, .uv = uv[3], .color = color},
|
||||
{.pos = v0, .uv = uv[0], .color = color},
|
||||
{.pos = v2, .uv = uv[2], .color = color},
|
||||
}
|
||||
};
|
||||
|
||||
tf++;
|
||||
}
|
||||
|
||||
mem_temp_free(bytes);
|
||||
}
|
||||
|
||||
|
||||
void track_load_sections(char *file_name) {
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(file_name, &size);
|
||||
|
||||
g.track.section_count = size / 156; // SECTION_DATA_SIZE
|
||||
g.track.sections = mem_bump(sizeof(section_t) * g.track.section_count);
|
||||
|
||||
uint32_t p = 0;
|
||||
section_t *ts = g.track.sections;
|
||||
for (int i = 0; i < g.track.section_count; i++) {
|
||||
int32_t junction_index = get_i32(bytes, &p);
|
||||
if (junction_index != -1) {
|
||||
ts->junction = g.track.sections + junction_index;
|
||||
}
|
||||
else {
|
||||
ts->junction = NULL;
|
||||
}
|
||||
|
||||
ts->prev = 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.y = get_i32(bytes, &p);
|
||||
ts->center.z = get_i32(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);
|
||||
p += 2; // padding
|
||||
|
||||
p += 4 + 4; // objects pointer, objectCount
|
||||
p += 5 * 3 * 4; // view section pointers
|
||||
p += 5 * 3 * 2; // view section counts
|
||||
|
||||
p += 4 * 2; // high list
|
||||
p += 4 * 2; // med list
|
||||
|
||||
ts->face_start = get_i16(bytes, &p);
|
||||
ts->face_count = get_i16(bytes, &p);
|
||||
|
||||
p += 2 * 2; // global/local radius
|
||||
|
||||
ts->flags = get_i16(bytes, &p);
|
||||
ts->num = get_i16(bytes, &p);
|
||||
p += 2; // padding
|
||||
ts++;
|
||||
}
|
||||
|
||||
mem_temp_free(bytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void track_draw_section(section_t *section) {
|
||||
track_face_t *face = g.track.faces + section->face_start;
|
||||
int16_t face_count = section->face_count;
|
||||
|
||||
for (uint32_t j = 0; j < face_count; j++) {
|
||||
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[1], tex_index);
|
||||
face++;
|
||||
}
|
||||
}
|
||||
|
||||
void track_draw(camera_t *camera) {
|
||||
render_set_model_mat(&mat4_identity());
|
||||
|
||||
// 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.
|
||||
vec3_t cam_pos = camera->position;
|
||||
vec3_t cam_dir = camera_forward(camera);
|
||||
|
||||
int drawn = 0;
|
||||
int skipped = 0;
|
||||
for(int32_t i = 0; i < g.track.section_count; i++) {
|
||||
section_t *s = &g.track.sections[i];
|
||||
vec3_t diff = vec3_sub(cam_pos, s->center);
|
||||
float cam_dot = vec3_dot(diff, cam_dir);
|
||||
float dist_sq = vec3_dot(diff, diff);
|
||||
if (
|
||||
cam_dot < 2048 && // FIXME: should use the bounding radius of the section
|
||||
dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR)
|
||||
) {
|
||||
track_draw_section(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void track_cycle_pickups(void) {
|
||||
float pickup_cycle_time = 1.5 * system_cycle_time();
|
||||
|
||||
for (int i = 0; i < g.track.pickups_len; i++) {
|
||||
if (flags_is(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;
|
||||
}
|
||||
else if (g.track.pickups[i].cooldown_timer <= 0) {
|
||||
flags_add(g.track.pickups[i].face->flags, FACE_PICKUP_ACTIVE);
|
||||
track_face_set_color(g.track.pickups[i].face, rgba(
|
||||
sin( pickup_cycle_time + i) * 127 + 128,
|
||||
cos( pickup_cycle_time + i) * 127 + 128,
|
||||
sin(-pickup_cycle_time - i) * 127 + 128,
|
||||
255
|
||||
));
|
||||
}
|
||||
else{
|
||||
g.track.pickups[i].cooldown_timer -= system_tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void track_face_set_color(track_face_t *face, rgba_t color) {
|
||||
face->tris[0].vertices[0].color = color;
|
||||
face->tris[0].vertices[1].color = color;
|
||||
face->tris[0].vertices[2].color = color;
|
||||
|
||||
face->tris[1].vertices[0].color = color;
|
||||
face->tris[1].vertices[1].color = color;
|
||||
face->tris[1].vertices[2].color = color;
|
||||
}
|
||||
|
||||
track_face_t *track_section_get_base_face(section_t *section) {
|
||||
track_face_t *face = g.track.faces +section->face_start;
|
||||
while(flags_not(face->flags, FACE_TRACK_BASE)) {
|
||||
face++;
|
||||
}
|
||||
return face;
|
||||
}
|
||||
|
||||
section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance) {
|
||||
// Start search several sections before current section
|
||||
|
||||
for (int i = 0; i < TRACK_SEARCH_LOOK_BACK; i++) {
|
||||
section = section->prev;
|
||||
}
|
||||
|
||||
// Find vector from ship center to track section under
|
||||
// consideration
|
||||
float shortest_distance = 1000000000.0;
|
||||
section_t *nearest_section = section;
|
||||
section_t *junction = NULL;
|
||||
for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) {
|
||||
if (section->junction) {
|
||||
junction = section->junction;
|
||||
}
|
||||
|
||||
float d = vec3_len(vec3_sub(pos, section->center));
|
||||
if (d < shortest_distance) {
|
||||
shortest_distance = d;
|
||||
nearest_section = section;
|
||||
}
|
||||
|
||||
section = section->next;
|
||||
}
|
||||
|
||||
if (junction) {
|
||||
section = junction;
|
||||
for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) {
|
||||
float d = vec3_len(vec3_sub(pos, section->center));
|
||||
if (d < shortest_distance) {
|
||||
shortest_distance = d;
|
||||
nearest_section = section;
|
||||
}
|
||||
|
||||
if (flags_is(junction->flags, SECTION_JUNCTION_START)) {
|
||||
section = section->next;
|
||||
}
|
||||
else {
|
||||
section = section->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (distance != NULL) {
|
||||
*distance = shortest_distance;
|
||||
}
|
||||
return nearest_section;
|
||||
}
|
||||
#include "../mem.h"
|
||||
#include "../utils.h"
|
||||
#include "../render.h"
|
||||
#include "../system.h"
|
||||
#include "../platform.h"
|
||||
|
||||
#include "object.h"
|
||||
#include "track.h"
|
||||
#include "camera.h"
|
||||
#include "object.h"
|
||||
#include "game.h"
|
||||
|
||||
void track_load(const char *base_path) {
|
||||
// Load and assemble high res track tiles
|
||||
|
||||
g.track.textures.start = render_textures_len();
|
||||
g.track.textures.len = 0;
|
||||
|
||||
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"));
|
||||
|
||||
image_t *temp_tile = image_alloc(128, 128);
|
||||
for (int i = 0; i < ttf->len; i++) {
|
||||
for (int tx = 0; tx < 4; tx++) {
|
||||
for (int ty = 0; ty < 4; ty++) {
|
||||
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_copy(sub_tile, temp_tile, 0, 0, 32, 32, tx * 32, ty * 32);
|
||||
mem_temp_free(sub_tile);
|
||||
}
|
||||
}
|
||||
render_texture_create(temp_tile->width, temp_tile->height, temp_tile->pixels);
|
||||
g.track.textures.len++;
|
||||
}
|
||||
|
||||
mem_temp_free(temp_tile);
|
||||
mem_temp_free(cmp);
|
||||
mem_temp_free(ttf);
|
||||
|
||||
vec3_t *vertices = track_load_vertices(get_path(base_path, "track.trv"));
|
||||
track_load_faces(get_path(base_path, "track.trf"), vertices);
|
||||
mem_temp_free(vertices);
|
||||
|
||||
track_load_sections(get_path(base_path, "track.trs"));
|
||||
|
||||
g.track.pickups_len = 0;
|
||||
section_t *s = g.track.sections;
|
||||
section_t *j = NULL;
|
||||
|
||||
// Nummerate all sections; take care to give both stretches at a junction
|
||||
// the same numbers.
|
||||
int num = 0;
|
||||
do {
|
||||
s->num = num++;
|
||||
if (s->junction) { // start junction
|
||||
j = s->junction;
|
||||
do {
|
||||
j->num = num++;
|
||||
j = j->next;
|
||||
} while (!j->junction); // end junction
|
||||
num = s->num;
|
||||
}
|
||||
s = s->next;
|
||||
} while (s != g.track.sections);
|
||||
g.track.total_section_nums = num;
|
||||
|
||||
g.track.pickups = mem_mark();
|
||||
for (int i = 0; i < g.track.section_count; i++) {
|
||||
track_face_t *face = track_section_get_base_face(&g.track.sections[i]);
|
||||
|
||||
for (int f = 0; f < 2; f++) {
|
||||
if (flags_any(face->flags, FACE_PICKUP_RIGHT | FACE_PICKUP_LEFT)) {
|
||||
mem_bump(sizeof(track_pickup_t));
|
||||
g.track.pickups[g.track.pickups_len].face = face;
|
||||
g.track.pickups[g.track.pickups_len].cooldown_timer = 0;
|
||||
g.track.pickups_len++;
|
||||
}
|
||||
|
||||
if (flags_is(face->flags, FACE_BOOST)) {
|
||||
track_face_set_color(face, rgba(0, 0, 255, 255));
|
||||
}
|
||||
face++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ttf_t *track_load_tile_format(char *ttf_name) {
|
||||
uint32_t ttf_size;
|
||||
uint8_t *ttf_bytes = platform_load_asset(ttf_name, &ttf_size);
|
||||
|
||||
uint32_t p = 0;
|
||||
uint32_t num_tiles = ttf_size / 42;
|
||||
|
||||
ttf_t *ttf = mem_temp_alloc(sizeof(ttf_t) + sizeof(ttf_tile_t) * num_tiles);
|
||||
ttf->len = num_tiles;
|
||||
|
||||
for (int t = 0; t < num_tiles; t++) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
ttf->tiles[t].near[i] = get_i16(ttf_bytes, &p);
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ttf->tiles[t].med[i] = get_i16(ttf_bytes, &p);
|
||||
}
|
||||
ttf->tiles[t].far = get_i16(ttf_bytes, &p);
|
||||
}
|
||||
mem_temp_free(ttf_bytes);
|
||||
|
||||
return ttf;
|
||||
}
|
||||
|
||||
bool track_collect_pickups(track_face_t *face) {
|
||||
if (flags_is(face->flags, FACE_PICKUP_ACTIVE)) {
|
||||
flags_rm(face->flags, FACE_PICKUP_ACTIVE);
|
||||
flags_add(face->flags, FACE_PICKUP_COLLECTED);
|
||||
track_face_set_color(face, rgba(255, 255, 255, 255));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vec3_t *track_load_vertices(char *file_name) {
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(file_name, &size);
|
||||
|
||||
g.track.vertex_count = size / 16; // VECTOR_SIZE
|
||||
vec3_t *vertices = mem_temp_alloc(sizeof(vec3_t) * g.track.vertex_count);
|
||||
|
||||
uint32_t p = 0;
|
||||
for (int i = 0; i < g.track.vertex_count; i++) {
|
||||
vertices[i].x = get_i32(bytes, &p);
|
||||
vertices[i].y = get_i32(bytes, &p);
|
||||
vertices[i].z = get_i32(bytes, &p);
|
||||
p += 4; // padding
|
||||
}
|
||||
|
||||
mem_temp_free(bytes);
|
||||
return vertices;
|
||||
}
|
||||
|
||||
static const vec2_t track_uv[2][4] = {
|
||||
{{128, 0}, { 0, 0}, { 0, 128}, {128, 128}},
|
||||
{{ 0, 0}, {128, 0}, {128, 128}, { 0, 128}}
|
||||
};
|
||||
|
||||
void track_load_faces(char *file_name, vec3_t *vertices) {
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(file_name, &size);
|
||||
|
||||
g.track.face_count = size / 20; // TRACK_FACE_DATA_SIZE
|
||||
g.track.faces = mem_bump(sizeof(track_face_t) * g.track.face_count);
|
||||
|
||||
uint32_t p = 0;
|
||||
track_face_t *tf = g.track.faces;
|
||||
|
||||
|
||||
for (int i = 0; i < g.track.face_count; i++) {
|
||||
|
||||
vec3_t v0 = vertices[get_i16(bytes, &p)];
|
||||
vec3_t v1 = vertices[get_i16(bytes, &p)];
|
||||
vec3_t v2 = 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.y = (float)get_i16(bytes, &p) / 4096.0;
|
||||
tf->normal.z = (float)get_i16(bytes, &p) / 4096.0;
|
||||
|
||||
tf->texture = get_i8(bytes, &p);
|
||||
tf->flags = get_i8(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];
|
||||
|
||||
tf->tris[0] = (tris_t){
|
||||
.vertices = {
|
||||
{.pos = v0, .uv = uv[0], .color = color},
|
||||
{.pos = v1, .uv = uv[1], .color = color},
|
||||
{.pos = v2, .uv = uv[2], .color = color},
|
||||
}
|
||||
};
|
||||
tf->tris[1] = (tris_t){
|
||||
.vertices = {
|
||||
{.pos = v3, .uv = uv[3], .color = color},
|
||||
{.pos = v0, .uv = uv[0], .color = color},
|
||||
{.pos = v2, .uv = uv[2], .color = color},
|
||||
}
|
||||
};
|
||||
|
||||
tf++;
|
||||
}
|
||||
|
||||
mem_temp_free(bytes);
|
||||
}
|
||||
|
||||
|
||||
void track_load_sections(char *file_name) {
|
||||
uint32_t size;
|
||||
uint8_t *bytes = platform_load_asset(file_name, &size);
|
||||
|
||||
g.track.section_count = size / 156; // SECTION_DATA_SIZE
|
||||
g.track.sections = mem_bump(sizeof(section_t) * g.track.section_count);
|
||||
|
||||
uint32_t p = 0;
|
||||
section_t *ts = g.track.sections;
|
||||
for (int i = 0; i < g.track.section_count; i++) {
|
||||
int32_t junction_index = get_i32(bytes, &p);
|
||||
if (junction_index != -1) {
|
||||
ts->junction = g.track.sections + junction_index;
|
||||
}
|
||||
else {
|
||||
ts->junction = NULL;
|
||||
}
|
||||
|
||||
ts->prev = 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.y = get_i32(bytes, &p);
|
||||
ts->center.z = get_i32(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);
|
||||
p += 2; // padding
|
||||
|
||||
p += 4 + 4; // objects pointer, objectCount
|
||||
p += 5 * 3 * 4; // view section pointers
|
||||
p += 5 * 3 * 2; // view section counts
|
||||
|
||||
p += 4 * 2; // high list
|
||||
p += 4 * 2; // med list
|
||||
|
||||
ts->face_start = get_i16(bytes, &p);
|
||||
ts->face_count = get_i16(bytes, &p);
|
||||
|
||||
p += 2 * 2; // global/local radius
|
||||
|
||||
ts->flags = get_i16(bytes, &p);
|
||||
ts->num = get_i16(bytes, &p);
|
||||
p += 2; // padding
|
||||
ts++;
|
||||
}
|
||||
|
||||
mem_temp_free(bytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void track_draw_section(section_t *section) {
|
||||
track_face_t *face = g.track.faces + section->face_start;
|
||||
int16_t face_count = section->face_count;
|
||||
|
||||
for (uint32_t j = 0; j < face_count; j++) {
|
||||
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[1], tex_index);
|
||||
face++;
|
||||
}
|
||||
}
|
||||
|
||||
void track_draw(camera_t *camera) {
|
||||
render_set_model_mat(&mat4_identity());
|
||||
|
||||
// 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.
|
||||
vec3_t cam_pos = camera->position;
|
||||
vec3_t cam_dir = camera_forward(camera);
|
||||
|
||||
int drawn = 0;
|
||||
int skipped = 0;
|
||||
for(int32_t i = 0; i < g.track.section_count; i++) {
|
||||
section_t *s = &g.track.sections[i];
|
||||
vec3_t diff = vec3_sub(cam_pos, s->center);
|
||||
float cam_dot = vec3_dot(diff, cam_dir);
|
||||
float dist_sq = vec3_dot(diff, diff);
|
||||
if (
|
||||
cam_dot < 2048 && // FIXME: should use the bounding radius of the section
|
||||
dist_sq < (RENDER_FADEOUT_FAR * RENDER_FADEOUT_FAR)
|
||||
) {
|
||||
track_draw_section(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void track_cycle_pickups(void) {
|
||||
float pickup_cycle_time = 1.5 * system_cycle_time();
|
||||
|
||||
for (int i = 0; i < g.track.pickups_len; i++) {
|
||||
if (flags_is(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;
|
||||
}
|
||||
else if (g.track.pickups[i].cooldown_timer <= 0) {
|
||||
flags_add(g.track.pickups[i].face->flags, FACE_PICKUP_ACTIVE);
|
||||
track_face_set_color(g.track.pickups[i].face, rgba(
|
||||
sin( pickup_cycle_time + i) * 127 + 128,
|
||||
cos( pickup_cycle_time + i) * 127 + 128,
|
||||
sin(-pickup_cycle_time - i) * 127 + 128,
|
||||
255
|
||||
));
|
||||
}
|
||||
else{
|
||||
g.track.pickups[i].cooldown_timer -= system_tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void track_face_set_color(track_face_t *face, rgba_t color) {
|
||||
face->tris[0].vertices[0].color = color;
|
||||
face->tris[0].vertices[1].color = color;
|
||||
face->tris[0].vertices[2].color = color;
|
||||
|
||||
face->tris[1].vertices[0].color = color;
|
||||
face->tris[1].vertices[1].color = color;
|
||||
face->tris[1].vertices[2].color = color;
|
||||
}
|
||||
|
||||
track_face_t *track_section_get_base_face(section_t *section) {
|
||||
track_face_t *face = g.track.faces +section->face_start;
|
||||
while(flags_not(face->flags, FACE_TRACK_BASE)) {
|
||||
face++;
|
||||
}
|
||||
return face;
|
||||
}
|
||||
|
||||
section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance) {
|
||||
// Start search several sections before current section
|
||||
|
||||
for (int i = 0; i < TRACK_SEARCH_LOOK_BACK; i++) {
|
||||
section = section->prev;
|
||||
}
|
||||
|
||||
// Find vector from ship center to track section under
|
||||
// consideration
|
||||
float shortest_distance = 1000000000.0;
|
||||
section_t *nearest_section = section;
|
||||
section_t *junction = NULL;
|
||||
for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) {
|
||||
if (section->junction) {
|
||||
junction = section->junction;
|
||||
}
|
||||
|
||||
float d = vec3_len(vec3_sub(pos, section->center));
|
||||
if (d < shortest_distance) {
|
||||
shortest_distance = d;
|
||||
nearest_section = section;
|
||||
}
|
||||
|
||||
section = section->next;
|
||||
}
|
||||
|
||||
if (junction) {
|
||||
section = junction;
|
||||
for (int i = 0; i < TRACK_SEARCH_LOOK_AHEAD; i++) {
|
||||
float d = vec3_len(vec3_sub(pos, section->center));
|
||||
if (d < shortest_distance) {
|
||||
shortest_distance = d;
|
||||
nearest_section = section;
|
||||
}
|
||||
|
||||
if (flags_is(junction->flags, SECTION_JUNCTION_START)) {
|
||||
section = section->next;
|
||||
}
|
||||
else {
|
||||
section = section->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (distance != NULL) {
|
||||
*distance = shortest_distance;
|
||||
}
|
||||
return nearest_section;
|
||||
}
|
||||
|
|
|
@ -1,96 +1,96 @@
|
|||
#ifndef TRACK_H
|
||||
#define TRACK_H
|
||||
|
||||
|
||||
#include "../types.h"
|
||||
#include "object.h"
|
||||
#include "image.h"
|
||||
|
||||
#define TRACK_VERSION 8
|
||||
|
||||
#define TRACK_PICKUP_COOLDOWN_TIME 1
|
||||
|
||||
#define TRACK_SEARCH_LOOK_BACK 3
|
||||
#define TRACK_SEARCH_LOOK_AHEAD 6
|
||||
|
||||
typedef struct track_face_t {
|
||||
tris_t tris[2];
|
||||
vec3_t normal;
|
||||
uint8_t flags;
|
||||
uint8_t texture;
|
||||
} track_face_t;
|
||||
|
||||
#define FACE_TRACK_BASE (1<<0)
|
||||
#define FACE_PICKUP_LEFT (1<<1)
|
||||
#define FACE_FLIP_TEXTURE (1<<2)
|
||||
#define FACE_PICKUP_RIGHT (1<<3)
|
||||
#define FACE_START_GRID (1<<4)
|
||||
#define FACE_BOOST (1<<5)
|
||||
#define FACE_PICKUP_COLLECTED (1<<6)
|
||||
#define FACE_PICKUP_ACTIVE (1<<7)
|
||||
|
||||
typedef struct {
|
||||
uint16_t near[16];
|
||||
uint16_t med[4];
|
||||
uint16_t far;
|
||||
} ttf_tile_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t len;
|
||||
ttf_tile_t tiles[];
|
||||
} ttf_t;
|
||||
|
||||
typedef struct section_t {
|
||||
struct section_t *junction;
|
||||
struct section_t *prev;
|
||||
struct section_t *next;
|
||||
|
||||
vec3_t center;
|
||||
|
||||
int16_t face_start;
|
||||
int16_t face_count;
|
||||
|
||||
int16_t flags;
|
||||
int16_t num;
|
||||
} section_t;
|
||||
|
||||
#define SECTION_JUMP 1
|
||||
#define SECTION_JUNCTION_END 8
|
||||
#define SECTION_JUNCTION_START 16
|
||||
#define SECTION_JUNCTION 32
|
||||
|
||||
typedef struct {
|
||||
track_face_t *face;
|
||||
float cooldown_timer;
|
||||
} track_pickup_t;
|
||||
|
||||
typedef struct track_t {
|
||||
int32_t vertex_count;
|
||||
int32_t face_count;
|
||||
int32_t section_count;
|
||||
int32_t pickups_len;
|
||||
int32_t total_section_nums;
|
||||
texture_list_t textures;
|
||||
|
||||
track_face_t *faces;
|
||||
section_t *sections;
|
||||
track_pickup_t *pickups;
|
||||
} track_t;
|
||||
|
||||
|
||||
void track_load(const char *base_path);
|
||||
ttf_t *track_load_tile_format(char *ttf_name);
|
||||
vec3_t *track_load_vertices(char *file);
|
||||
void track_load_faces(char *file, vec3_t *vertices);
|
||||
void track_load_sections(char *file);
|
||||
bool track_collect_pickups(track_face_t *face);
|
||||
void track_face_set_color(track_face_t *face, rgba_t color);
|
||||
track_face_t *track_section_get_base_face(section_t *section);
|
||||
section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance);
|
||||
|
||||
struct camera_t;
|
||||
void track_draw(struct camera_t *camera);
|
||||
|
||||
void track_cycle_pickups(void);
|
||||
|
||||
#endif
|
||||
#ifndef TRACK_H
|
||||
#define TRACK_H
|
||||
|
||||
|
||||
#include "../types.h"
|
||||
#include "object.h"
|
||||
#include "image.h"
|
||||
|
||||
#define TRACK_VERSION 8
|
||||
|
||||
#define TRACK_PICKUP_COOLDOWN_TIME 1
|
||||
|
||||
#define TRACK_SEARCH_LOOK_BACK 3
|
||||
#define TRACK_SEARCH_LOOK_AHEAD 6
|
||||
|
||||
typedef struct track_face_t {
|
||||
tris_t tris[2];
|
||||
vec3_t normal;
|
||||
uint8_t flags;
|
||||
uint8_t texture;
|
||||
} track_face_t;
|
||||
|
||||
#define FACE_TRACK_BASE (1<<0)
|
||||
#define FACE_PICKUP_LEFT (1<<1)
|
||||
#define FACE_FLIP_TEXTURE (1<<2)
|
||||
#define FACE_PICKUP_RIGHT (1<<3)
|
||||
#define FACE_START_GRID (1<<4)
|
||||
#define FACE_BOOST (1<<5)
|
||||
#define FACE_PICKUP_COLLECTED (1<<6)
|
||||
#define FACE_PICKUP_ACTIVE (1<<7)
|
||||
|
||||
typedef struct {
|
||||
uint16_t near[16];
|
||||
uint16_t med[4];
|
||||
uint16_t far;
|
||||
} ttf_tile_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t len;
|
||||
ttf_tile_t tiles[];
|
||||
} ttf_t;
|
||||
|
||||
typedef struct section_t {
|
||||
struct section_t *junction;
|
||||
struct section_t *prev;
|
||||
struct section_t *next;
|
||||
|
||||
vec3_t center;
|
||||
|
||||
int16_t face_start;
|
||||
int16_t face_count;
|
||||
|
||||
int16_t flags;
|
||||
int16_t num;
|
||||
} section_t;
|
||||
|
||||
#define SECTION_JUMP 1
|
||||
#define SECTION_JUNCTION_END 8
|
||||
#define SECTION_JUNCTION_START 16
|
||||
#define SECTION_JUNCTION 32
|
||||
|
||||
typedef struct {
|
||||
track_face_t *face;
|
||||
float cooldown_timer;
|
||||
} track_pickup_t;
|
||||
|
||||
typedef struct track_t {
|
||||
int32_t vertex_count;
|
||||
int32_t face_count;
|
||||
int32_t section_count;
|
||||
int32_t pickups_len;
|
||||
int32_t total_section_nums;
|
||||
texture_list_t textures;
|
||||
|
||||
track_face_t *faces;
|
||||
section_t *sections;
|
||||
track_pickup_t *pickups;
|
||||
} track_t;
|
||||
|
||||
|
||||
void track_load(const char *base_path);
|
||||
ttf_t *track_load_tile_format(char *ttf_name);
|
||||
vec3_t *track_load_vertices(char *file);
|
||||
void track_load_faces(char *file, vec3_t *vertices);
|
||||
void track_load_sections(char *file);
|
||||
bool track_collect_pickups(track_face_t *face);
|
||||
void track_face_set_color(track_face_t *face, rgba_t color);
|
||||
track_face_t *track_section_get_base_face(section_t *section);
|
||||
section_t *track_nearest_section(vec3_t pos, section_t *section, float *distance);
|
||||
|
||||
struct camera_t;
|
||||
void track_draw(struct camera_t *camera);
|
||||
|
||||
void track_cycle_pickups(void);
|
||||
|
||||
#endif
|
||||
|
|
1378
src/wipeout/weapon.c
1378
src/wipeout/weapon.c
File diff suppressed because it is too large
Load diff
|
@ -1,51 +1,51 @@
|
|||
#ifndef WEAPON_H
|
||||
#define WEAPON_H
|
||||
|
||||
#define WEAPONS_MAX 64
|
||||
|
||||
#define WEAPON_MINE_DURATION (450 * (1.0/30.0))
|
||||
#define WEAPON_ROCKET_DURATION (200 * (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_MISSILE_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_SPECIAL_DURATION (400 * (1.0/30.0))
|
||||
|
||||
#define WEAPON_MINE_RELEASE_RATE (3 * (1.0/30.0))
|
||||
#define WEAPON_DELAY (40 * (1.0/30.0))
|
||||
|
||||
#define WEAPON_TYPE_NONE 0
|
||||
#define WEAPON_TYPE_MINE 1
|
||||
#define WEAPON_TYPE_MISSILE 2
|
||||
#define WEAPON_TYPE_ROCKET 3
|
||||
#define WEAPON_TYPE_SPECIAL 4
|
||||
#define WEAPON_TYPE_EBOLT 5
|
||||
#define WEAPON_TYPE_FLARE 6
|
||||
#define WEAPON_TYPE_REV_CON 7
|
||||
#define WEAPON_TYPE_SHIELD 8
|
||||
#define WEAPON_TYPE_TURBO 9
|
||||
#define WEAPON_TYPE_MAX 10
|
||||
|
||||
#define WEAPON_MINE_COUNT 5
|
||||
|
||||
#define WEAPON_HIT_NONE 0
|
||||
#define WEAPON_HIT_SHIP 1
|
||||
#define WEAPON_HIT_TRACK 2
|
||||
|
||||
#define WEAPON_PARTICLE_SPAWN_RATE 0.011
|
||||
#define WEAPON_AI_DELAY 1.1
|
||||
|
||||
#define WEAPON_CLASS_ANY 1
|
||||
#define WEAPON_CLASS_PROJECTILE 2
|
||||
|
||||
|
||||
void weapons_load(void);
|
||||
void weapons_init(void);
|
||||
void weapons_fire(ship_t *ship, int weapon_type);
|
||||
void weapons_fire_delayed(ship_t *ship, int weapon_type);
|
||||
void weapons_update(void);
|
||||
void weapons_draw(void);
|
||||
int weapon_get_random_type(int type_class);
|
||||
|
||||
#endif
|
||||
#ifndef WEAPON_H
|
||||
#define WEAPON_H
|
||||
|
||||
#define WEAPONS_MAX 64
|
||||
|
||||
#define WEAPON_MINE_DURATION (450 * (1.0/30.0))
|
||||
#define WEAPON_ROCKET_DURATION (200 * (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_MISSILE_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_SPECIAL_DURATION (400 * (1.0/30.0))
|
||||
|
||||
#define WEAPON_MINE_RELEASE_RATE (3 * (1.0/30.0))
|
||||
#define WEAPON_DELAY (40 * (1.0/30.0))
|
||||
|
||||
#define WEAPON_TYPE_NONE 0
|
||||
#define WEAPON_TYPE_MINE 1
|
||||
#define WEAPON_TYPE_MISSILE 2
|
||||
#define WEAPON_TYPE_ROCKET 3
|
||||
#define WEAPON_TYPE_SPECIAL 4
|
||||
#define WEAPON_TYPE_EBOLT 5
|
||||
#define WEAPON_TYPE_FLARE 6
|
||||
#define WEAPON_TYPE_REV_CON 7
|
||||
#define WEAPON_TYPE_SHIELD 8
|
||||
#define WEAPON_TYPE_TURBO 9
|
||||
#define WEAPON_TYPE_MAX 10
|
||||
|
||||
#define WEAPON_MINE_COUNT 5
|
||||
|
||||
#define WEAPON_HIT_NONE 0
|
||||
#define WEAPON_HIT_SHIP 1
|
||||
#define WEAPON_HIT_TRACK 2
|
||||
|
||||
#define WEAPON_PARTICLE_SPAWN_RATE 0.011
|
||||
#define WEAPON_AI_DELAY 1.1
|
||||
|
||||
#define WEAPON_CLASS_ANY 1
|
||||
#define WEAPON_CLASS_PROJECTILE 2
|
||||
|
||||
|
||||
void weapons_load(void);
|
||||
void weapons_init(void);
|
||||
void weapons_fire(ship_t *ship, int weapon_type);
|
||||
void weapons_fire_delayed(ship_t *ship, int weapon_type);
|
||||
void weapons_update(void);
|
||||
void weapons_draw(void);
|
||||
int weapon_get_random_type(int type_class);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue