Permalink
Fetching contributors…
Cannot retrieve contributors at this time
642 lines (540 sloc) 15.4 KB
/*
* See Licensing and Copyright notice in naev.h
*/
/**
* @file menu.h
*
* @brief Handles the important game menus.
*/
#include "menu.h"
#include "naev.h"
#include "nstring.h"
#include "SDL.h"
#include "toolkit.h"
#include "tk/toolkit_priv.h" /* Needed for menu_main_resize */
#include "dialogue.h"
#include "log.h"
#include "pilot.h"
#include "space.h"
#include "player.h"
#include "mission.h"
#include "ntime.h"
#include "save.h"
#include "load.h"
#include "land.h"
#include "rng.h"
#include "nebula.h"
#include "pause.h"
#include "options.h"
#include "intro.h"
#include "music.h"
#include "map.h"
#include "nfile.h"
#include "info.h"
#include "comm.h"
#include "conf.h"
#include "dev_uniedit.h"
#include "gui.h"
#include "start.h"
#include "camera.h"
#include "board.h"
#include "ndata.h"
#define MAIN_WIDTH 130 /**< Main menu width. */
#define MENU_WIDTH 130 /**< Escape menu width. */
#define MENU_HEIGHT 250 /**< Escape menu height. */
#define DEATH_WIDTH 130 /**< Death menu width. */
#define DEATH_HEIGHT 200 /**< Death menu height. */
#define OPTIONS_WIDTH 360 /**< Options menu width. */
#define OPTIONS_HEIGHT 90 /**< Options menu height. */
#define BUTTON_WIDTH 90 /**< Button width, standard across menus. */
#define BUTTON_HEIGHT 30 /**< Button height, standard across menus. */
#define menu_Open(f) (menu_open |= (f)) /**< Marks a menu as opened. */
#define menu_Close(f) (menu_open &= ~(f)) /**< Marks a menu as closed. */
int menu_open = 0; /**< Stores the opened/closed menus. */
static glTexture *main_naevLogo = NULL; /**< Naev Logo texture. */
/*
* prototypes
*/
/* Generic. */
static void menu_exit( unsigned int wid, char* str );
/* main menu */
static int menu_main_bkg_system (void);
static void main_menu_promptClose( unsigned int wid, char *unused );
static void menu_main_load( unsigned int wid, char* str );
static void menu_main_new( unsigned int wid, char* str );
static void menu_main_tutorial( unsigned int wid, char* str );
static void menu_main_credits( unsigned int wid, char* str );
static void menu_main_cleanBG( unsigned int wid, char* str );
/* small menu */
static void menu_small_close( unsigned int wid, char* str );
static void menu_small_info( unsigned int wid, char *str );
static void menu_small_exit( unsigned int wid, char* str );
static void exit_game (void);
/* death menu */
static void menu_death_continue( unsigned int wid, char* str );
static void menu_death_restart( unsigned int wid, char* str );
static void menu_death_main( unsigned int wid, char* str );
static void menu_death_close( unsigned int wid, char* str );
/* options button. */
static void menu_options_button( unsigned int wid, char *str );
static int menu_main_bkg_system (void)
{
nsave_t *ns;
int n;
const char *sys;
Planet *pnt;
double cx, cy;
/* Clean pilots. */
pilots_cleanAll();
sys = NULL;
/* Refresh saves. */
load_refresh();
/* Get start position. */
ns = load_getList( &n );
if ((n > 0) && (planet_exists( ns[0].planet ))) {
pnt = planet_get( ns[0].planet );
if (pnt != NULL) {
sys = planet_getSystem( ns[0].planet );
if (sys != NULL) {
cx = pnt->pos.x;
cy = pnt->pos.y;
cx += 300;
cy += 200;
}
}
}
/* Fallback if necessary. */
if (sys == NULL) {
sys = start_system();
start_position( &cx, &cy );
}
/* Initialize. */
space_init( sys );
cam_setTargetPos( cx, cy, 0 );
cam_setZoom( conf.zoom_far );
pause_setSpeed( 1. );
return 0;
}
/**
* @brief Opens the main menu (titlescreen).
*/
void menu_main (void)
{
int offset_logo, offset_wdw, freespace;
unsigned int bwid, wid;
glTexture *tex;
int h, y;
if (menu_isOpen(MENU_MAIN)) {
WARN("Menu main is already open.");
return;
}
/* Clean up GUI - must be done before using SCREEN_W or SCREEN_H. */
gui_cleanup();
player_soundStop(); /* Stop sound. */
/* Play load music. */
music_choose("load");
/* Load background and friends. */
tex = gl_newImage( GFX_PATH"Naev.png", 0 );
main_naevLogo = tex;
menu_main_bkg_system();
/* Set dimensions */
y = 20 + (BUTTON_HEIGHT+20)*5;
h = y + 80;
if (conf.devmode) {
h += BUTTON_HEIGHT + 20;
y += BUTTON_HEIGHT + 20;
}
/* Calculate Logo and window offset. */
freespace = SCREEN_H - tex->sh - h;
if (freespace < 0) { /* Not enough freespace, this can get ugly. */
offset_logo = SCREEN_W - tex->sh;
offset_wdw = 0;
}
/* Otherwise space evenly. */
else {
offset_logo = -freespace/4;
offset_wdw = freespace/2;
}
/* create background image window */
bwid = window_create( "BG", -1, -1, -1, -1 );
window_onClose( bwid, menu_main_cleanBG );
window_setBorder( bwid, 0 );
window_addImage( bwid, (SCREEN_W-tex->sw)/2., offset_logo, 0, 0, "imgLogo", tex, 0 );
window_addText( bwid, 0, 10, SCREEN_W, 30., 1, "txtBG", NULL,
&cWhite, naev_version(1) );
/* create menu window */
wid = window_create( "Main Menu", -1, offset_wdw, MAIN_WIDTH, h );
window_setCancel( wid, main_menu_promptClose );
/* Buttons. */
window_addButtonKey( wid, 20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnLoad", "Load Game", menu_main_load, SDLK_l );
y -= BUTTON_HEIGHT+20;
window_addButtonKey( wid, 20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnNew", "New Game", menu_main_new, SDLK_n );
y -= BUTTON_HEIGHT+20;
window_addButtonKey( wid, 20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnTutorial", "Tutorial", menu_main_tutorial, SDLK_t );
y -= BUTTON_HEIGHT+20;
if (conf.devmode) {
window_addButtonKey( wid, 20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnEditor", "Editor", uniedit_open, SDLK_e );
y -= BUTTON_HEIGHT+20;
}
window_addButtonKey( wid, 20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnOptions", "Options", menu_options_button, SDLK_o );
y -= BUTTON_HEIGHT+20;
window_addButtonKey( wid, 20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnCredits", "Credits", menu_main_credits, SDLK_c );
y -= BUTTON_HEIGHT+20;
window_addButtonKey( wid, 20, y, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnExit", "Exit", menu_exit, SDLK_x );
/* Disable load button if there are no saves. */
if (!save_hasSave())
window_disableButton( wid, "btnLoad" );
/* Make the background window a child of the menu. */
window_setParent( bwid, wid );
unpause_game();
menu_Open(MENU_MAIN);
}
/**
* @brief Resizes the main menu and its background.
*
* This is a one-off function that ensures the main menu's appearance
* is consistent regardless of window resizing.
*/
void menu_main_resize (void)
{
int w, h, bgw, bgh, tw, th;
int offset_logo, offset_wdw, freespace;
int menu_id, bg_id;
Widget *wgt;
if (!menu_isOpen(MENU_MAIN))
return;
menu_id = window_get("Main Menu");
bg_id = window_get("BG");
window_dimWindow( menu_id, &w, &h );
window_dimWindow( bg_id, &bgw, &bgh );
freespace = SCREEN_H - main_naevLogo->sh - h;
if (freespace < 0) {
offset_logo = SCREEN_H - main_naevLogo->sh;
offset_wdw = 0;
}
else {
offset_logo = -freespace/4;
offset_wdw = freespace/2;
}
window_moveWidget( bg_id, "imgLogo",
(bgw - main_naevLogo->sw)/2., offset_logo );
window_dimWidget( bg_id, "txtBG", &tw, &th );
if (tw > SCREEN_W) {
/* RIP abstractions. X must be set manually because window_moveWidget
* transforms negative coordinates. */
wgt = window_getwgt( bg_id, "txtBG" );
if (wgt)
wgt->x = (SCREEN_W - tw) / 2;
}
else
window_moveWidget( bg_id, "txtBG", (SCREEN_W - tw)/2, 10. );
window_move( menu_id, -1, offset_wdw );
}
/**
* @brief Main menu closing prompt.
*/
static void main_menu_promptClose( unsigned int wid, char *unused )
{
(void) wid;
(void) unused;
exit_game();
}
/**
* @brief Closes the main menu.
*/
void menu_main_close (void)
{
if (window_exists("Main Menu"))
window_destroy( window_get("Main Menu") );
else
WARN("Main menu does not exist.");
menu_Close(MENU_MAIN);
pause_game();
}
/**
* @brief Function to active the load game menu.
* @param str Unused.
*/
static void menu_main_load( unsigned int wid, char* str )
{
(void) str;
(void) wid;
load_loadGameMenu();
}
/**
* @brief Function to active the new game menu.
* @param str Unused.
*/
static void menu_main_new( unsigned int wid, char* str )
{
(void) str;
(void) wid;
/* Closes the main menu window. */
window_destroy( wid );
menu_Close(MENU_MAIN);
pause_game();
/* Start the new player. */
player_new();
}
/**
* @brief Function to active the new game menu.
* @param str Unused.
*/
static void menu_main_tutorial( unsigned int wid, char* str )
{
(void) str;
(void) wid;
window_destroy( wid );
menu_Close(MENU_MAIN);
pause_game();
player_newTutorial();
}
/**
* @brief Function to exit the main menu and game.
* @param str Unused.
*/
static void menu_main_credits( unsigned int wid, char* str )
{
(void) str;
(void) wid;
intro_display( "AUTHORS", "credits" );
/* We'll need to start music again. */
music_choose("load");
}
/**
* @brief Function to exit the main menu and game.
* @param str Unused.
*/
static void menu_exit( unsigned int wid, char* str )
{
(void) str;
(void) wid;
naev_quit();
}
/**
* @brief Function to clean up the background window.
* @param wid Window to clean.
* @param str Unused.
*/
static void menu_main_cleanBG( unsigned int wid, char* str )
{
(void) str;
/*
* Ugly hack to prevent player.c from segfaulting due to the fact
* that game will attempt to render while waiting for the quit event
* pushed by exit_game() to be handled without actually having a player
* nor anything of the likes (nor toolkit to stop rendering) while
* not leaking any texture.
*/
if (main_naevLogo != NULL)
gl_freeTexture(main_naevLogo);
main_naevLogo = NULL;
window_modifyImage( wid, "imgLogo", NULL, 0, 0 );
}
/*
*
* ingame menu
*
*/
/**
* @brief Opens the small ingame menu.
*/
void menu_small (void)
{
unsigned int wid;
/* Check if menu should be openable. */
if ((player.p == NULL) || player_isFlag(PLAYER_DESTROYED) ||
pilot_isFlag(player.p,PILOT_DEAD) ||
comm_isOpen() ||
dialogue_isOpen() || /* Shouldn't open over dialogues. */
(menu_isOpen(MENU_MAIN) ||
menu_isOpen(MENU_SMALL) ||
menu_isOpen(MENU_DEATH) ))
return;
wid = window_create( "Menu", -1, -1, MENU_WIDTH, MENU_HEIGHT );
window_setCancel( wid, menu_small_close );
window_addButtonKey( wid, 20, 20 + BUTTON_HEIGHT*3 + 20*3,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnResume", "Resume", menu_small_close, SDLK_r );
window_addButtonKey( wid, 20, 20 + BUTTON_HEIGHT*2 + 20*2,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnInfo", "Info", menu_small_info, SDLK_i );
window_addButtonKey( wid, 20, 20 + BUTTON_HEIGHT + 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnOptions", "Options", menu_options_button, SDLK_o );
window_addButtonKey( wid, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnExit", "Exit", menu_small_exit, SDLK_x );
menu_Open(MENU_SMALL);
}
/**
* @brief Closes the small ingame menu.
* @param str Unused.
*/
static void menu_small_close( unsigned int wid, char* str )
{
(void)str;
window_destroy( wid );
menu_Close(MENU_SMALL);
}
/**
* @brief Opens the info window.
* @param wid Unused.
* @param str Unused.
*/
static void menu_small_info( unsigned int wid, char *str )
{
(void) str;
(void) wid;
menu_info( INFO_MAIN );
}
/**
* @brief Closes the small ingame menu and goes back to the main menu.
* @param str Unused.
*/
static void menu_small_exit( unsigned int wid, char* str )
{
(void) str;
unsigned int info_wid, board_wid;
/* if landed we must save anyways */
if (landed) {
save_all();
land_cleanup();
}
/* Close info menu if open. */
if (menu_isOpen(MENU_INFO)) {
info_wid = window_get("Info");
window_destroy( info_wid );
menu_Close(MENU_INFO);
}
/* Force unboard. */
if (player_isBoarded()) {
board_wid = window_get("Boarding");
board_exit(board_wid, NULL);
}
/* Stop player sounds because sometimes they hang. */
player_restoreControl( 0, "Exited game." );
player_soundStop();
/* Clean up. */
window_destroy( wid );
menu_Close(MENU_SMALL);
menu_main();
}
/**
* @brief Exits the game.
*/
static void exit_game (void)
{
/* if landed we must save anyways */
if (landed) {
save_all();
land_cleanup();
}
SDL_Event quit;
quit.type = SDL_QUIT;
SDL_PushEvent(&quit);
}
/**
* @brief Reload the current savegame, when player want to continue after death
*/
static void menu_death_continue( unsigned int wid, char* str )
{
(void) str;
window_destroy( wid );
menu_Close(MENU_DEATH);
save_reload();
}
/**
* @brief Restart the game, when player want to continue after death but without a savegame
*/
static void menu_death_restart( unsigned int wid, char* str )
{
(void) str;
window_destroy( wid );
menu_Close(MENU_DEATH);
if (player_isTut())
player_newTutorial();
else
player_new();
}
/**
* @brief Player death menu, appears when player got creamed.
*/
void menu_death (void)
{
unsigned int wid;
char path[PATH_MAX];
wid = window_create( "Death", -1, -1, DEATH_WIDTH, DEATH_HEIGHT );
window_onClose( wid, menu_death_close );
/* Allow the player to continue if the savegame exists, if not, propose to restart */
nsnprintf(path, PATH_MAX, "%ssaves/%s.ns", nfile_dataPath(), player.name);
if (!player_isTut() && nfile_fileExists(path))
window_addButtonKey( wid, 20, 20 + BUTTON_HEIGHT*2 + 20*2, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnContinue", "Continue", menu_death_continue, SDLK_c );
else
window_addButtonKey( wid, 20, 20 + BUTTON_HEIGHT*2 + 20*2, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnRestart", "Restart", menu_death_restart, SDLK_r );
window_addButtonKey( wid, 20, 20 + (BUTTON_HEIGHT+20),
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnMain", "Main Menu", menu_death_main, SDLK_m );
window_addButtonKey( wid, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnExit", "Exit Game", menu_exit, SDLK_x );
menu_Open(MENU_DEATH);
/* Makes it all look cooler since everything still goes on. */
unpause_game();
}
/**
* @brief Closes the player death menu.
* @param str Unused.
*/
static void menu_death_main( unsigned int wid, char* str )
{
(void) str;
window_destroy( wid );
menu_Close(MENU_DEATH);
/* Game will repause now since toolkit closes and reopens. */
menu_main();
}
/**
* @brief Hack to get around the fact the death menu unpauses the game.
*/
static void menu_death_close( unsigned int wid, char* str )
{
(void) wid;
(void) str;
pause_game(); /* Repause the game. */
}
/**
* @brief Opens the menu options from a button.
*/
static void menu_options_button( unsigned int wid, char *str )
{
(void) wid;
(void) str;
opt_menu();
}
/**
* @brief Menu to ask if player really wants to quit.
*/
int menu_askQuit (void)
{
/* Asked twice, quit. */
if (menu_isOpen( MENU_ASKQUIT )) {
exit_game();
return 1;
}
/* Ask if should quit. */
menu_Open( MENU_ASKQUIT );
if (dialogue_YesNoRaw( "Quit Naev", "Are you sure you want to quit Naev?" )) {
exit_game();
return 1;
}
menu_Close( MENU_ASKQUIT );
return 0;
}