213 lines
6.1 KiB
C
213 lines
6.1 KiB
C
#include "map.h"
|
|
#include "level.h"
|
|
#include "render.h"
|
|
#include <gint/timer.h>
|
|
#include <gint/cpu.h>
|
|
#include <gint/keyboard.h>
|
|
#include <gint/defs/util.h>
|
|
#include <gint/usb.h>
|
|
#include <gint/usb-ff-bulk.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
typedef struct {
|
|
/* Same mechanism as in game_t */
|
|
level_t const *level;
|
|
map_t const *map;
|
|
uint16_t *map_anim;
|
|
camera_t camera;
|
|
|
|
} menu_game_t;
|
|
|
|
static menu_game_t *menu_load_game(level_t const *level)
|
|
{
|
|
menu_game_t *mg = malloc(sizeof *mg);
|
|
mg->level = level;
|
|
mg->map = level->map;
|
|
mg->map_anim = malloc(mg->map->width * mg->map->height *
|
|
sizeof *mg->map_anim);
|
|
|
|
for(int i = 0; i < mg->map->width * mg->map->height; i++)
|
|
mg->map_anim[i] = rand() & 4095;
|
|
|
|
camera_init(&mg->camera, mg->map);
|
|
|
|
return mg;
|
|
}
|
|
|
|
static void menu_destroy_game(menu_game_t *mg)
|
|
{
|
|
free(mg->map_anim);
|
|
free(mg);
|
|
}
|
|
|
|
static void menu_render_game(menu_game_t *mg)
|
|
{
|
|
render_map_layer(mg->map, &mg->camera, 0, 0, HORIZONTAL, mg->map_anim,
|
|
DIMAGE_NONE);
|
|
render_map_layer(mg->map, &mg->camera, 0, 0, VERTICAL, mg->map_anim,
|
|
DIMAGE_NONE);
|
|
render_map_layer(mg->map, &mg->camera, 0, 0, CEILING, mg->map_anim,
|
|
DIMAGE_NONE);
|
|
}
|
|
|
|
static void darken_vram(void)
|
|
{
|
|
int top_margin = 0; // 24;
|
|
int bottom_margin = 0; // 32;
|
|
|
|
uint32_t *v = (void *)(gint_vram + top_margin * DWIDTH);
|
|
int N = DWIDTH * (DHEIGHT - top_margin - bottom_margin) / 2;
|
|
|
|
for(int i = 0; i < N; i++)
|
|
// v[i] = (v[i] & 0xf7def7de) >> 1;
|
|
v[i] = (v[i] & 0xe79ce79c) >> 2;
|
|
}
|
|
|
|
static void menu_update_animations(menu_game_t *mg, fixed_t dt)
|
|
{
|
|
for(int i = 0; i < mg->map->width * mg->map->height; i++)
|
|
mg->map_anim[i] += fround(dt * 1000);
|
|
}
|
|
|
|
static void dsprint(int x, int y, int fg1, int fg2, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char str[256];
|
|
va_start(args, fmt);
|
|
vsnprintf(str, 256, fmt, args);
|
|
va_end(args);
|
|
|
|
dtext(x, y+1, fg2, str);
|
|
dtext(x, y, fg1, str);
|
|
}
|
|
|
|
int menu_level_select(int start)
|
|
{
|
|
extern bopti_image_t img_menu_title;
|
|
|
|
menu_game_t *options[LEVEL_COUNT];
|
|
/* Don't show the sandbox in the menu; ALPHA+EXE to enter it instead */
|
|
#define OPTION_COUNT (LEVEL_COUNT-1)
|
|
|
|
for(int i = 0; i < LEVEL_COUNT; i++)
|
|
options[i] = menu_load_game(level_all[i]);
|
|
|
|
int selection = (start >= 0 && start < OPTION_COUNT) ? start : 0;
|
|
int target_y=0, y=0;
|
|
/* Time spent in the transition animation that moves the GUI items away
|
|
before starting the level. */
|
|
fixed_t transition_time = fix(0.0);
|
|
|
|
int volatile frame_tick = 1;
|
|
int t = timer_configure(TIMER_ANY, 1000000 / FRAME_RATE,
|
|
GINT_CALL_SET(&frame_tick));
|
|
timer_start(t);
|
|
|
|
while(1) {
|
|
while(!frame_tick)
|
|
sleep();
|
|
fixed_t dt = fix(1) / FRAME_RATE;
|
|
|
|
if(y != target_y) {
|
|
int dy = (target_y - y) / 4;
|
|
if(y < target_y && dy < 6)
|
|
dy = min(target_y-y, 6);
|
|
if(y > target_y && dy > -6)
|
|
dy = max(target_y-y, -6);
|
|
y += dy;
|
|
}
|
|
|
|
/* GUI positioning variables, accounting for the final transition */
|
|
fixed_t TRANSITION_LEN = fix(0.75);
|
|
int LOGO_Y = cubic(5, 5-80, transition_time, TRANSITION_LEN);
|
|
int LEVELS_X = cubic(40, 40-200, transition_time, TRANSITION_LEN);
|
|
int HISCORE_X = cubic(DWIDTH-80, DWIDTH, transition_time,
|
|
TRANSITION_LEN);
|
|
|
|
extern font_t font_rogue;
|
|
font_t const *old_font = dfont(&font_rogue);
|
|
|
|
dclear(C_BLACK);
|
|
|
|
for(int i = 0; i < OPTION_COUNT; i++) {
|
|
if(-y <= (i-1)*DHEIGHT)
|
|
continue;
|
|
if(-y >= (i+1)*DHEIGHT)
|
|
continue;
|
|
|
|
int local_offset = y + i*DHEIGHT;
|
|
fixed_t map_center = fix(options[i]->map->height) / 2;
|
|
options[i]->camera.y =
|
|
map_center - fix(local_offset + 2) / TILE_HEIGHT;
|
|
menu_render_game(options[i]);
|
|
}
|
|
if(transition_time == 0)
|
|
darken_vram();
|
|
|
|
dimage(148, LOGO_Y, &img_menu_title);
|
|
render_small_text(LEVELS_X, 80, C_WHITE, "PLAY", -1);
|
|
render_small_text(HISCORE_X, 80, C_WHITE, "HIGH SCORE", -1);
|
|
for(int i = 0; i < OPTION_COUNT; i++) {
|
|
dsprint(LEVELS_X, 95+16*i,
|
|
(i == selection) ? C_WHITE : C_RGB(20, 20, 20), C_BLACK,
|
|
"%s%s",
|
|
(i == selection) ? "> " : "", options[i]->level->name);
|
|
// TODO: Wire high score to main menu
|
|
dsprint(HISCORE_X, 95+16*i,
|
|
(i == selection) ? C_WHITE : C_RGB(20, 20, 20), C_BLACK,
|
|
"%03d", 0);
|
|
}
|
|
render_small_text(LEVELS_X, 180, C_WHITE, "OTHER", -1);
|
|
dsprint(LEVELS_X, 195,
|
|
(selection == OPTION_COUNT) ? C_WHITE : C_RGB(20, 20, 20), C_BLACK,
|
|
(selection == OPTION_COUNT) ? "> Credits" : "Credits");
|
|
|
|
dfont(old_font);
|
|
dupdate();
|
|
|
|
for(int i = 0; i < OPTION_COUNT; i++)
|
|
menu_update_animations(options[i], dt);
|
|
|
|
int key = getkey_opt(GETKEY_MENU, &frame_tick).key;
|
|
|
|
extern bool rogue_life_video_capture;
|
|
if(key == KEY_F6 && keydown(KEY_ALPHA) && !keydown(KEY_VARS)
|
|
&& usb_is_open()) {
|
|
rogue_life_video_capture = !rogue_life_video_capture;
|
|
}
|
|
if(rogue_life_video_capture) {
|
|
usb_fxlink_videocapture(false);
|
|
}
|
|
|
|
if(key == KEY_UP && selection > 0) {
|
|
selection--;
|
|
target_y += DHEIGHT;
|
|
}
|
|
if(key == KEY_DOWN && selection < OPTION_COUNT) {
|
|
selection++;
|
|
target_y -= DHEIGHT;
|
|
}
|
|
|
|
if(key == KEY_EXE && keydown(KEY_ALPHA)) {
|
|
selection = LEVEL_COUNT - 1;
|
|
break;
|
|
}
|
|
if((key == KEY_EXE || key == KEY_SHIFT) && transition_time == 0) {
|
|
transition_time = dt;
|
|
}
|
|
else if(transition_time > fix(0.0)) {
|
|
transition_time += dt;
|
|
if(transition_time >= TRANSITION_LEN)
|
|
break;
|
|
}
|
|
}
|
|
|
|
timer_stop(t);
|
|
|
|
for(size_t i = 0; i < OPTION_COUNT; i++)
|
|
menu_destroy_game(options[i]);
|
|
|
|
return selection;
|
|
}
|