#include "driver2.h" #include "pause.h" #include "system.h" #include "mission.h" #include "overlay.h" #include "pres.h" #include "pad.h" #include "main.h" #include "glaunch.h" #include "scores.h" #include "sound.h" #include "cutscene.h" #include "overmap.h" #include "handling.h" #include "platform.h" #include "loadsave.h" #include "cutrecorder.h" #define REPLAY_NAME_LEN 16 #define SCORE_NAME_LEN 5 #define PAUSE_MENU_LEVELS 3 static int gScoreEntered = 0; static char EnterNameText[32] = { 0 }; // translated text static int PauseReturnValue; int pauseflag = 0; int gShowMap = 0; int gDrawPauseMenus = 0; int gEnteringScore = 0; static int gScorePosition = 0; static int allownameentry = 0; int playerwithcontrol[3] = { 0 }; // message box struct MENU_MESSAGE { char* header; char* text; int show; } gDisplayedMessage = { NULL, NULL, 0 }; typedef void(*pauseFunc)(int dir); struct MENU_ITEM; struct MENU_HEADER; struct MENU_ITEM { char* Text; u_char Type; u_char Justify; pauseFunc func; EXIT_VALUE ExitValue; MENU_HEADER* SubMenu; }; struct MENU_HEADER { char* Title; XYWH Bound; u_char NumItems; MENU_ITEM* MenuItems; }; static MENU_ITEM* ActiveItem[PAUSE_MENU_LEVELS]; static MENU_HEADER* VisibleMenus[PAUSE_MENU_LEVELS]; static MENU_HEADER* ActiveMenu; static int ActiveMenuItem; static int VisibleMenu; static char SfxVolumeText[8]; static char MusicVolumeText[8]; static char ScoreTime[5][16]; static char ScoreItems[5][16]; static char ScoreName[5][7]; static char validchars[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+!\xFF\xFE"; void PauseMap(int direction); void SfxVolume(int direction); void MusicVolume(int direction); void SaveReplay(int direction); void SaveGame(int direction); void SkipCutscene(int direction); void EnterScoreName(); void CreateScoreNames(SCORE_ENTRY *table, PLAYER_SCORE *score, int position); void DrawHighScoreMenu(int selection); char EnterScoreText[32] = { 0 }; enum MenuItemType { //PAUSE_TYPE_ACTIVE = (1 << 0), // suggested - USUSED flag PAUSE_TYPE_FUNC = (1 << 1), // simple function calling when pressing Cross PAUSE_TYPE_DIRFUNC = (1 << 2), // function calling with direction (left/right) PAUSE_TYPE_SFXVOLUME = (1 << 3), PAUSE_TYPE_MUSICVOLUME = (1 << 4), PAUSE_TYPE_SUBMENU = (1 << 6), PAUSE_TYPE_ENDITEMS = (1 << 7), }; #if defined(_DEBUG) || defined(DEBUG_OPTIONS) void SetRightWayUp(int direction) { extern char gRightWayUp; gRightWayUp = 1; PauseReturnValue = MENU_QUIT_CONTINUE; } void SetDisplayPosition(int direction) { extern int gDisplayPosition; gDisplayPosition ^= 1; } void ToggleInvincibility(int direction) { extern int gInvincibleCar; gInvincibleCar ^= 1; } void ToggleImmunity(int direction) { extern int gPlayerImmune; gPlayerImmune ^= 1; } void TogglePlayerGhost(int direction) { extern int playerghost; playerghost ^= 1; } void ToggleOverlays(int direction) { gDoOverlays ^= 1; } int lastCar = -1; void ToggleSecretCarFun(int direction) { extern CAR_COSMETICS car_cosmetics[5]; extern int wantedCar[2]; int active = (ActiveCheats.cheat10 ^= 1); if (active) { if (lastCar == -1) lastCar = wantedCar[0]; // make our current car the secret car wantedCar[0] = 12; } else { if (lastCar != -1) { // restore our initial car wantedCar[0] = lastCar; lastCar = -1; } } FixCarCos(&car_cosmetics[4], 12); } void ToggleJerichoMode(int direction) { ActiveCheats.cheat12 ^= 1; } void TogglePuppyDogCops(int direction) { gPuppyDogCop ^= 1; } extern void LoadSky(void); void DebugTimeOfDayDay(int direction) { wantedTimeOfDay = 1; gTimeOfDay = 1; gWantNight = 0; LoadSky(); } void DebugTimeOfDayNight(int direction) { wantedTimeOfDay = 3; gTimeOfDay = 3; gWantNight = 1; LoadSky(); } void DebugTimeOfDayDusk(int direction) { wantedTimeOfDay = 0; gTimeOfDay = 0; gWantNight = 0; LoadSky(); } void DebugTimeOfDayDawn(int direction) { wantedTimeOfDay = 2; gTimeOfDay = 2; gWantNight = 0; LoadSky(); } void DebugTimeOfDayRain(int direction) { //extern int weather; //weather ^= weather; gWeather ^= 1; wantedWeather = gWeather; if (gWeather == 1) wetness = 7000; else wetness = 0; LoadSky(); } MENU_ITEM DebugTimeOfDayItems[] = { { "Day", PAUSE_TYPE_FUNC, 2, DebugTimeOfDayDay, MENU_QUIT_NONE, NULL }, { "Night", PAUSE_TYPE_FUNC, 2, DebugTimeOfDayNight,MENU_QUIT_NONE, NULL }, { "Dusk", PAUSE_TYPE_FUNC, 2, DebugTimeOfDayDusk, MENU_QUIT_NONE, NULL }, { "Dawn", PAUSE_TYPE_FUNC, 2, DebugTimeOfDayDawn, MENU_QUIT_NONE, NULL }, { "Rain", PAUSE_TYPE_FUNC, 2, DebugTimeOfDayRain, MENU_QUIT_NONE, NULL }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_HEADER DebugTimeOfDayHeader = { "Time Of Day", { 0, 0, 0, 0 }, 0u, DebugTimeOfDayItems }; MENU_ITEM DebugJustForFunItems[] = { { "Secret Car Fun", PAUSE_TYPE_FUNC, 2, ToggleSecretCarFun, MENU_QUIT_RESTART, NULL }, { "Jericho Mode", PAUSE_TYPE_FUNC, 2, ToggleJerichoMode, MENU_QUIT_NONE, NULL }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_HEADER DebugJustForFunHeader = { "Just for fun", { 0, 0, 0, 0 }, 0u, DebugJustForFunItems }; MENU_ITEM DebugOptionsItems[] = { #ifdef CUTSCENE_RECORDER //{ gCutsceneRecorderPauseText, 5u, 2u, (pauseFunc)&NextCutsceneRecorderPlayer, MENU_QUIT_NONE, NULL }, { gCurrentChasePauseText, 5u, 2u, (pauseFunc)&CutRec_NextChase, MENU_QUIT_NONE, NULL }, #endif { "Display position", PAUSE_TYPE_FUNC, 2, SetDisplayPosition, MENU_QUIT_NONE, NULL}, { "Back on Wheels", PAUSE_TYPE_FUNC, 2, SetRightWayUp, MENU_QUIT_NONE, NULL}, { "Time of Day", PAUSE_TYPE_SUBMENU, 2, NULL, MENU_QUIT_NONE, &DebugTimeOfDayHeader }, { "Fun Cheats", PAUSE_TYPE_SUBMENU, 2, NULL, MENU_QUIT_NONE, &DebugJustForFunHeader }, { "Invincibility", PAUSE_TYPE_FUNC, 2, ToggleInvincibility,MENU_QUIT_NONE, NULL}, { "Immunity", PAUSE_TYPE_FUNC, 2, ToggleImmunity, MENU_QUIT_NONE, NULL}, { "Puppy Dog Cops", PAUSE_TYPE_FUNC, 2, TogglePuppyDogCops, MENU_QUIT_NONE, NULL }, { "Toggle Overlay", PAUSE_TYPE_FUNC, 2, ToggleOverlays, MENU_QUIT_NONE, NULL }, { "Player Ghost", PAUSE_TYPE_FUNC, 2, TogglePlayerGhost, MENU_QUIT_NONE, NULL }, { "Next Mission", 0, 2, NULL, MENU_QUIT_NEXTMISSION, NULL }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_HEADER DebugOptionsHeader = { "Debug Options", { 0, 0, 0, 0 }, 0u, DebugOptionsItems }; #endif MENU_ITEM YesNoRestartItems[] = { { G_LTXT_ID(GTXT_NO), 1u, 2u, NULL, MENU_QUIT_BACKMENU, NULL }, { G_LTXT_ID(GTXT_YES), 1u, 2u, NULL, MENU_QUIT_RESTART, NULL }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM YesNoQuitItems[] = { { G_LTXT_ID(GTXT_NO), 1u, 2u, NULL, MENU_QUIT_BACKMENU, NULL }, { G_LTXT_ID(GTXT_YES), 1u, 2u, NULL, MENU_QUIT_QUIT, NULL }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_HEADER YesNoRestartHeader = { G_LTXT_ID(GTXT_AreYouSure), { 0, 0, 0, 0 }, 0u, YesNoRestartItems }; MENU_HEADER YesNoQuitHeader = { G_LTXT_ID(GTXT_AreYouSure), { 0, 0, 0, 0 }, 0u, YesNoQuitItems }; MENU_ITEM MainPauseItems[] = { { G_LTXT_ID(GTXT_Continue), 1u, 2u, NULL, MENU_QUIT_CONTINUE, NULL }, { G_LTXT_ID(GTXT_ShowMap), PAUSE_TYPE_FUNC, 2u, (pauseFunc)&PauseMap, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_Restart), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { G_LTXT_ID(GTXT_SfxVolume), PAUSE_TYPE_SFXVOLUME | PAUSE_TYPE_DIRFUNC, 2u, (pauseFunc)&SfxVolume, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_MusicVolume), PAUSE_TYPE_MUSICVOLUME | PAUSE_TYPE_DIRFUNC, 2u, (pauseFunc)&MusicVolume, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_FilmDirector), 1u, 2u, NULL, MENU_QUIT_DIRECTOR, NULL}, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, #if defined(_DEBUG) || defined(DEBUG_OPTIONS) { "Debug Options", PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &DebugOptionsHeader }, #endif { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM MultiplayerPauseItems[] = { { G_LTXT_ID(GTXT_Continue), 1u, 2u, NULL, MENU_QUIT_CONTINUE, NULL }, { G_LTXT_ID(GTXT_Restart), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { G_LTXT_ID(GTXT_SfxVolume), PAUSE_TYPE_SFXVOLUME | PAUSE_TYPE_DIRFUNC, 2u, (pauseFunc)&SfxVolume, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_MusicVolume), PAUSE_TYPE_MUSICVOLUME | PAUSE_TYPE_DIRFUNC, 2u, (pauseFunc)&MusicVolume, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM CutscenePauseItems[] = { { G_LTXT_ID(GTXT_Continue), 1u, 2u, NULL, MENU_QUIT_CONTINUE, NULL }, { G_LTXT_ID(GTXT_SkipCutscene), PAUSE_TYPE_FUNC, 2u, (pauseFunc)&SkipCutscene, MENU_QUIT_CONTINUE, NULL }, { G_LTXT_ID(GTXT_Restart), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { G_LTXT_ID(GTXT_SfxVolume), PAUSE_TYPE_SFXVOLUME | PAUSE_TYPE_DIRFUNC, 2u, (pauseFunc)&SfxVolume, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_MusicVolume), PAUSE_TYPE_MUSICVOLUME | PAUSE_TYPE_DIRFUNC, 2u, (pauseFunc)&MusicVolume, MENU_QUIT_NONE, NULL }, #if defined(_DEBUG) || defined(DEBUG_OPTIONS) { "Debug Options", PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &DebugOptionsHeader }, #endif { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM MissionCompleteItems[] = { #ifdef PSX { G_LTXT_ID(GTXT_SaveGame), 3u, 2u, (pauseFunc)&SaveGame, MENU_QUIT_NONE, NULL }, #endif { G_LTXT_ID(GTXT_Continue), 1u, 2u, NULL, MENU_QUIT_NEXTMISSION, NULL }, { G_LTXT_ID(GTXT_FilmDirector),1u,2u,NULL,MENU_QUIT_DIRECTOR,NULL}, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, { G_LTXT_ID(GTXT_SaveReplay), PAUSE_TYPE_FUNC, 2u, (pauseFunc)&SaveReplay, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_Restart), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM MissionFailedItems[6] = { { G_LTXT_ID(GTXT_FilmDirector),1u,2u,NULL,MENU_QUIT_DIRECTOR,NULL}, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, { G_LTXT_ID(GTXT_Exit), 3u, 2u, (pauseFunc)&SaveReplay, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_RetryMission),PAUSE_TYPE_SUBMENU,2u,NULL,MENU_QUIT_NONE,&YesNoRestartHeader }, { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM TakeARideFinishedItems[] = { { G_LTXT_ID(GTXT_Restart), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { G_LTXT_ID(GTXT_FilmDirector),1u,2u,NULL,MENU_QUIT_DIRECTOR,NULL}, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, { G_LTXT_ID(GTXT_SaveReplay), PAUSE_TYPE_FUNC, 2u, (pauseFunc)&SaveReplay, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM DrivingGameFinishedItems[] = { { G_LTXT_ID(GTXT_PlayAgain), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { EnterScoreText, 3u, 2u, (pauseFunc)&EnterScoreName, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_FilmDirector),1u,2u,NULL,MENU_QUIT_DIRECTOR,NULL}, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, { G_LTXT_ID(GTXT_SaveReplay), PAUSE_TYPE_FUNC, 2u, (pauseFunc)&SaveReplay, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM MultiplayerFinishedItems[] = { { G_LTXT_ID(GTXT_PlayAgain), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, { G_LTXT_ID(GTXT_SaveReplay), PAUSE_TYPE_FUNC, 2u, (pauseFunc)&SaveReplay, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM ChaseGameFinishedItems[] = { { G_LTXT_ID(GTXT_PlayAgain), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoRestartHeader }, { G_LTXT_ID(GTXT_FilmDirector),1u,2u,NULL,MENU_QUIT_DIRECTOR,NULL}, { G_LTXT_ID(GTXT_QuickReplay),1u,2u,NULL,MENU_QUIT_QUICKREPLAY,NULL}, { G_LTXT_ID(GTXT_SaveReplay), PAUSE_TYPE_FUNC, 2u, (pauseFunc)&SaveReplay, MENU_QUIT_NONE, NULL }, { G_LTXT_ID(GTXT_Exit), PAUSE_TYPE_SUBMENU, 2u, NULL, MENU_QUIT_NONE, &YesNoQuitHeader }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM NoPadItems[] = { { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL} }; MENU_ITEM NoMultiPadItems[] = { { "Exit", 1u, 2u, NULL, MENU_QUIT_QUIT, NULL }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_ITEM InvalidPadItems[] = { { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL} }; MENU_ITEM InvalidMultiPadItems[] = { { G_LTXT_ID(GTXT_Exit), 1u, 2u, NULL, MENU_QUIT_QUIT, NULL }, { NULL, PAUSE_TYPE_ENDITEMS, 0u, NULL, MENU_QUIT_NONE, NULL } }; MENU_HEADER PauseMenuHeader = { G_LTXT_ID(GTXT_Paused), { 0, 0, 0, 0 }, 0u, MainPauseItems }; MENU_HEADER MultiplayerPauseHeader = { G_LTXT_ID(GTXT_Paused), { 0, 0, 0, 0 }, 0u, MultiplayerPauseItems }; MENU_HEADER CutscenePauseMenuHeader = { G_LTXT_ID(GTXT_Paused), { 0, 0, 0, 0 }, 0u, CutscenePauseItems }; MENU_HEADER MissionCompleteHeader = { G_LTXT_ID(GTXT_MissionSuccessful), { 0, 0, 0, 0 }, 0u, MissionCompleteItems }; MENU_HEADER MissionFailedHeader = { G_LTXT_ID(GTXT_MissionFailed), { 0, 0, 0, 0 }, 0u, MissionFailedItems }; MENU_HEADER TakeARideFinishedHeader = { G_LTXT_ID(GTXT_GameOver), { 0, 0, 0, 0 }, 0u, TakeARideFinishedItems }; MENU_HEADER DrivingGameFinishedHeader = { G_LTXT_ID(GTXT_GameOver), { 0, 0, 0, 0 }, 0u, DrivingGameFinishedItems }; MENU_HEADER MultiplayerFinishedHeader = { G_LTXT_ID(GTXT_GameOver), { 0, 0, 0, 0 }, 0u, MultiplayerFinishedItems }; MENU_HEADER ChaseGameFinishedHeader = { G_LTXT_ID(GTXT_GameOver), { 0, 0, 0, 0 }, 0u, ChaseGameFinishedItems }; MENU_HEADER NoPadHeader = { G_LTXT_ID(GTXT_InsertController1), { 0, 0, 0, 0 }, 0u, NoPadItems }; MENU_HEADER NoMultiPadHeader = { G_LTXT_ID(GTXT_InsertController2), { 0, 0, 0, 0 }, 0u, NoMultiPadItems }; MENU_HEADER InvalidPadHeader = { G_LTXT_ID(GTXT_IncorrectController1), { 0, 0, 0, 0 }, 0u, InvalidPadItems }; MENU_HEADER InvalidMultiPadHeader = { G_LTXT_ID(GTXT_IncorrectController2), { 0, 0, 0, 0 }, 0u, InvalidMultiPadItems }; u_char gCurrentTextChar = 0; typedef void(*OnEntryComplete)(void* data, char* text); void ScoreNameInputHandler(const char* input) { if (!input) { gCurrentTextChar = 255; return; } gCurrentTextChar = *input; } #define USE_PAD_INPUT defined(PSX) // [A] Enter the replay name to save char* WaitForTextEntry(char* textBufPtr, int maxLength) { u_char chr; int so, co; int delay, toggle; char* username; unsigned short npad, dpad; username = textBufPtr ? textBufPtr : (char*)_overlay_buffer; delay = 0; toggle = 0; co = 1; so = strlen(username); #if !USE_PAD_INPUT // PsyX input handler g_cfg_gameOnTextInput = ScoreNameInputHandler; gCurrentTextChar = 0; #endif do { if (!FilterFrameTime()) continue; ReadControllers(); npad = Pads[0].dirnew; dpad = Pads[0].direct; // cancel if (npad & 0x10) return NULL; #if USE_PAD_INPUT if (dpad & 0x20) { // switch to end delay = 1; toggle = 0; co = 67; } else if (dpad & 0x8000) { // move left/right to switch chars if (delay-- == 0) { delay = 20; toggle = 0; co--; } else if (delay < 1) { delay = 2; toggle = 0; co--; } if (co < 0) co = 67; } else if (dpad & 0x2000) { if (delay-- == 0) { delay = 20; toggle = 0; co++; } else if (delay < 1) { delay = 2; toggle = 0; co++; } if (co > 67) co = 0; } else { delay = 0; } if (so == maxLength) chr = 254; else chr = validchars[co]; #else if (gCurrentTextChar > 0) { if (gCurrentTextChar == 255) { // do backspace chr = 255; } else { // Find a valid character for (co = 0; co < 69; co++) { if (validchars[co] == gCurrentTextChar) break; } if (so == maxLength) { chr = 254; gCurrentTextChar = 0; } else chr = validchars[co]; } } else { if (so == maxLength) chr = 254; else chr = 'I'; } if (npad & 0x40) { gCurrentTextChar = 254; chr = 254; } #endif toggle++; if (toggle & 4) username[so] = ' '; else if (chr == ' ') username[so] = '.'; else username[so] = chr; #if USE_PAD_INPUT if (npad & 0x80) { if (so > 0) so--; username[so] = 0; username[so + 1] = 0; } if (npad & 0x40) { #else if (gCurrentTextChar > 0) { gCurrentTextChar = 0; #endif // complete if (chr == 254) { username[so] = 0; break; } // delete chars if (chr == 255) { if (so > 0) so--; username[so] = 0; username[so + 1] = 0; } else if (so < maxLength) { username[so] = chr; username[so + 1] = 0; so++; } } DrawGame(); #ifdef __EMSCRIPTEN__ emscripten_sleep(0); #endif } while (true); #if !USE_PAD_INPUT g_cfg_gameOnTextInput = NULL; #endif return username; } void SkipCutscene(int direction) { gSkipInGameCutscene = 1; } // [D] [T] void SaveReplay(int direction) { char* result; char filename[64]; #ifdef PSX UNIMPLEMENTED(); // CallMemoryCard(0x10, 1); #else #ifdef CUTSCENE_RECORDER if(_CutRec_IsOn()) { if (CutRec_SaveChase()) { gDisplayedMessage.header = G_LTXT(GTXT_SaveReplay); gDisplayedMessage.text = G_LTXT(GTXT_OK); gDisplayedMessage.show = 25; } else { gDisplayedMessage.header = G_LTXT(GTXT_SaveReplay); gDisplayedMessage.text = G_LTXT(GTXT_SavingError); gDisplayedMessage.show = 15; } } else #endif // CUTSCENE_RECORDER { int cnt; FILE* temp; _mkdir("Replays"); ClearMem(EnterNameText, REPLAY_NAME_LEN); // detect the best file name // TODO: if replay is loaded - set the loaded replay filename if (gLoadedReplay) { strcpy(EnterNameText, gCurrentReplayFilename); } else { cnt = 1; while (cnt < 1000) { sprintf(EnterNameText, "Chase%d", cnt); sprintf(filename, "Replays/%s.D2RP", EnterNameText); if ((temp = fopen(filename, "r")) != NULL) { fclose(temp); cnt++; } else break; } } gDisplayedMessage.header = G_LTXT(GTXT_EnterName); gDisplayedMessage.text = EnterNameText; gDisplayedMessage.show = -1; // wait for user input the replay name result = WaitForTextEntry(EnterNameText, REPLAY_NAME_LEN); gDisplayedMessage.show = 0; if (result) { sprintf(filename, "Replays/%s.D2RP", result); if (SaveReplayToFile(filename)) { gDisplayedMessage.header = G_LTXT(GTXT_SaveReplay); gDisplayedMessage.text = G_LTXT(GTXT_OK); gDisplayedMessage.show = 15; } else { gDisplayedMessage.header = G_LTXT(GTXT_SaveReplay); gDisplayedMessage.text = G_LTXT(GTXT_SavingError); gDisplayedMessage.show = 15; } } } #endif // PSX } // [D] [T] void SaveGame(int direction) { #ifndef PSX SaveCurrentGame(); #endif } // [D] [T] int MaxMenuStringLength(MENU_HEADER *pMenu) { int max; int temp; MENU_ITEM *pItems; pItems = pMenu->MenuItems; max = StringWidth(GET_GAME_TXT(pMenu->Title)); while ((pItems->Type & PAUSE_TYPE_ENDITEMS) == 0) { temp = StringWidth(GET_GAME_TXT(pItems->Text)); if (pItems->Type & (PAUSE_TYPE_SFXVOLUME | PAUSE_TYPE_MUSICVOLUME)) temp = temp + StringWidth(" 100"); if (temp > max) max = temp; pItems++; } return max; } // [D] [T] void SetupMenu(MENU_HEADER *menu, int back) { int len; MENU_ITEM *pItem; int numItems; numItems = 0; ActiveMenuItem = 0; pItem = menu->MenuItems; while (pItem->Type != PAUSE_TYPE_ENDITEMS) { if (back && pItem == ActiveItem[VisibleMenu]) ActiveMenuItem = numItems; numItems++; pItem++; } ActiveMenu = menu; ActiveMenu->NumItems = numItems; len = MaxMenuStringLength(ActiveMenu); ActiveMenu->Bound.x = ((304 - len) / 2) - 4; ActiveMenu->Bound.w = len + 24; ActiveMenu->Bound.y = MAX(48, (SCREEN_H - (numItems + 1) * 15) / 2); ActiveMenu->Bound.h = (numItems + 1) * 15 + 10; ActiveItem[VisibleMenu] = &ActiveMenu->MenuItems[ActiveMenuItem]; } // [D] [T] void InitaliseMenu(PAUSEMODE mode) { MENU_HEADER* pNewMenu; int i; for (i = 0; i < PAUSE_MENU_LEVELS; i++) { ActiveItem[i] = NULL; VisibleMenus[i] = NULL; } pNewMenu = NULL; allownameentry = 0; switch (mode) { case PAUSEMODE_PAUSE: case PAUSEMODE_PAUSEP1: case PAUSEMODE_PAUSEP2: if (NumPlayers == 1 && gMultiplayerLevels == 0) { if (gInGameCutsceneActive == 0) pNewMenu = &PauseMenuHeader; else pNewMenu = &CutscenePauseMenuHeader; } else pNewMenu = &MultiplayerPauseHeader; break; case PAUSEMODE_GAMEOVER: switch (GameType) { case GAME_PURSUIT: pNewMenu = &ChaseGameFinishedHeader; gMissionCompletionState = mode; break; case GAME_GETAWAY: case GAME_CHECKPOINT: if (NumPlayers == 1) { pNewMenu = &DrivingGameFinishedHeader; gMissionCompletionState = mode; } else { pNewMenu = &MultiplayerFinishedHeader; gMissionCompletionState = mode; } break; case GAME_GATERACE: case GAME_TRAILBLAZER: case GAME_SURVIVAL: if (NumPlayers == 1) { pNewMenu = &DrivingGameFinishedHeader; gMissionCompletionState = mode; allownameentry = 1; } else { pNewMenu = &MultiplayerFinishedHeader; gMissionCompletionState = mode; } break; default: if (NumPlayers == 1) { pNewMenu = &TakeARideFinishedHeader; gMissionCompletionState = mode; } else { pNewMenu = &MultiplayerFinishedHeader; gMissionCompletionState = mode; } break; } break; case PAUSEMODE_COMPLETE: switch (GameType) { case GAME_MISSION: pNewMenu = &MissionCompleteHeader; gMissionCompletionState = mode; break; case GAME_GETAWAY: case GAME_GATERACE: case GAME_CHECKPOINT: case GAME_TRAILBLAZER: case GAME_SURVIVAL: case GAME_COPSANDROBBERS: case GAME_CAPTURETHEFLAG: if (NumPlayers == 1) { pNewMenu = &DrivingGameFinishedHeader; gMissionCompletionState = mode; allownameentry = 1; } else { pNewMenu = &MultiplayerFinishedHeader; gMissionCompletionState = mode; } break; case GAME_PURSUIT: pNewMenu = &ChaseGameFinishedHeader; gMissionCompletionState = mode; break; default: if (NumPlayers == 1) { pNewMenu = &TakeARideFinishedHeader; gMissionCompletionState = mode; } else { pNewMenu = &MultiplayerFinishedHeader; gMissionCompletionState = mode; } break; } break; case PAUSEMODE_PADERROR: if (pad_connected < 0) { if (NumPlayers == 1) pNewMenu = &InvalidPadHeader; else pNewMenu = &InvalidMultiPadHeader; if (Pads[0].type == 1) pNewMenu->Title = G_LTXT(GTXT_IncorrectController1); else pNewMenu->Title = G_LTXT(GTXT_IncorrectController2); } else { if (NumPlayers == 1) pNewMenu = &NoPadHeader; else pNewMenu = &NoMultiPadHeader; if (Pads[0].type == 0) pNewMenu->Title = G_LTXT(GTXT_InsertController1); else pNewMenu->Title = G_LTXT(GTXT_InsertController2); } break; } // [A] if(pNewMenu) { VisibleMenu = 0; VisibleMenus[VisibleMenu] = pNewMenu; if (NoPlayerControl == 0 && OnScoreTable(NULL) != -1 && allownameentry) { gScoreEntered = 0; sprintf(EnterScoreText, G_LTXT(GTXT_EnterScore)); sprintf(EnterNameText, G_LTXT(GTXT_EnterName)); } else { gScoreEntered = 1; sprintf(EnterScoreText, G_LTXT(GTXT_ViewTable)); sprintf(EnterNameText, G_LTXT(GTXT_HighScores)); } SetupMenu(pNewMenu, 0); } else { printError("pNewMenu is NULL!\n"); } } // [D] [T] void DrawVisibleMenus(void) { int width; int itemXpos; POLY_FT3 *null; POLY_F4 *poly; MENU_ITEM *pItem; MENU_HEADER *pActive; int r, b; int i; int xpos, ypos; int menuWidth; int x1, x2, y1, y2; static int maxPercentageWidth = StringWidth(" 100"); if (NumPlayers > 1) { SetFullscreenDrawing(); } pActive = VisibleMenus[VisibleMenu]; xpos = pActive->Bound.x; ypos = pActive->Bound.y; menuWidth = pActive->Bound.w; if (pActive->Title) { OutputString(GET_GAME_TXT(pActive->Title), 2, xpos, ypos, menuWidth, 128, 32, 32); ypos += 15; } // show all menu items for (i = 0; i < pActive->NumItems; i++) { pItem = &pActive->MenuItems[i]; if (!pItem->Text) continue; if (pItem == ActiveItem[VisibleMenu]) { r = 0; b = 0; } else { b = 128; r = 128; } if(pItem->Type & (PAUSE_TYPE_SFXVOLUME | PAUSE_TYPE_MUSICVOLUME)) { width = StringWidth(GET_GAME_TXT(pItem->Text)); itemXpos = xpos + ((menuWidth - width) - maxPercentageWidth) / 2; OutputString(GET_GAME_TXT(pItem->Text), 1, itemXpos, ypos, menuWidth, r, 128, b); if (pItem->Type & PAUSE_TYPE_SFXVOLUME) OutputString(SfxVolumeText, 1, itemXpos + width + 10, ypos, menuWidth, r, 128, b); else if (pItem->Type & PAUSE_TYPE_MUSICVOLUME) OutputString(MusicVolumeText, 1, itemXpos + width + 10, ypos, menuWidth, r, 128, b); } else { OutputString(GET_GAME_TXT(pItem->Text), pItem->Justify, xpos, ypos, menuWidth, r, 128, b); } ypos += 15; } poly = (POLY_F4*)current->primptr; ypos = pActive->Bound.y; setPolyF4(poly); x1 = xpos - 5; x2 = xpos + menuWidth + 5; y1 = ypos - 5; y2 = ypos + pActive->Bound.h; poly->x0 = x1; poly->x1 = x2; poly->y0 = y1; poly->x2 = x1; poly->y1 = y1; poly->y2 = y2; poly->x3 = x2; poly->y3 = y2; poly->r0 = 16; poly->g0 = 16; poly->b0 = 16; setSemiTrans(poly, 1); addPrim(current->ot, poly); current->primptr += sizeof(POLY_F4); null = (POLY_FT3 *)current->primptr; setPolyFT3(null); null->x0 = -1; null->y0 = -1; null->x1 = -1; null->y1 = -1; null->x2 = -1; null->y2 = -1; null->tpage = 0; addPrim(current->ot, null); current->primptr += sizeof(POLY_FT3); } // [D] [T] void ControlMenu(void) { static int controlmenu_debounce = 0; MENU_ITEM* pItem; int i; ushort paddata; MENU_HEADER* menu; ushort paddirect; int doit; if (playerwithcontrol[2] == 0) { paddata = Pads[1].dirnew; paddirect = Pads[1].direct; if (playerwithcontrol[0]) { paddata = Pads[0].dirnew; paddirect = Pads[0].direct; } } else { paddata = Pads[0].dirnew; paddirect = Pads[0].direct; if (NumPlayers == 2) { paddata = Pads[1].dirnew | Pads[0].dirnew; paddirect = Pads[1].direct | Pads[0].direct; } } // toggle map off if (gShowMap) { if (paddata & (MPAD_CROSS | MPAD_TRIANGLE)) PauseMap(0); return; } pItem = ActiveItem[VisibleMenu]; if ((paddirect & 0xA000) && (pItem->Type & PAUSE_TYPE_DIRFUNC)) { doit = 0; if (controlmenu_debounce == 0) { doit = 1; controlmenu_debounce = 10; } else { controlmenu_debounce--; if (controlmenu_debounce == 0) { doit = 1; controlmenu_debounce = 2; } } if (doit) { // left/right (*pItem->func)((paddirect & 0x8000) ? -1 : 1); } return; } controlmenu_debounce = 0; #ifndef PSX // Pause fix for PC mapping if ((paddata & MPAD_TRIANGLE) && paddata & (MPAD_D_UP | MPAD_D_DOWN)) { paddata = 0; } #endif if (paddata & MPAD_D_UP) { // go up ActiveMenuItem--; if (ActiveMenuItem < 0) ActiveMenuItem = ActiveMenu->NumItems - 1; ActiveItem[VisibleMenu] = &ActiveMenu->MenuItems[ActiveMenuItem]; } else if (paddata & MPAD_D_DOWN) { // go down ActiveMenuItem++; if (ActiveMenuItem > ActiveMenu->NumItems - 1) ActiveMenuItem = 0; ActiveItem[VisibleMenu] = &ActiveMenu->MenuItems[ActiveMenuItem]; } else if (paddata & MPAD_CROSS) { // Enter submenu if (pItem->Type & PAUSE_TYPE_SUBMENU) { menu = pItem->SubMenu; VisibleMenu++; VisibleMenus[VisibleMenu] = menu; SetupMenu(menu, 0); return; } // function flag if (pItem->Type & PAUSE_TYPE_FUNC) (*pItem->func)(0); if (pItem->ExitValue == MENU_QUIT_NONE) return; if (pItem->ExitValue == MENU_QUIT_BACKMENU) { VisibleMenu--; // go back in menu stack SetupMenu(VisibleMenus[VisibleMenu], 1); } else PauseReturnValue = pItem->ExitValue; } else if ((paddata & MPAD_TRIANGLE) || (paddata & MPAD_START)) // Triangle or Start { // continue game if needed if (VisibleMenu == 0) { #ifndef PSX // hack for keyboard swap if(!(paddata & MPAD_START)) return; #endif for (i = 0; i < ActiveMenu->NumItems; i++) { pItem = &ActiveMenu->MenuItems[i]; if (pItem->ExitValue == MENU_QUIT_CONTINUE) { PauseReturnValue = pItem->ExitValue; return; } } } else { // only triangle if(paddata & 0x10) { VisibleMenu--; SetupMenu(VisibleMenus[VisibleMenu], 1); } } } } // [D] [T] void PauseMap(int direction) { gShowMap ^= 1; ReadControllers(); map_x_shift = 0; map_z_shift = 0; if (gShowMap == 0) InitOverheadMap(); } // [D] [T] void SfxVolume(int direction) { if (direction < 0) gMasterVolume = gMasterVolume + -100; else if (0 < direction) gMasterVolume = gMasterVolume + 100; if (gMasterVolume < -10000) gMasterVolume = -10000; if (gMasterVolume > 0) gMasterVolume = 0; sprintf(SfxVolumeText, "%d", (10000 + gMasterVolume) / 100); SetMasterVolume(gMasterVolume); } // [D] [T] void MusicVolume(int direction) { if (direction < 0) gMusicVolume = gMusicVolume + -100; else if (0 < direction) gMusicVolume = gMusicVolume + 100; if (gMusicVolume < -10000) gMusicVolume = -10000; if (gMusicVolume > 0) gMusicVolume = 0; sprintf(MusicVolumeText, "%d", (10000 + gMusicVolume) / 100); SetXMVolume(gMusicVolume); } // [D] [T] void EnterScoreName(void) { u_char chr; char* username; char* enteredName; SCORE_ENTRY* table; unsigned short npad, dpad; username = NULL; if (!gScoreEntered) { gScorePosition = OnScoreTable(&table); if (gScorePosition != -1) username = ScoreName[gScorePosition]; CreateScoreNames(table, &gPlayerScore, gScorePosition); } else { gScorePosition = -1; } gEnteringScore = 1; enteredName = WaitForTextEntry(username, SCORE_NAME_LEN); if (enteredName && username) { strcpy(gPlayerScore.name, enteredName); AddScoreToTable(table, gScorePosition); sprintf(EnterScoreText, G_LTXT(GTXT_ViewTable)); sprintf(EnterNameText, G_LTXT(GTXT_HighScores)); gScoreEntered = 1; } gEnteringScore = 0; } void DisplayMenuMessage(char* header, char* text) { POLY_FT3* null; POLY_F4* prim; int i; int ypos; OutputString(header, 2, 160, 105, 0, 128, 32, 32); ypos = 125; OutputString(text, 2, 160, ypos, 0, 128, 128, 128); // TODO: Multiline support /* i = 0; do { OutputString(text, 2, 160, ypos, 0, 128, 128, 128); ypos += 15; i++; } while (i < 5); */ prim = (POLY_F4*)current->primptr; setPolyF4(prim); setSemiTrans(prim, 1); setShadeTex(prim, 1); prim->y0 = 100; prim->y1 = 100; prim->y2 = 160; prim->y3 = 160; prim->x0 = 80; prim->x1 = 240; prim->x2 = 80; prim->x3 = 240; prim->r0 = 16; prim->g0 = 16; prim->b0 = 16; addPrim(current->ot, prim); current->primptr += sizeof(POLY_F4); null = (POLY_FT3*)current->primptr; setPolyFT3(null); null->x0 = -1; null->y0 = -1; null->x1 = -1; null->y1 = -1; null->x2 = -1; null->y2 = -1; null->tpage = 0; addPrim(current->ot, null); current->primptr += sizeof(POLY_FT3); } // [D] [T] void DrawHighScoreMenu(int selection) { POLY_FT3* null; POLY_F4* prim; int i; int r,b; int ypos; char text[8]; OutputString(EnterNameText, 2, 160, 70, 0, 128, 32, 32); OutputString(G_LTXT(GTXT_Name), 1, 40, 90, 0, 128, 128, 32); OutputString(G_LTXT(GTXT_Time), 4, 280, 90, 0, 128, 128, 32); ypos = 110; i = 0; do { if (i == selection) { r = 0; b = 0; } else { r = 128; b = 128; } sprintf(text, "%d", i+1); OutputString(text, 1, 40, ypos, 0, r, 128, b); OutputString(ScoreName[i], 1, 60, ypos, 0, r, 128, b); OutputString(ScoreItems[i], 4, 220, ypos, 0, r, 128, b); OutputString(ScoreTime[i], 4, 280, ypos, 0, r, 128, b); ypos += 15; i++; } while (i < 5); prim = (POLY_F4*)current->primptr; setPolyF4(prim); setSemiTrans(prim, 1); setShadeTex(prim, 1); prim->y0 = 65; prim->y1 = 65; prim->y2 = 197; prim->y3 = 197; prim->x0 = 26; prim->x1 = 294; prim->x2 = 26; prim->x3 = 294; prim->r0 = 16; prim->g0 = 16; prim->b0 = 16; addPrim(current->ot, prim); current->primptr += sizeof(POLY_F4); null = (POLY_FT3*)current->primptr; setPolyFT3(null); null->x0 = -1; null->y0 = -1; null->x1 = -1; null->y1 = -1; null->x2 = -1; null->y2 = -1; null->tpage = 0; addPrim(current->ot, null); current->primptr += sizeof(POLY_FT3); } int mytolower(int ch) { if (ch >= 'A' && ch <= 'Z') return ('a' + ch - 'A'); else return ch; } void strlower(char* str) { while (*str != '\0') { *str = mytolower(*str); str++; } } // [D] [T] void CreateScoreNames(SCORE_ENTRY* table, PLAYER_SCORE* score, int position) { int time; char* text; int i; switch (GameType) { case GAME_PURSUIT: case GAME_GETAWAY: case GAME_CHECKPOINT: case GAME_SURVIVAL: text = NULL; break; case GAME_GATERACE: text = G_LTXT(GTXT_Gates); break; case GAME_TRAILBLAZER: text = G_LTXT(GTXT_Cones); break; default: printError("CreateScoreNames: Invalid game type\n"); return; } i = 0; do { if (i == position) { time = score->time; if (time == -1) sprintf(ScoreTime[i], "-:--.--"); else sprintf(ScoreTime[i], "%d:%02d.%02d", time / 180000, time / 3000 + (time / 180000) * -0x3c, (time % 3000) / 0x1e); ScoreItems[i][0] = '\0'; if (text != NULL && score->items != -1) { sprintf(ScoreItems[i], "%d %s", score->items, text); strlower(ScoreItems[i]); } ClearMem(ScoreName[i], 7); } else { time = table->time; if (time == -1) sprintf(ScoreTime[i], "-:--.--"); else sprintf(ScoreTime[i], "%d:%02d.%02d", time / 180000, time / 3000 + (time / 180000) * -0x3c, (time % 3000) / 0x1e); ScoreItems[i][0] = '\0'; if (text != NULL && table->items != -1) { sprintf(ScoreItems[i], "%d %s", table->items, text); strlower(ScoreItems[i]); } sprintf(ScoreName[i], "%s", table->name); table++; } i++; } while (i < 5); } // [A] int UpdatePauseMenu(PAUSEMODE mode) { PAUSEMODE passed_mode; passed_mode = mode; if (mode == PAUSEMODE_PADERROR) mode = PAUSEMODE_PAUSE; if (passed_mode == PAUSEMODE_PADERROR) { if (pad_connected == 1) InitaliseMenu(mode); else InitaliseMenu(PAUSEMODE_PADERROR); } else { if (pad_connected != 1) InitaliseMenu(PAUSEMODE_PADERROR); } if (pad_connected < 1) playerwithcontrol[2] = 1; ControlMenu(); if (PauseReturnValue != 0) gDrawPauseMenus = 0; return PauseReturnValue; } // [D] [T] void ShowPauseMenu(PAUSEMODE mode) { ReadControllers(); if (mode == PAUSEMODE_PAUSEP1) { playerwithcontrol[0] = 1; playerwithcontrol[1] = 0; playerwithcontrol[2] = 0; } else if (mode == PAUSEMODE_PAUSEP2) { playerwithcontrol[0] = 0; playerwithcontrol[1] = 1; playerwithcontrol[2] = 0; } else { playerwithcontrol[0] = 0; playerwithcontrol[1] = 0; playerwithcontrol[2] = 1; } SetDispMask(1); SfxVolume(0); MusicVolume(0); StopPadVibration(0); StopPadVibration(1); InitaliseMenu(mode); gDrawPauseMenus = 1; PauseReturnValue = 0; } // [D] [T] void DrawPauseMenus(void) { int displayMessage; #if !defined(PSX) && defined(DEBUG_OPTIONS) extern int g_FreeCameraEnabled; if (g_FreeCameraEnabled) return; #endif displayMessage = gDisplayedMessage.show == -1 || gDisplayedMessage.show > 0; if ((gDrawPauseMenus || displayMessage) && gShowMap == 0) { if (displayMessage) { DisplayMenuMessage(gDisplayedMessage.header, gDisplayedMessage.text); if(gDisplayedMessage.show > 0) gDisplayedMessage.show--; } else if (gEnteringScore) DrawHighScoreMenu(gScorePosition); else DrawVisibleMenus(); } }