ref: 6d241d506fbce2d0b6a69f19f83b34bdda0ac6af
dir: /src/wipeout/main_menu.c/
#include "../utils.h" #include "../system.h" #include "../mem.h" #include "../platform.h" #include "menu.h" #include "main_menu.h" #include "game.h" #include "image.h" #include "ui.h" static void page_main_init(menu_t *menu); static void page_options_init(menu_t *menu); static void page_race_class_init(menu_t *menu); static void page_race_type_init(menu_t *menu); static void page_team_init(menu_t *menu); static void page_pilot_init(menu_t *menu); static void page_circut_init(menu_t *menu); static void page_options_controls_init(menu_t *menu); static void page_options_video_init(menu_t *menu); static void page_options_audio_init(menu_t *menu); static uint16_t background; static texture_list_t track_images; static menu_t *main_menu; static struct { Object *race_classes[2]; Object *teams[4]; Object *pilots[8]; struct { Object *stopwatch, *save, *load, *headphones, *cd; } options; struct { Object *championship, *msdos, *single_race, *options; } misc; Object *rescue; Object *controller; } models; static void draw_model(Object *model, vec2_t offset, vec3_t pos, float rotation) { render_set_view(vec3(0,0,0), vec3(0, -M_PI, -M_PI)); render_set_screen_position(offset); mat4_t mat = mat4_identity(); mat4_set_translation(&mat, pos); mat4_set_yaw_pitch_roll(&mat, vec3(0, rotation, M_PI)); object_draw(model, &mat); render_set_screen_position(vec2(0, 0)); } // ----------------------------------------------------------------------------- // Main Menu static void button_start_game(menu_t *menu, int data) { page_race_class_init(menu); } static void button_options(menu_t *menu, int data) { page_options_init(menu); } static void button_quit_confirm(menu_t *menu, int data) { if (data) { system_exit(); } 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 page_main_draw(menu_t *menu, int data) { switch (data) { case 0: draw_model(g.ships[0].model, vec2(0, -0.1), vec3(0, 0, -700), system_cycle_time()); break; case 1: draw_model(models.misc.options, vec2(0, -0.2), vec3(0, 0, -700), system_cycle_time()); break; case 2: draw_model(models.misc.msdos, vec2(0, -0.2), vec3(0, 0, -700), system_cycle_time()); break; } } static void page_main_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "OPTIONS", page_main_draw); flags_add(page->layout_flags, MENU_FIXED); page->title_pos = vec2i(0, 30); page->title_anchor = UI_POS_TOP | UI_POS_CENTER; page->items_pos = vec2i(0, -110); page->items_anchor = UI_POS_BOTTOM | UI_POS_CENTER; menu_page_add_button(page, 0, "START GAME", button_start_game); menu_page_add_button(page, 1, "OPTIONS", button_options); #ifndef __EMSCRIPTEN__ menu_page_add_button(page, 2, "QUIT", button_quit); #endif } // ----------------------------------------------------------------------------- // Options static void button_controls(menu_t *menu, int data) { page_options_controls_init(menu); } static void button_video(menu_t *menu, int data) { page_options_video_init(menu); } static void button_audio(menu_t *menu, int data) { page_options_audio_init(menu); } static void page_options_draw(menu_t *menu, int data) { switch (data) { case 0: draw_model(models.controller, vec2(0, -0.1), vec3(0, 0, -6000), system_cycle_time()); break; case 1: draw_model(models.rescue, vec2(0, -0.2), vec3(0, 0, -700), system_cycle_time()); break; // TODO: needs better model case 2: draw_model(models.options.headphones, vec2(0, -0.2), vec3(0, 0, -300), system_cycle_time()); break; } } static void page_options_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "OPTIONS", page_options_draw); flags_add(page->layout_flags, MENU_FIXED); page->title_pos = vec2i(0, 30); page->title_anchor = UI_POS_TOP | UI_POS_CENTER; page->items_pos = vec2i(0, -110); page->items_anchor = UI_POS_BOTTOM | UI_POS_CENTER; menu_page_add_button(page, 0, "CONTROLS", button_controls); menu_page_add_button(page, 1, "VIDEO", button_video); menu_page_add_button(page, 2, "AUDIO", button_audio); } // ----------------------------------------------------------------------------- // Options Controls static void page_options_controls_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "TODO", page_options_draw); } // ----------------------------------------------------------------------------- // Options Video static void toggle_fullscreen(menu_t *menu, int data) { save.fullscreen = data; save.is_dirty = true; platform_set_fullscreen(save.fullscreen); } static void toggle_show_fps(menu_t *menu, int data) { save.show_fps = data; save.is_dirty = true; } static void toggle_ui_scale(menu_t *menu, int data) { save.ui_scale = data; save.is_dirty = true; } static const char *opts_off_on[] = {"OFF", "ON"}; static const char *opts_ui_sizes[] = {"AUTO", "1X", "2X", "3X", "4X"}; static void page_options_video_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "VIDEO OPTIONS", NULL); flags_set(page->layout_flags, MENU_VERTICAL | MENU_FIXED); page->title_pos = vec2i(-160, -100); page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->items_pos = vec2i(-160, -80); page->block_width = 320; page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER; #ifndef __EMSCRIPTEN__ menu_page_add_toggle(page, save.fullscreen, "FULLSCREEN", opts_off_on, len(opts_off_on), toggle_fullscreen); #endif menu_page_add_toggle(page, save.ui_scale, "UI SCALE", opts_ui_sizes, len(opts_ui_sizes), toggle_ui_scale); menu_page_add_toggle(page, save.show_fps, "SHOW FPS", opts_off_on, len(opts_off_on), toggle_show_fps); } // ----------------------------------------------------------------------------- // Options Audio static void toggle_music_volume(menu_t *menu, int data) { save.music_volume = (float)data * 0.1; save.is_dirty = true; } static void toggle_sfx_volume(menu_t *menu, int data) { save.sfx_volume = (float)data * 0.1; save.is_dirty = true; } static const char *opts_volume[] = {"0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100"}; static void page_options_audio_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "AUDIO OPTIONS", NULL); flags_set(page->layout_flags, MENU_VERTICAL | MENU_FIXED); page->title_pos = vec2i(-160, -100); page->title_anchor = UI_POS_MIDDLE | UI_POS_CENTER; page->items_pos = vec2i(-160, -80); page->block_width = 320; page->items_anchor = UI_POS_MIDDLE | UI_POS_CENTER; menu_page_add_toggle(page, save.music_volume * 10, "MUSIC VOLUME", opts_volume, len(opts_volume), toggle_music_volume); menu_page_add_toggle(page, save.sfx_volume * 10, "SOUND EFFECTS VOLUME", opts_volume, len(opts_volume), toggle_sfx_volume); } // ----------------------------------------------------------------------------- // Racing class static void button_race_class_select(menu_t *menu, int data) { if (!save.has_rapier_class && data == RACE_CLASS_RAPIER) { return; } g.race_class = data; page_race_type_init(menu); } static void page_race_class_draw(menu_t *menu, int data) { menu_page_t *page = &menu->pages[menu->index]; flags_add(page->layout_flags, MENU_FIXED); page->title_pos = vec2i(0, 30); page->title_anchor = UI_POS_TOP | UI_POS_CENTER; page->items_pos = vec2i(0, -110); page->items_anchor = UI_POS_BOTTOM | UI_POS_CENTER; draw_model(models.race_classes[data], vec2(0, -0.2), vec3(0, 0, -350), system_cycle_time()); if (!save.has_rapier_class && data == RACE_CLASS_RAPIER) { render_set_view_2d(); vec2i_t pos = vec2i(page->items_pos.x, page->items_pos.y + 32); ui_draw_text_centered("NOT AVAILABLE", ui_scaled_pos(page->items_anchor, pos), UI_SIZE_12, UI_COLOR_ACCENT); } } static void page_race_class_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "SELECT RACING CLASS", page_race_class_draw); for (int i = 0; i < len(def.race_classes); i++) { menu_page_add_button(page, i, def.race_classes[i].name, button_race_class_select); } } // ----------------------------------------------------------------------------- // Race Type static void button_race_type_select(menu_t *menu, int data) { g.race_type = data; g.highscore_tab = g.race_type == RACE_TYPE_TIME_TRIAL ? HIGHSCORE_TAB_TIME_TRIAL : HIGHSCORE_TAB_RACE; page_team_init(menu); } static void page_race_type_draw(menu_t *menu, int data) { switch (data) { case 0: draw_model(models.misc.championship, vec2(0, -0.2), vec3(0, 0, -400), system_cycle_time()); break; case 1: draw_model(models.misc.single_race, vec2(0, -0.2), vec3(0, 0, -400), system_cycle_time()); break; case 2: draw_model(models.options.stopwatch, vec2(0, -0.2), vec3(0, 0, -400), system_cycle_time()); break; } } static void page_race_type_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "SELECT RACE TYPE", page_race_type_draw); flags_add(page->layout_flags, MENU_FIXED); page->title_pos = vec2i(0, 30); page->title_anchor = UI_POS_TOP | UI_POS_CENTER; page->items_pos = vec2i(0, -110); page->items_anchor = UI_POS_BOTTOM | UI_POS_CENTER; for (int i = 0; i < len(def.race_types); i++) { menu_page_add_button(page, i, def.race_types[i].name, button_race_type_select); } } // ----------------------------------------------------------------------------- // Team static void button_team_select(menu_t *menu, int data) { g.team = data; page_pilot_init(menu); } static void page_team_draw(menu_t *menu, int data) { draw_model(models.teams[data], vec2(0, -0.2), vec3(0, 0, -10000), system_cycle_time()); draw_model(g.ships[def.teams[data].pilots[0]].model, vec2(0, -0.3), vec3(-700, -800, -1300), system_cycle_time()*1.1); draw_model(g.ships[def.teams[data].pilots[1]].model, vec2(0, -0.3), vec3( 700, -800, -1300), system_cycle_time()*1.2); } static void page_team_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "SELECT YOUR TEAM", page_team_draw); flags_add(page->layout_flags, MENU_FIXED); page->title_pos = vec2i(0, 30); page->title_anchor = UI_POS_TOP | UI_POS_CENTER; page->items_pos = vec2i(0, -110); page->items_anchor = UI_POS_BOTTOM | UI_POS_CENTER; for (int i = 0; i < len(def.teams); i++) { menu_page_add_button(page, i, def.teams[i].name, button_team_select); } } // ----------------------------------------------------------------------------- // Pilot static void button_pilot_select(menu_t *menu, int data) { g.pilot = data; if (g.race_type != RACE_TYPE_CHAMPIONSHIP) { page_circut_init(menu); } else { g.circut = 0; game_reset_championship(); game_set_scene(GAME_SCENE_RACE); } } static void page_pilot_draw(menu_t *menu, int data) { draw_model(models.pilots[data], vec2(0, -0.2), vec3(0, 0, -10000), system_cycle_time()); } static void page_pilot_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "CHOOSE YOUR PILOT", page_pilot_draw); flags_add(page->layout_flags, MENU_FIXED); page->title_pos = vec2i(0, 30); page->title_anchor = UI_POS_TOP | UI_POS_CENTER; page->items_pos = vec2i(0, -110); page->items_anchor = UI_POS_BOTTOM | UI_POS_CENTER; for (int i = 0; i < len(def.teams[g.team].pilots); i++) { menu_page_add_button(page, def.teams[g.team].pilots[i], def.pilots[def.teams[g.team].pilots[i]].name, button_pilot_select); } } // ----------------------------------------------------------------------------- // Circut static void button_circut_select(menu_t *menu, int data) { g.circut = data; game_set_scene(GAME_SCENE_RACE); } static void page_circut_draw(menu_t *menu, int data) { vec2i_t pos = vec2i(0, -25); vec2i_t size = vec2i(128, 74); vec2i_t scaled_size = ui_scaled(size); vec2i_t scaled_pos = ui_scaled_pos(UI_POS_MIDDLE | UI_POS_CENTER, vec2i(pos.x - size.x/2, pos.y - size.y/2)); render_push_2d(scaled_pos, scaled_size, rgba(128, 128, 128, 255), texture_from_list(track_images, data)); } static void page_circut_init(menu_t *menu) { menu_page_t *page = menu_push(menu, "SELECT RACING CIRCUT", page_circut_draw); flags_add(page->layout_flags, MENU_FIXED); page->title_pos = vec2i(0, 30); page->title_anchor = UI_POS_TOP | UI_POS_CENTER; page->items_pos = vec2i(0, -100); page->items_anchor = UI_POS_BOTTOM | UI_POS_CENTER; for (int i = 0; i < len(def.circuts); i++) { if (!def.circuts[i].is_bonus_circut || save.has_bonus_circuts) { menu_page_add_button(page, i, def.circuts[i].name, button_circut_select); } } } #define objects_unpack(DEST, SRC) \ objects_unpack_imp((Object **)&DEST, sizeof(DEST)/sizeof(Object*), SRC) static void objects_unpack_imp(Object **dest_array, int len, Object *src) { int i; for (i = 0; src && i < len; i++) { dest_array[i] = src; src = src->next; } error_if(i != len, "expected %d models got %d", len, i) } void main_menu_init() { g.is_attract_mode = false; main_menu = mem_bump(sizeof(menu_t)); background = image_get_texture("wipeout/textures/wipeout1.tim"); track_images = image_get_compressed_textures("wipeout/textures/track.cmp"); objects_unpack(models.race_classes, objects_load("wipeout/common/leeg.prm", image_get_compressed_textures("wipeout/common/leeg.cmp"))); objects_unpack(models.teams, objects_load("wipeout/common/teams.prm", texture_list_empty())); objects_unpack(models.pilots, objects_load("wipeout/common/pilot.prm", image_get_compressed_textures("wipeout/common/pilot.cmp"))); objects_unpack(models.options, objects_load("wipeout/common/alopt.prm", image_get_compressed_textures("wipeout/common/alopt.cmp"))); objects_unpack(models.rescue, objects_load("wipeout/common/rescu.prm", image_get_compressed_textures("wipeout/common/rescu.cmp"))); objects_unpack(models.controller, objects_load("wipeout/common/pad1.prm", image_get_compressed_textures("wipeout/common/pad1.cmp"))); objects_unpack(models.misc, objects_load("wipeout/common/msdos.prm", image_get_compressed_textures("wipeout/common/msdos.cmp"))); menu_reset(main_menu); page_main_init(main_menu); } void main_menu_update() { render_set_view_2d(); render_push_2d(vec2i(0, 0), render_size(), rgba(128, 128, 128, 255), background); menu_update(main_menu); }