diff --git a/engine/common.h b/engine/common.h index f028486e..246428da 100644 --- a/engine/common.h +++ b/engine/common.h @@ -227,7 +227,7 @@ edict_t *CL_GetEdictByIndex( int index ); edict_t *CL_GetLocalPlayer( void ); int CL_GetMaxClients( void ); byte CL_GetMouthOpen( int entityIndex ); -bool SV_GetComment( char *comment, int savenum ); +bool SV_GetComment( const char *savename, char *comment ); void SV_ForceMod( void ); void CL_MouseEvent( int mx, int my ); void CL_AddLoopingSounds( void ); diff --git a/engine/common/con_keys.c b/engine/common/con_keys.c index 522c1166..a9a6f061 100644 --- a/engine/common/con_keys.c +++ b/engine/common/con_keys.c @@ -1124,8 +1124,14 @@ void Key_Event( int key, bool down, int time ) return; case key_console: if( cls.state == ca_active ) + { cls.key_dest = key_game; - else UI_SetActiveMenu( UI_MAINMENU ); + } + else + { + UI_SetActiveMenu( UI_MAINMENU ); + return; // don't pass Esc into menu + } break; case key_menu: UI_KeyEvent( key ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index df8209c5..02d57756 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -3842,4 +3842,7 @@ void SV_LoadProgs( const char *name ) // initialize pm_shared SV_InitClientMove(); + + // fire once + MsgDev( D_INFO, "Dll loaded for mod %s\n", svgame.dllFuncs.pfnGetGameDescription() ); } \ No newline at end of file diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 33b073ad..1f85b7bf 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -373,9 +373,6 @@ void SV_InitGame( void ) if( !svgame.hInstance ) SV_LoadProgs( "server" ); - // fire once - MsgDev( D_INFO, "Dll loaded for mod %s\n", svgame.dllFuncs.pfnGetGameDescription() ); - // make sure the client is down CL_Drop(); } diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 130428d6..80821987 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -323,11 +323,13 @@ static void SV_SaveServerData( wfile_t *f, const char *name, bool bUseLandMark ) SV_WriteSaveFile ============= */ -void SV_WriteSaveFile( const char *name, bool autosave, bool bUseLandmark ) +void SV_WriteSaveFile( const char *inname, bool autosave, bool bUseLandmark ) { wfile_t *savfile = NULL; - char path[MAX_SYSPATH]; - + int n, minutes, tens, units; + string path, name; + float total; + if( sv.state != ss_active ) return; if( svgame.globals->deathmatch || svgame.globals->coop || svgame.globals->teamplay ) { @@ -340,6 +342,10 @@ void SV_WriteSaveFile( const char *name, bool autosave, bool bUseLandmark ) return; } + if( !com.stricmp( inname, "new" )) + com.strncpy( name, timestamp( TIME_FILENAME ), sizeof( name )); + else com.strncpy( name, inname, sizeof( name )); + com.sprintf( path, "save/%s.bin", name ); savfile = WAD_Open( path, "wb" ); @@ -349,11 +355,18 @@ void SV_WriteSaveFile( const char *name, bool autosave, bool bUseLandmark ) return; } + // calc time + total = svgame.globals->time; + minutes = (int)total / 60; + n = total - minutes * 60; + tens = n / 10; + units = n % 10; + // split comment to sections com.strncpy( svs.comment, sv.configstrings[CS_NAME], CS_SIZE ); com.strncpy( svs.comment + CS_SIZE, timestamp( TIME_DATE_ONLY ), CS_TIME ); com.strncpy( svs.comment + CS_SIZE + CS_TIME, timestamp( TIME_NO_SECONDS ), CS_TIME ); - com.strncpy( svs.comment + CS_SIZE + (CS_TIME * 2), svs.mapname, CS_SIZE ); + com.strncpy( svs.comment + CS_SIZE + (CS_TIME * 2), va( "%i:%i%i", minutes, tens, units ), CS_SIZE ); if( !autosave ) MsgDev( D_INFO, "Saving game..." ); // write lumps @@ -364,7 +377,7 @@ void SV_WriteSaveFile( const char *name, bool autosave, bool bUseLandmark ) WAD_Close( savfile ); // write saveshot for preview, but autosave - if( !autosave ) Cbuf_AddText( va( "saveshot %s\n", name )); + if( !autosave ) Cbuf_AddText( va( "saveshot \"%s\"\n", name )); if( !autosave ) MsgDev( D_INFO, "done.\n" ); } @@ -373,10 +386,13 @@ void SV_ReadComment( wfile_t *l ) SAVERESTOREDATA *pSaveData; game_header_t ghdr; + // init the game to get acess for read funcs + if( !svgame.hInstance ) SV_LoadProgs( "server" ); + // initialize SAVERESTOREDATA Mem_Set( &svgame.SaveData, 0, sizeof( SAVERESTOREDATA )); svgame.SaveData.tokenCount = 0xFFF; // assume a maximum of 4K-1 symbol table entries - svgame.SaveData.pTokens = (char **)Mem_Alloc( svgame.temppool, svgame.SaveData.tokenCount * sizeof( char* )); + svgame.SaveData.pTokens = (char **)Mem_Alloc( host.mempool, svgame.SaveData.tokenCount * sizeof( char* )); pSaveData = svgame.globals->pSaveData = &svgame.SaveData; SV_ReadBuffer( l, LUMP_GLOBALS ); @@ -780,18 +796,18 @@ void SV_ChangeLevel( bool bUseLandmark, const char *mapname, const char *start ) SV_ActivateServer (); } -bool SV_GetComment( char *comment, int savenum ) +bool SV_GetComment( const char *savename, char *comment ) { wfile_t *savfile; int result; if( !comment ) return false; - result = WAD_Check( va( "save/save%i.bin", savenum )); + result = WAD_Check( savename ); switch( result ) { case 0: - com.strncpy( comment, "", MAX_STRING ); + com.strncpy( comment, "", MAX_STRING ); return false; case 1: break; default: @@ -799,7 +815,7 @@ bool SV_GetComment( char *comment, int savenum ) return false; } - savfile = WAD_Open( va( "save/save%i.bin", savenum ), "rb" ); + savfile = WAD_Open( savename, "rb" ); SV_ReadComment( savfile ); Mem_Copy( comment, svs.comment, MAX_STRING ); WAD_Close( savfile ); diff --git a/engine/uimenu/ui_loadgame.c b/engine/uimenu/ui_loadgame.c index 3680d761..4693b58c 100644 --- a/engine/uimenu/ui_loadgame.c +++ b/engine/uimenu/ui_loadgame.c @@ -19,67 +19,52 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common.h" +#include "com_library.h" #include "ui_local.h" #include "client.h" #define ART_BACKGROUND "gfx/shell/splash" -#define ART_BANNER "gfx/shell/banners/loadgame_t" -#define ART_LISTBACK "gfx/shell/segments/files_box" +#define ART_BANNER "gfx/shell/head_load" #define ART_LEVELSHOTBLUR "gfx/shell/segments/sp_mapshot" -#define UI_MAXGAMES 14 - #define ID_BACKGROUND 0 #define ID_BANNER 1 +#define ID_LOAD 2 +#define ID_DELETE 3 +#define ID_CANCEL 4 +#define ID_SAVELIST 5 +#define ID_TABLEHINT 6 +#define ID_LEVELSHOT 7 -#define ID_BACK 2 -#define ID_LOAD 3 +#define LEVELSHOT_X 72 +#define LEVELSHOT_Y 400 +#define LEVELSHOT_W 192 +#define LEVELSHOT_H 160 -#define ID_LISTBACK 4 -#define ID_GAMETITLE 5 -#define ID_LISTGAMES 6 -#define ID_LEVELSHOT 20 - -#define ID_NEWGAME 21 -#define ID_SAVEGAME 22 -#define ID_DELETEGAME 23 +#define TIME_LENGTH 20 +#define NAME_LENGTH 32+TIME_LENGTH +#define GAMETIME_LENGTH 15+NAME_LENGTH typedef struct { - char map[CS_SIZE]; - char time[CS_TIME]; - char date[CS_TIME]; - char name[CS_SIZE]; - bool valid; -} uiLoadGameGame_t; - -static rgba_t uiLoadGameColor = { 0, 76, 127, 255 }; - -typedef struct -{ - uiLoadGameGame_t games[UI_MAXGAMES]; - int currentGame; - - int currentLevelShot; - long fadeTime; + char saveName[UI_MAXGAMES][CS_SIZE]; + char delName[UI_MAXGAMES][CS_SIZE]; + string saveDescription[UI_MAXGAMES]; + char *saveDescriptionPtr[UI_MAXGAMES]; menuFramework_s menu; menuBitmap_s background; menuBitmap_s banner; + menuAction_s load; + menuAction_s delete; + menuAction_s cancel; - menuBitmap_s back; - menuBitmap_s load; + menuScrollList_s savesList; - menuBitmap_s listBack; - menuAction_s gameTitle; - menuAction_s listGames[UI_MAXGAMES]; menuBitmap_s levelShot; - - menuBitmap_s newGame; - menuBitmap_s saveGame; - menuBitmap_s deleteGame; - + menuAction_s hintMessage; + char hintText[MAX_SYSPATH]; } uiLoadGame_t; static uiLoadGame_t uiLoadGame; @@ -92,43 +77,60 @@ UI_LoadGame_GetGameList */ static void UI_LoadGame_GetGameList( void ) { - string name; + string comment; + search_t *t; int i; - for( i = 0; i < UI_MAXGAMES; i++ ) + t = FS_Search( "save/*.bin", true ); + + for( i = 0; t && i < t->numfilenames; i++ ) { - if( !SV_GetComment( name, i )) + if( i >= UI_MAXGAMES ) break; + + if( !SV_GetComment( t->filenames[i], comment )) { - // get name string even if not found - SV_GetComment can be mark saves - // as etc - com.strncpy(uiLoadGame.games[i].name, name, sizeof( uiLoadGame.games[i].name )); - com.strncpy(uiLoadGame.games[i].map, "", sizeof( uiLoadGame.games[i].map )); - com.strncpy(uiLoadGame.games[i].time, "", sizeof( uiLoadGame.games[i].time )); - com.strncpy(uiLoadGame.games[i].date, "", sizeof( uiLoadGame.games[i].date )); - uiLoadGame.games[i].valid = false; + if( com.strlen( comment )) + { + // get name string even if not found - SV_GetComment can be mark saves + // as etc + com.strncat( uiLoadGame.saveDescription[i], uiEmptyString, TIME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], comment, NAME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], uiEmptyString, NAME_LENGTH ); + uiLoadGame.saveDescriptionPtr[i] = uiLoadGame.saveDescription[i]; + FS_FileBase( t->filenames[i], uiLoadGame.delName[i] ); + } + else uiLoadGame.saveDescriptionPtr[i] = NULL; continue; } - com.strncpy( uiLoadGame.games[i].map, name + CS_SIZE + (CS_TIME * 2), CS_SIZE ); - com.strncpy( uiLoadGame.games[i].time, name + CS_SIZE + CS_TIME, CS_TIME ); - com.strncpy( uiLoadGame.games[i].date, name + CS_SIZE, CS_TIME ); - com.strncpy( uiLoadGame.games[i].name, name, CS_SIZE ); - uiLoadGame.games[i].valid = true; + // strip path, leave only filename (empty slots doesn't have savename) + FS_FileBase( t->filenames[i], uiLoadGame.saveName[i] ); + FS_FileBase( t->filenames[i], uiLoadGame.delName[i] ); + + // fill save desc + com.strncat( uiLoadGame.saveDescription[i], comment + CS_SIZE, TIME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], " ", TIME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], comment + CS_SIZE + CS_TIME, TIME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], uiEmptyString, TIME_LENGTH ); // fill remaining entries + com.strncat( uiLoadGame.saveDescription[i], comment, NAME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], uiEmptyString, NAME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], comment + CS_SIZE + (CS_TIME * 2), GAMETIME_LENGTH ); + com.strncat( uiLoadGame.saveDescription[i], uiEmptyString, GAMETIME_LENGTH ); + uiLoadGame.saveDescriptionPtr[i] = uiLoadGame.saveDescription[i]; } - // select first valid slot - for( i = 0; i < UI_MAXGAMES; i++ ) - { - if( uiLoadGame.games[i].valid ) - { - uiLoadGame.listGames[i].generic.color = uiColorOrange; - uiLoadGame.currentGame = i; - break; - } - } + for( ; i < UI_MAXGAMES; i++ ) uiLoadGame.saveDescriptionPtr[i] = NULL; + uiLoadGame.savesList.itemNames = uiLoadGame.saveDescriptionPtr; - // couldn't find a valid slot, so gray load button - if( i == UI_MAXGAMES ) uiLoadGame.load.generic.flags |= QMF_GRAYED; + if( com.strlen( uiLoadGame.saveName[0] ) == 0 ) + uiLoadGame.load.generic.flags |= QMF_GRAYED; + else uiLoadGame.load.generic.flags &= ~QMF_GRAYED; + + if( com.strlen( uiLoadGame.delName[0] ) == 0 ) + uiLoadGame.delete.generic.flags |= QMF_GRAYED; + else uiLoadGame.delete.generic.flags &= ~QMF_GRAYED; + + if( t ) Mem_Free( t ); } /* @@ -140,40 +142,36 @@ static void UI_LoadGame_Callback( void *self, int event ) { menuCommon_s *item = (menuCommon_s *)self; - if( event != QM_ACTIVATED ) - return; - - if( item->type == QMTYPE_ACTION ) + if( event == QM_CHANGED ) { - // reset color, get current game, set color - uiLoadGame.listGames[uiLoadGame.currentGame].generic.color = uiColorOrange; - uiLoadGame.currentGame = item->id - ID_LISTGAMES; - uiLoadGame.listGames[uiLoadGame.currentGame].generic.color = uiColorYellow; + if( com.strlen( uiLoadGame.saveName[uiLoadGame.savesList.curItem] ) == 0 ) + uiLoadGame.load.generic.flags |= QMF_GRAYED; + else uiLoadGame.load.generic.flags &= ~QMF_GRAYED; - // restart levelshot animation - uiLoadGame.currentLevelShot = 0; - uiLoadGame.fadeTime = uiStatic.realTime; + if( com.strlen( uiLoadGame.delName[uiLoadGame.savesList.curItem] ) == 0 ) + uiLoadGame.delete.generic.flags |= QMF_GRAYED; + else uiLoadGame.delete.generic.flags &= ~QMF_GRAYED; return; } + + if( event != QM_ACTIVATED ) + return; switch( item->id ) { - case ID_BACK: + case ID_CANCEL: UI_PopMenu(); break; case ID_LOAD: - if( uiLoadGame.games[uiLoadGame.currentGame].valid ) - Cbuf_ExecuteText( EXEC_APPEND, va( "load save%i\n", uiLoadGame.currentGame )); + if( com.strlen( uiLoadGame.saveName[uiLoadGame.savesList.curItem] )) + Cbuf_ExecuteText( EXEC_APPEND, va( "load \"%s\"\n", uiLoadGame.saveName[uiLoadGame.savesList.curItem] )); break; - case ID_NEWGAME: - UI_NewGame_Menu(); - break; - case ID_SAVEGAME: - UI_SaveGame_Menu(); - break; - case ID_DELETEGAME: - Cbuf_ExecuteText( EXEC_NOW, va( "delete save%i\n", uiLoadGame.currentGame )); - UI_LoadGame_GetGameList(); + case ID_DELETE: + if( com.strlen( uiLoadGame.delName[uiLoadGame.savesList.curItem] )) + { + Cbuf_ExecuteText( EXEC_NOW, va( "delete \"%s\"\n", uiLoadGame.delName[uiLoadGame.savesList.curItem] )); + UI_LoadGame_GetGameList(); + } break; } } @@ -187,106 +185,32 @@ static void UI_LoadGame_Ownerdraw( void *self ) { menuCommon_s *item = (menuCommon_s *)self; - if( item->type == QMTYPE_ACTION ) + if( item->type != QMTYPE_ACTION && item->id == ID_LEVELSHOT ) { - rgba_t color; - char *time, *date, *name; - bool centered; + int x, y, w, h; - if( item->id == ID_GAMETITLE ) - { - time = "Time"; - date = "Date"; - name = "Map Name"; - centered = false; - } - else - { - time = uiLoadGame.games[item->id - ID_LISTGAMES].time; - date = uiLoadGame.games[item->id - ID_LISTGAMES].date; - name = uiLoadGame.games[item->id - ID_LISTGAMES].name; - centered = !uiLoadGame.games[item->id - ID_LISTGAMES].valid; - - if( com.strstr( uiLoadGame.games[item->id - ID_LISTGAMES].name, "" )) - centered = true; - if( com.strstr( uiLoadGame.games[item->id - ID_LISTGAMES].name, "" )) - centered = true; - } - - if( !centered ) - { - UI_DrawString( item->x, item->y, 82*uiStatic.scaleX, item->height, time, item->color, true, item->charWidth, item->charHeight, 1, true ); - UI_DrawString( item->x + 83*uiStatic.scaleX, item->y, 82*uiStatic.scaleX, item->height, date, item->color, true, item->charWidth, item->charHeight, 1, true ); - UI_DrawString( item->x + 83*uiStatic.scaleX + 83*uiStatic.scaleX, item->y, 296*uiStatic.scaleX, item->height, name, item->color, true, item->charWidth, item->charHeight, 1, true ); - } - else UI_DrawString( item->x, item->y, item->width, item->height, name, item->color, true, item->charWidth, item->charHeight, 1, true ); - - if( self != UI_ItemAtCursor( item->parent )) - return; - - *(uint *)color = *(uint *)item->color; - - if( !centered ) - { - UI_DrawString(item->x, item->y, 82*uiStatic.scaleX, item->height, time, color, true, item->charWidth, item->charHeight, 1, true); - UI_DrawString(item->x + 83*uiStatic.scaleX, item->y, 82*uiStatic.scaleX, item->height, date, color, true, item->charWidth, item->charHeight, 1, true); - UI_DrawString(item->x + 83*uiStatic.scaleX + 83*uiStatic.scaleX, item->y, 296*uiStatic.scaleX, item->height, name, color, true, item->charWidth, item->charHeight, 1, true); - } - else UI_DrawString(item->x, item->y, item->width, item->height, name, color, true, item->charWidth, item->charHeight, 1, true); - } - else - { - if( item->id == ID_LEVELSHOT ) - { - int x, y, w, h; - int prev; - rgba_t color = { 255, 255, 255, 255 }; - - // draw the levelshot - x = 570; - y = 210; - w = 410; - h = 202; + // draw the levelshot + x = LEVELSHOT_X; + y = LEVELSHOT_Y; + w = LEVELSHOT_W; + h = LEVELSHOT_H; - UI_ScaleCoords( &x, &y, &w, &h ); + UI_ScaleCoords( &x, &y, &w, &h ); - if( uiLoadGame.games[uiLoadGame.currentGame].map[0] ) - { - string pathJPG; - - if( uiStatic.realTime - uiLoadGame.fadeTime >= 3000 ) - { - uiLoadGame.fadeTime = uiStatic.realTime; - - uiLoadGame.currentLevelShot++; - if( uiLoadGame.currentLevelShot == 3 ) - uiLoadGame.currentLevelShot = 0; - } - - prev = uiLoadGame.currentLevelShot - 1; - if( prev < 0 ) prev = 2; - - color[3] = bound( 0.0f, (float)(uiStatic.realTime - uiLoadGame.fadeTime) * 0.001f, 1.0f ) * 255; - - com.snprintf( pathJPG, sizeof( pathJPG ), "save/save%i.jpg", uiLoadGame.currentGame ); - - if( !FS_FileExists( pathJPG )) - UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); - else UI_DrawPic( x, y, w, h, uiColorWhite, pathJPG ); - } - else UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); - - // draw the blurred frame - UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); - } - else + if( com.strlen( uiLoadGame.saveName[uiLoadGame.savesList.curItem] )) { - if( uiLoadGame.menu.items[uiLoadGame.menu.cursor] == self ) - UI_DrawPic(item->x, item->y, item->width, item->height, uiColorWhite, UI_MOVEBOXFOCUS ); - else UI_DrawPic(item->x, item->y, item->width, item->height, uiColorWhite, UI_MOVEBOX ); + string pathJPG; - UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); + com.snprintf( pathJPG, sizeof( pathJPG ), "save/%s.jpg", uiLoadGame.saveName[uiLoadGame.savesList.curItem] ); + + if( !FS_FileExists( pathJPG )) + UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); + else UI_DrawPic( x, y, w, h, uiColorWhite, pathJPG ); } + else UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); + + // draw the blurred frame + UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); } } @@ -297,11 +221,14 @@ UI_LoadGame_Init */ static void UI_LoadGame_Init( void ) { - int i, y; - Mem_Set( &uiLoadGame, 0, sizeof( uiLoadGame_t )); - uiLoadGame.fadeTime = uiStatic.realTime; + com.strncat( uiLoadGame.hintText, "Time", TIME_LENGTH ); + com.strncat( uiLoadGame.hintText, uiEmptyString, TIME_LENGTH ); + com.strncat( uiLoadGame.hintText, "Game", NAME_LENGTH ); + com.strncat( uiLoadGame.hintText, uiEmptyString, NAME_LENGTH ); + com.strncat( uiLoadGame.hintText, "Elapsed time", GAMETIME_LENGTH ); + com.strncat( uiLoadGame.hintText, uiEmptyString, GAMETIME_LENGTH ); uiLoadGame.background.generic.id = ID_BACKGROUND; uiLoadGame.background.generic.type = QMTYPE_BITMAP; @@ -315,119 +242,76 @@ static void UI_LoadGame_Init( void ) uiLoadGame.banner.generic.id = ID_BANNER; uiLoadGame.banner.generic.type = QMTYPE_BITMAP; uiLoadGame.banner.generic.flags = QMF_INACTIVE; - uiLoadGame.banner.generic.x = 0; - uiLoadGame.banner.generic.y = 66; - uiLoadGame.banner.generic.width = 1024; - uiLoadGame.banner.generic.height = 46; + uiLoadGame.banner.generic.x = 65; + uiLoadGame.banner.generic.y = 92; + uiLoadGame.banner.generic.width = 690; + uiLoadGame.banner.generic.height = 120; uiLoadGame.banner.pic = ART_BANNER; - uiLoadGame.back.generic.id = ID_BACK; - uiLoadGame.back.generic.type = QMTYPE_BITMAP; - uiLoadGame.back.generic.x = 310; - uiLoadGame.back.generic.y = 656; - uiLoadGame.back.generic.width = 198; - uiLoadGame.back.generic.height = 38; - uiLoadGame.back.generic.callback = UI_LoadGame_Callback; - uiLoadGame.back.generic.ownerdraw = UI_LoadGame_Ownerdraw; - uiLoadGame.back.pic = UI_BACKBUTTON; - uiLoadGame.load.generic.id = ID_LOAD; - uiLoadGame.load.generic.type = QMTYPE_BITMAP; - uiLoadGame.load.generic.x = 516; - uiLoadGame.load.generic.y = 656; - uiLoadGame.load.generic.width = 198; - uiLoadGame.load.generic.height = 38; + uiLoadGame.load.generic.type = QMTYPE_ACTION; + uiLoadGame.load.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW; + uiLoadGame.load.generic.x = 72; + uiLoadGame.load.generic.y = 230; + uiLoadGame.load.generic.name = "Load"; + uiLoadGame.load.generic.statusText = "Load saved game"; uiLoadGame.load.generic.callback = UI_LoadGame_Callback; - uiLoadGame.load.generic.ownerdraw = UI_LoadGame_Ownerdraw; - uiLoadGame.load.pic = UI_LOADBUTTON; - uiLoadGame.listBack.generic.id = ID_LISTBACK; - uiLoadGame.listBack.generic.type = QMTYPE_BITMAP; - uiLoadGame.listBack.generic.flags = QMF_INACTIVE; - uiLoadGame.listBack.generic.x = 42; - uiLoadGame.listBack.generic.y = 146; - uiLoadGame.listBack.generic.width = 462; - uiLoadGame.listBack.generic.height = 476; - uiLoadGame.listBack.pic = ART_LISTBACK; + uiLoadGame.delete.generic.id = ID_DELETE; + uiLoadGame.delete.generic.type = QMTYPE_ACTION; + uiLoadGame.delete.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW; + uiLoadGame.delete.generic.x = 72; + uiLoadGame.delete.generic.y = 280; + uiLoadGame.delete.generic.name = "Delete"; + uiLoadGame.delete.generic.statusText = "Delete saved game"; + uiLoadGame.delete.generic.callback = UI_LoadGame_Callback; - uiLoadGame.gameTitle.generic.id = ID_GAMETITLE; - uiLoadGame.gameTitle.generic.type = QMTYPE_ACTION; - uiLoadGame.gameTitle.generic.flags = QMF_INACTIVE|QMF_SMALLFONT; - uiLoadGame.gameTitle.generic.x = 42; - uiLoadGame.gameTitle.generic.y = 146; - uiLoadGame.gameTitle.generic.width = 462; - uiLoadGame.gameTitle.generic.height = 24; - uiLoadGame.gameTitle.generic.ownerdraw = UI_LoadGame_Ownerdraw; + uiLoadGame.cancel.generic.id = ID_CANCEL; + uiLoadGame.cancel.generic.type = QMTYPE_ACTION; + uiLoadGame.cancel.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW; + uiLoadGame.cancel.generic.x = 72; + uiLoadGame.cancel.generic.y = 330; + uiLoadGame.cancel.generic.name = "Cancel"; + uiLoadGame.cancel.generic.statusText = "Return back to main menu"; + uiLoadGame.cancel.generic.callback = UI_LoadGame_Callback; - for( i = 0, y = 182; i < UI_MAXGAMES; i++, y += 32 ) - { - uiLoadGame.listGames[i].generic.id = ID_LISTGAMES+i; - uiLoadGame.listGames[i].generic.type = QMTYPE_ACTION; - uiLoadGame.listGames[i].generic.flags = QMF_SILENT|QMF_SMALLFONT; - uiLoadGame.listGames[i].generic.x = 42; - uiLoadGame.listGames[i].generic.y = y; - uiLoadGame.listGames[i].generic.width = 462; - uiLoadGame.listGames[i].generic.height = 24; - uiLoadGame.listGames[i].generic.callback = UI_LoadGame_Callback; - uiLoadGame.listGames[i].generic.ownerdraw = UI_LoadGame_Ownerdraw; - } + uiLoadGame.hintMessage.generic.id = ID_TABLEHINT; + uiLoadGame.hintMessage.generic.type = QMTYPE_ACTION; + uiLoadGame.hintMessage.generic.flags = QMF_INACTIVE|QMF_SMALLFONT; + uiLoadGame.hintMessage.generic.color = uiColorLtGrey; + uiLoadGame.hintMessage.generic.name = uiLoadGame.hintText; + uiLoadGame.hintMessage.generic.x = 360; + uiLoadGame.hintMessage.generic.y = 225; uiLoadGame.levelShot.generic.id = ID_LEVELSHOT; uiLoadGame.levelShot.generic.type = QMTYPE_BITMAP; uiLoadGame.levelShot.generic.flags = QMF_INACTIVE; - uiLoadGame.levelShot.generic.x = 568; - uiLoadGame.levelShot.generic.y = 208; - uiLoadGame.levelShot.generic.width = 414; - uiLoadGame.levelShot.generic.height = 206; + uiLoadGame.levelShot.generic.x = LEVELSHOT_X; + uiLoadGame.levelShot.generic.y = LEVELSHOT_Y; + uiLoadGame.levelShot.generic.width = LEVELSHOT_W; + uiLoadGame.levelShot.generic.height = LEVELSHOT_H; uiLoadGame.levelShot.generic.ownerdraw = UI_LoadGame_Ownerdraw; uiLoadGame.levelShot.pic = ART_LEVELSHOTBLUR; - uiLoadGame.newGame.generic.id = ID_NEWGAME; - uiLoadGame.newGame.generic.type = QMTYPE_BITMAP; - uiLoadGame.newGame.generic.x = 676; - uiLoadGame.newGame.generic.y = 468; - uiLoadGame.newGame.generic.width = 198; - uiLoadGame.newGame.generic.height = 38; - uiLoadGame.newGame.generic.callback = UI_LoadGame_Callback; - uiLoadGame.newGame.generic.ownerdraw = UI_LoadGame_Ownerdraw; - uiLoadGame.newGame.pic = UI_NEWGAMEBUTTON; - - uiLoadGame.saveGame.generic.id = ID_SAVEGAME; - uiLoadGame.saveGame.generic.type = QMTYPE_BITMAP; - uiLoadGame.saveGame.generic.x = 676; - uiLoadGame.saveGame.generic.y = 516; - uiLoadGame.saveGame.generic.width = 198; - uiLoadGame.saveGame.generic.height = 38; - uiLoadGame.saveGame.generic.callback = UI_LoadGame_Callback; - uiLoadGame.saveGame.generic.ownerdraw = UI_LoadGame_Ownerdraw; - uiLoadGame.saveGame.pic = UI_SAVEBUTTON; - - uiLoadGame.deleteGame.generic.id = ID_DELETEGAME; - uiLoadGame.deleteGame.generic.type = QMTYPE_BITMAP; - uiLoadGame.deleteGame.generic.x = 676; - uiLoadGame.deleteGame.generic.y = 564; - uiLoadGame.deleteGame.generic.width = 198; - uiLoadGame.deleteGame.generic.height = 38; - uiLoadGame.deleteGame.generic.callback = UI_LoadGame_Callback; - uiLoadGame.deleteGame.generic.ownerdraw = UI_LoadGame_Ownerdraw; - uiLoadGame.deleteGame.pic = UI_DELETEBUTTON; + uiLoadGame.savesList.generic.id = ID_SAVELIST; + uiLoadGame.savesList.generic.type = QMTYPE_SCROLLLIST; + uiLoadGame.savesList.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_SMALLFONT; + uiLoadGame.savesList.generic.x = 360; + uiLoadGame.savesList.generic.y = 255; + uiLoadGame.savesList.generic.width = 640; + uiLoadGame.savesList.generic.height = 440; + uiLoadGame.savesList.generic.callback = UI_LoadGame_Callback; UI_LoadGame_GetGameList(); UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.background ); UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.banner ); - UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.back ); UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.load ); - UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.listBack ); - UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.gameTitle ); - - for( i = 0; i < UI_MAXGAMES; i++ ) - UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.listGames[i] ); - + UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.delete ); + UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.cancel ); + UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.hintMessage ); UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.levelShot ); - UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.newGame ); - UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.saveGame ); - UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.deleteGame ); + UI_AddItem( &uiLoadGame.menu, (void *)&uiLoadGame.savesList ); } /* @@ -441,7 +325,6 @@ void UI_LoadGame_Precache( void ) re->RegisterShader( ART_BACKGROUND, SHADER_NOMIP ); re->RegisterShader( ART_BANNER, SHADER_NOMIP ); - re->RegisterShader( ART_LISTBACK, SHADER_NOMIP ); re->RegisterShader( ART_LEVELSHOTBLUR, SHADER_NOMIP ); } @@ -452,6 +335,11 @@ UI_LoadGame_Menu */ void UI_LoadGame_Menu( void ) { + string libpath; + + Com_BuildPath( "server", libpath ); + if( !FS_FileExists( libpath )) return; + UI_LoadGame_Precache(); UI_LoadGame_Init(); diff --git a/engine/uimenu/ui_local.h b/engine/uimenu/ui_local.h index 3c309cee..34c5c26c 100644 --- a/engine/uimenu/ui_local.h +++ b/engine/uimenu/ui_local.h @@ -72,7 +72,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define UI_MAX_FIELD_LINE 256 #define UI_OUTLINE_WIDTH 4 // outline thickness -#define UI_MAXGAMES 14 +#define UI_MAXGAMES 128 #define UI_MAX_SERVERS 10 // Generic types @@ -263,6 +263,7 @@ typedef struct extern uiStatic_t uiStatic; +extern string uiEmptyString; extern const char *uiSoundIn; extern const char *uiSoundMove; extern const char *uiSoundOut; diff --git a/engine/uimenu/ui_main.c b/engine/uimenu/ui_main.c index 053a34fa..7d6f5d81 100644 --- a/engine/uimenu/ui_main.c +++ b/engine/uimenu/ui_main.c @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common.h" +#include "com_library.h" #include "ui_local.h" #include "input.h" #include "client.h" @@ -72,28 +73,6 @@ typedef struct static uiMain_t uiMain; -/* -================= -UI_Main_KeyFunc -================= -*/ -static const char *UI_Main_KeyFunc( int key ) -{ - switch( key ) - { - case K_ESCAPE: - case K_MOUSE2: - if( cls.state == ca_active ) - { - // this shouldn't be happening, but for some reason it is in some mods - UI_CloseMenu(); - return uiSoundNull; - } - return uiSoundNull; - } - return UI_DefaultKey( &uiMain.menu, key ); -} - /* ================= UI_MsgBox_Ownerdraw @@ -129,6 +108,24 @@ static void UI_QuitDialog( void ) } +/* +================= +UI_Main_KeyFunc +================= +*/ +static const char *UI_Main_KeyFunc( int key ) +{ + switch( key ) + { + case K_ESCAPE: + if( cls.state == ca_active ) + UI_CloseMenu(); + else UI_QuitDialog (); + return uiSoundNull; + } + return UI_DefaultKey( &uiMain.menu, key ); +} + /* ================= UI_Main_Callback @@ -192,6 +189,8 @@ UI_Main_Init */ static void UI_Main_Init( void ) { + string libpath; + Mem_Set( &uiMain, 0, sizeof( uiMain_t )); uiMain.menu.keyFunc = UI_Main_KeyFunc; @@ -235,6 +234,11 @@ static void UI_Main_Init( void ) uiMain.saveRestore.generic.type = QMTYPE_ACTION; uiMain.saveRestore.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW; + // server.dll needs for reading savefiles + Com_BuildPath( "server", libpath ); + if( !FS_FileExists( libpath )) + uiMain.saveRestore.generic.flags |= QMF_GRAYED; + if( cls.state == ca_active ) { uiMain.saveRestore.generic.name = "Save\\Load Game"; diff --git a/engine/uimenu/ui_menu.c b/engine/uimenu/ui_menu.c index 61ed2c19..0a0a8ea2 100644 --- a/engine/uimenu/ui_menu.c +++ b/engine/uimenu/ui_menu.c @@ -31,6 +31,7 @@ cvar_t *ui_sensitivity; uiStatic_t uiStatic; +string uiEmptyString; const char *uiSoundIn = "misc/menu1.wav"; const char *uiSoundMove = "misc/menu2.wav"; const char *uiSoundOut = "misc/menu3.wav"; @@ -1037,6 +1038,7 @@ void UI_Init( void ) Cmd_AddCommand( "menu_demos", UI_Demos_Menu, "open the demos viewer menu" ); Cmd_AddCommand( "menu_customgame", UI_CustomGame_Menu, "open the change game menu" ); + Mem_Set( uiEmptyString, ' ', sizeof( uiEmptyString )); uiStatic.scaleX = scr_width->integer / 1024.0f; uiStatic.scaleY = scr_height->integer / 768.0f; diff --git a/engine/uimenu/ui_mods.c b/engine/uimenu/ui_mods.c index 0b9632b1..0db3d334 100644 --- a/engine/uimenu/ui_mods.c +++ b/engine/uimenu/ui_mods.c @@ -33,6 +33,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ID_MODLIST 5 #define ID_TABLEHINT 6 +#define TYPE_LENGTH 10 +#define NAME_LENGTH 32+TYPE_LENGTH +#define VER_LENGTH 10+NAME_LENGTH +#define SIZE_LENGTH 10+VER_LENGTH + typedef struct { string modsDir[MAX_MODS]; @@ -64,23 +69,24 @@ static void UI_CustomGame_GetModList( void ) { int i; + Com_Assert( SIZE_LENGTH >= MAX_STRING ); + for( i = 0; i < SI->numgames; i++ ) { com.strncpy( uiCustomGame.modsDir[i], SI->games[i]->gamefolder, sizeof( uiCustomGame.modsDir[i] )); com.strncpy( uiCustomGame.modsWebSites[i], SI->games[i]->game_url, sizeof( uiCustomGame.modsWebSites[i] )); if( com.strlen( SI->games[i]->type )) - { - com.strncat( uiCustomGame.modsDescription[i], SI->games[i]->type, 10 ); - com.strncat( uiCustomGame.modsDescription[i], " ", 10 ); // ugly method to fill remaining space - } - else com.strncat( uiCustomGame.modsDescription[i], " ", 10 ); - com.strncat( uiCustomGame.modsDescription[i], SI->games[i]->title, 43 ); - com.strncat( uiCustomGame.modsDescription[i], " ", 43 ); - com.strncat( uiCustomGame.modsDescription[i], va( "%g", SI->games[i]->version ), 48 ); - com.strncat( uiCustomGame.modsDescription[i], " ", 48 ); + com.strncat( uiCustomGame.modsDescription[i], SI->games[i]->type, TYPE_LENGTH ); + com.strncat( uiCustomGame.modsDescription[i], uiEmptyString, TYPE_LENGTH ); + com.strncat( uiCustomGame.modsDescription[i], SI->games[i]->title, NAME_LENGTH ); + com.strncat( uiCustomGame.modsDescription[i], uiEmptyString, NAME_LENGTH ); + com.strncat( uiCustomGame.modsDescription[i], va( "%g", SI->games[i]->version ), VER_LENGTH ); + com.strncat( uiCustomGame.modsDescription[i], uiEmptyString, VER_LENGTH ); if( SI->games[i]->size > 0 ) - com.strncat( uiCustomGame.modsDescription[i], memprint( SI->games[i]->size ), MAX_STRING ); + com.strncat( uiCustomGame.modsDescription[i], com.pretifymem( SI->games[i]->size, 0 ), SIZE_LENGTH ); + else com.strncat( uiCustomGame.modsDescription[i], "0.0 Mb", SIZE_LENGTH ); + com.strncat( uiCustomGame.modsDescription[i], uiEmptyString, SIZE_LENGTH ); uiCustomGame.modsDescriptionPtr[i] = uiCustomGame.modsDescription[i]; } for( ; i < MAX_MODS; i++ ) uiCustomGame.modsDescriptionPtr[i] = NULL; @@ -147,7 +153,14 @@ static void UI_CustomGame_Init( void ) { Mem_Set( &uiCustomGame, 0, sizeof( uiCustomGame_t )); - com.strncat( uiCustomGame.hintText, "Type Name Version Size", MAX_SYSPATH ); + com.strncat( uiCustomGame.hintText, "Type", TYPE_LENGTH ); + com.strncat( uiCustomGame.hintText, uiEmptyString, TYPE_LENGTH ); + com.strncat( uiCustomGame.hintText, "Name", NAME_LENGTH ); + com.strncat( uiCustomGame.hintText, uiEmptyString, NAME_LENGTH ); + com.strncat( uiCustomGame.hintText, "Version", VER_LENGTH ); + com.strncat( uiCustomGame.hintText, uiEmptyString, VER_LENGTH ); + com.strncat( uiCustomGame.hintText, "Size", SIZE_LENGTH ); + com.strncat( uiCustomGame.hintText, uiEmptyString, SIZE_LENGTH ); uiCustomGame.background.generic.id = ID_BACKGROUND; uiCustomGame.background.generic.type = QMTYPE_BITMAP; @@ -200,7 +213,7 @@ static void UI_CustomGame_Init( void ) uiCustomGame.hintMessage.generic.color = uiColorLtGrey; uiCustomGame.hintMessage.generic.name = uiCustomGame.hintText; uiCustomGame.hintMessage.generic.x = 360; - uiCustomGame.hintMessage.generic.y = 220; + uiCustomGame.hintMessage.generic.y = 225; uiCustomGame.modList.generic.id = ID_MODLIST; uiCustomGame.modList.generic.type = QMTYPE_SCROLLLIST; diff --git a/engine/uimenu/ui_savegame.c b/engine/uimenu/ui_savegame.c index 2b090e9f..0ce5fb99 100644 --- a/engine/uimenu/ui_savegame.c +++ b/engine/uimenu/ui_savegame.c @@ -19,62 +19,52 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common.h" +#include "com_library.h" #include "ui_local.h" #include "client.h" -#define ART_BACKGROUND "gfx/shell/splash" -#define ART_BANNER "gfx/shell/banners/savegame_t" -#define ART_LISTBACK "gfx/shell/segments/files_box" +#define ART_BACKGROUND "gfx/shell/splash" +#define ART_BANNER "gfx/shell/head_save" #define ART_LEVELSHOTBLUR "gfx/shell/segments/sp_mapshot" #define ID_BACKGROUND 0 #define ID_BANNER 1 +#define ID_SAVE 2 +#define ID_DELETE 3 +#define ID_CANCEL 4 +#define ID_SAVELIST 5 +#define ID_TABLEHINT 6 +#define ID_LEVELSHOT 7 -#define ID_BACK 2 -#define ID_SAVE 3 +#define LEVELSHOT_X 72 +#define LEVELSHOT_Y 400 +#define LEVELSHOT_W 192 +#define LEVELSHOT_H 160 -#define ID_LISTBACK 4 -#define ID_GAMETITLE 5 -#define ID_LISTGAMES 6 -#define ID_LEVELSHOT 20 - -#define ID_NEWGAME 21 -#define ID_LOADGAME 22 -#define ID_DELETEGAME 23 +#define TIME_LENGTH 20 +#define NAME_LENGTH 32+TIME_LENGTH +#define GAMETIME_LENGTH 15+NAME_LENGTH typedef struct { - char map[80]; - char time[16]; - char date[16]; - char name[32]; - bool valid; -} uiSaveGameGame_t; - -static rgba_t uiSaveGameColor = { 0, 76, 127, 255 }; - -typedef struct -{ - uiSaveGameGame_t games[UI_MAXGAMES]; - int currentGame; - - int currentLevelShot; - long fadeTime; + char saveName[UI_MAXGAMES][CS_SIZE]; + char delName[UI_MAXGAMES][CS_SIZE]; + string saveDescription[UI_MAXGAMES]; + char *saveDescriptionPtr[UI_MAXGAMES]; menuFramework_s menu; menuBitmap_s background; menuBitmap_s banner; - menuBitmap_s back; - menuBitmap_s save; - menuBitmap_s listBack; - menuAction_s gameTitle; - menuAction_s listGames[UI_MAXGAMES]; + menuAction_s save; + menuAction_s delete; + menuAction_s cancel; + + menuScrollList_s savesList; menuBitmap_s levelShot; - menuBitmap_s newGame; - menuBitmap_s loadGame; - menuBitmap_s deleteGame; + menuAction_s hintMessage; + char hintText[MAX_SYSPATH]; } uiSaveGame_t; static uiSaveGame_t uiSaveGame; @@ -87,47 +77,74 @@ UI_SaveGame_GetGameList */ static void UI_SaveGame_GetGameList( void ) { - string name; - int i; + string comment; + search_t *t; + int i = 0, j; - for( i = 0; i < UI_MAXGAMES; i++ ) + t = FS_Search( "save/*.bin", true ); + + if( cls.state == ca_active ) { - if( !SV_GetComment( name, i )) + // create new entry for current save game + com.strncpy( uiSaveGame.saveName[i], "new", CS_SIZE ); + com.strncat( uiSaveGame.saveDescription[i], "Current", TIME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, TIME_LENGTH ); // fill remaining entries + com.strncat( uiSaveGame.saveDescription[i], "New Saved Game", NAME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, NAME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], "New", GAMETIME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, GAMETIME_LENGTH ); + uiSaveGame.saveDescriptionPtr[i] = uiSaveGame.saveDescription[i]; + i++; + } + + for( j = 0; t && j < t->numfilenames; i++, j++ ) + { + if( i >= UI_MAXGAMES ) break; + + if( !SV_GetComment( t->filenames[j], comment )) { - // get name string even if not found - SV_GetComment can be mark saves - // as etc - com.strncpy(uiSaveGame.games[i].name, name, sizeof( uiSaveGame.games[i].name )); - com.strncpy(uiSaveGame.games[i].map, "", sizeof( uiSaveGame.games[i].map )); - com.strncpy(uiSaveGame.games[i].time, "", sizeof( uiSaveGame.games[i].time )); - com.strncpy(uiSaveGame.games[i].date, "", sizeof( uiSaveGame.games[i].date )); - uiSaveGame.games[i].valid = false; + if( com.strlen( comment )) + { + // get name string even if not found - SV_GetComment can be mark saves + // as etc + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, TIME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], comment, NAME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, NAME_LENGTH ); + uiSaveGame.saveDescriptionPtr[i] = uiSaveGame.saveDescription[i]; + FS_FileBase( t->filenames[j], uiSaveGame.delName[i] ); + } + else uiSaveGame.saveDescriptionPtr[i] = NULL; continue; } - com.strncpy( uiSaveGame.games[i].map, name + CS_SIZE + (CS_TIME * 2), CS_SIZE ); - com.strncpy( uiSaveGame.games[i].time, name + CS_SIZE + CS_TIME, CS_TIME ); - com.strncpy( uiSaveGame.games[i].date, name + CS_SIZE, CS_TIME ); - com.strncpy( uiSaveGame.games[i].name, name, CS_SIZE ); - uiSaveGame.games[i].valid = true; + // strip path, leave only filename (empty slots doesn't have savename) + FS_FileBase( t->filenames[j], uiSaveGame.saveName[i] ); + FS_FileBase( t->filenames[j], uiSaveGame.delName[i] ); + + // fill save desc + com.strncat( uiSaveGame.saveDescription[i], comment + CS_SIZE, TIME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], " ", TIME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], comment + CS_SIZE + CS_TIME, TIME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, TIME_LENGTH ); // fill remaining entries + com.strncat( uiSaveGame.saveDescription[i], comment, NAME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, NAME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], comment + CS_SIZE + (CS_TIME * 2), GAMETIME_LENGTH ); + com.strncat( uiSaveGame.saveDescription[i], uiEmptyString, GAMETIME_LENGTH ); + uiSaveGame.saveDescriptionPtr[i] = uiSaveGame.saveDescription[i]; } - // select first empty slot - for( i = 0; i < UI_MAXGAMES; i++ ) - { - if( !uiSaveGame.games[i].valid ) - { - uiSaveGame.listGames[i].generic.color = uiSaveGameColor; - uiSaveGame.currentGame = i; - break; - } - } + for( ; i < UI_MAXGAMES; i++ ) uiSaveGame.saveDescriptionPtr[i] = NULL; + uiSaveGame.savesList.itemNames = uiSaveGame.saveDescriptionPtr; - // couldn't find an empty slot, so select first - if( i == UI_MAXGAMES ) - { - uiSaveGame.listGames[i].generic.color = uiSaveGameColor; - uiSaveGame.currentGame = 0; - } + if( com.strlen( uiSaveGame.saveName[0] ) == 0 || cls.state != ca_active ) + uiSaveGame.save.generic.flags |= QMF_GRAYED; + else uiSaveGame.save.generic.flags &= ~QMF_GRAYED; + + if( com.strlen( uiSaveGame.delName[0] ) == 0 || cls.state != ca_active ) + uiSaveGame.delete.generic.flags |= QMF_GRAYED; + else uiSaveGame.delete.generic.flags &= ~QMF_GRAYED; + + if( t ) Mem_Free( t ); } /* @@ -139,43 +156,44 @@ static void UI_SaveGame_Callback( void *self, int event ) { menuCommon_s *item = (menuCommon_s *)self; - if( event != QM_ACTIVATED ) - return; - - if( item->type == QMTYPE_ACTION ) + if( event == QM_CHANGED ) { - // reset color, get current game, set color - uiSaveGame.listGames[uiSaveGame.currentGame].generic.color = uiColorWhite; - uiSaveGame.currentGame = item->id - ID_LISTGAMES; - uiSaveGame.listGames[uiSaveGame.currentGame].generic.color = uiSaveGameColor; + // never overwrite existing saves, because their names was never get collision + if( com.stricmp( uiSaveGame.saveName[uiSaveGame.savesList.curItem], "new" )) + uiSaveGame.save.generic.flags |= QMF_GRAYED; + else uiSaveGame.save.generic.flags &= ~QMF_GRAYED; - // restart levelshot animation - uiSaveGame.currentLevelShot = 0; - uiSaveGame.fadeTime = uiStatic.realTime; + if( com.strlen( uiSaveGame.delName[uiSaveGame.savesList.curItem] ) == 0 ) + uiSaveGame.delete.generic.flags |= QMF_GRAYED; + else uiSaveGame.delete.generic.flags &= ~QMF_GRAYED; return; } + + if( event != QM_ACTIVATED ) + return; switch( item->id ) { - case ID_BACK: + case ID_CANCEL: UI_PopMenu(); break; case ID_SAVE: - if( Host_ServerState()) + if( com.strlen( uiSaveGame.saveName[uiSaveGame.savesList.curItem] )) { - Cbuf_ExecuteText( EXEC_APPEND, va( "save save%i\n", uiSaveGame.currentGame )); + Cbuf_ExecuteText( EXEC_APPEND, va( "save \"%s\"\n", uiSaveGame.saveName[uiSaveGame.savesList.curItem] )); UI_CloseMenu(); } break; - case ID_NEWGAME: - UI_NewGame_Menu(); - break; - case ID_LOADGAME: - UI_LoadGame_Menu(); - break; - case ID_DELETEGAME: - Cbuf_ExecuteText( EXEC_NOW, va( "delete save%i\n", uiSaveGame.currentGame )); - UI_SaveGame_GetGameList(); + case ID_DELETE: + if( com.strlen( uiSaveGame.delName[uiSaveGame.savesList.curItem] )) + { + string pathJPG; + + com.snprintf( pathJPG, sizeof( pathJPG ), "save/%s.jpg", uiSaveGame.saveName[uiSaveGame.savesList.curItem] ); + Cbuf_ExecuteText( EXEC_NOW, va( "delete \"%s\"\n", uiSaveGame.delName[uiSaveGame.savesList.curItem] )); + if( re ) re->FreeShader( pathJPG ); + UI_SaveGame_GetGameList(); + } break; } } @@ -189,107 +207,32 @@ static void UI_SaveGame_Ownerdraw( void *self ) { menuCommon_s *item = (menuCommon_s *)self; - if( item->type == QMTYPE_ACTION ) + if( item->type != QMTYPE_ACTION && item->id == ID_LEVELSHOT ) { - rgba_t color; - char *time, *date, *name; - bool centered; + int x, y, w, h; - if( item->id == ID_GAMETITLE ) - { - time = "Time"; - date = "Date"; - name = "Map Name"; - centered = false; - } - else - { - time = uiSaveGame.games[item->id - ID_LISTGAMES].time; - date = uiSaveGame.games[item->id - ID_LISTGAMES].date; - name = uiSaveGame.games[item->id - ID_LISTGAMES].name; - centered = !uiSaveGame.games[item->id - ID_LISTGAMES].valid; - - if( com.strstr( uiSaveGame.games[item->id - ID_LISTGAMES].name, "" )) - centered = true; - if( com.strstr( uiSaveGame.games[item->id - ID_LISTGAMES].name, "" )) - centered = true; - } - - if( !centered ) - { - UI_DrawString( item->x, item->y, 82*uiStatic.scaleX, item->height, time, item->color, true, item->charWidth, item->charHeight, 1, true ); - UI_DrawString( item->x + 83*uiStatic.scaleX, item->y, 82*uiStatic.scaleX, item->height, date, item->color, true, item->charWidth, item->charHeight, 1, true ); - UI_DrawString( item->x + 83*uiStatic.scaleX + 83*uiStatic.scaleX, item->y, 296*uiStatic.scaleX, item->height, name, item->color, true, item->charWidth, item->charHeight, 1, true ); - } - else UI_DrawString( item->x, item->y, item->width, item->height, name, item->color, true, item->charWidth, item->charHeight, 1, true ); - - if( self != UI_ItemAtCursor( item->parent )) - return; - - *(uint *)color = *(uint *)item->color; - color[3] = 255 * (0.5 + 0.5 * com.sin( uiStatic.realTime / UI_PULSE_DIVISOR )); - - if( !centered ) - { - UI_DrawString( item->x, item->y, 82*uiStatic.scaleX, item->height, time, color, true, item->charWidth, item->charHeight, 1, true ); - UI_DrawString( item->x + 83*uiStatic.scaleX, item->y, 82*uiStatic.scaleX, item->height, date, color, true, item->charWidth, item->charHeight, 1, true ); - UI_DrawString( item->x + 83*uiStatic.scaleX + 83*uiStatic.scaleX, item->y, 296*uiStatic.scaleX, item->height, name, color, true, item->charWidth, item->charHeight, 1, true ); - } - else UI_DrawString( item->x, item->y, item->width, item->height, name, color, true, item->charWidth, item->charHeight, 1, true ); - } - else - { - if( item->id == ID_LEVELSHOT ) - { - int x, y, w, h; - int prev; - rgba_t color = { 255, 255, 255, 255 }; - - // draw the levelshot - x = 570; - y = 210; - w = 410; - h = 202; + // draw the levelshot + x = LEVELSHOT_X; + y = LEVELSHOT_Y; + w = LEVELSHOT_W; + h = LEVELSHOT_H; - UI_ScaleCoords( &x, &y, &w, &h ); + UI_ScaleCoords( &x, &y, &w, &h ); - if( uiSaveGame.games[uiSaveGame.currentGame].map[0] ) - { - string pathJPG; - - if( uiStatic.realTime - uiSaveGame.fadeTime >= 3000 ) - { - uiSaveGame.fadeTime = uiStatic.realTime; - - uiSaveGame.currentLevelShot++; - if( uiSaveGame.currentLevelShot == 3 ) - uiSaveGame.currentLevelShot = 0; - } - - prev = uiSaveGame.currentLevelShot - 1; - if( prev < 0 ) prev = 2; - - color[3] = bound( 0.0f, (float)(uiStatic.realTime - uiSaveGame.fadeTime) * 0.001f, 1.0f ) * 255; - - com.snprintf( pathJPG, sizeof( pathJPG ), "save/save%i.jpg", uiSaveGame.currentGame ); - - if( !FS_FileExists( pathJPG )) - UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); - else UI_DrawPic( x, y, w, h, uiColorWhite, pathJPG ); - } - else UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); - - // draw the blurred frame - UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); - } - else + if( com.strlen( uiSaveGame.saveName[uiSaveGame.savesList.curItem] )) { - if( uiSaveGame.menu.items[uiSaveGame.menu.cursor] == self ) - UI_DrawPic(item->x, item->y, item->width, item->height, uiColorWhite, UI_MOVEBOXFOCUS ); - else UI_DrawPic(item->x, item->y, item->width, item->height, uiColorWhite, UI_MOVEBOX ); + string pathJPG; - UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); + com.snprintf( pathJPG, sizeof( pathJPG ), "save/%s.jpg", uiSaveGame.saveName[uiSaveGame.savesList.curItem] ); + + if( !FS_FileExists( pathJPG )) + UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); + else UI_DrawPic( x, y, w, h, uiColorWhite, pathJPG ); } + else UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); + + // draw the blurred frame + UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); } } @@ -300,11 +243,14 @@ UI_SaveGame_Init */ static void UI_SaveGame_Init( void ) { - int i, y; - Mem_Set( &uiSaveGame, 0, sizeof( uiSaveGame_t )); - uiSaveGame.fadeTime = uiStatic.realTime; + com.strncat( uiSaveGame.hintText, "Time", TIME_LENGTH ); + com.strncat( uiSaveGame.hintText, uiEmptyString, TIME_LENGTH ); + com.strncat( uiSaveGame.hintText, "Game", NAME_LENGTH ); + com.strncat( uiSaveGame.hintText, uiEmptyString, NAME_LENGTH ); + com.strncat( uiSaveGame.hintText, "Elapsed time", GAMETIME_LENGTH ); + com.strncat( uiSaveGame.hintText, uiEmptyString, GAMETIME_LENGTH ); uiSaveGame.background.generic.id = ID_BACKGROUND; uiSaveGame.background.generic.type = QMTYPE_BITMAP; @@ -318,119 +264,76 @@ static void UI_SaveGame_Init( void ) uiSaveGame.banner.generic.id = ID_BANNER; uiSaveGame.banner.generic.type = QMTYPE_BITMAP; uiSaveGame.banner.generic.flags = QMF_INACTIVE; - uiSaveGame.banner.generic.x = 0; - uiSaveGame.banner.generic.y = 66; - uiSaveGame.banner.generic.width = 1024; - uiSaveGame.banner.generic.height = 46; + uiSaveGame.banner.generic.x = 65; + uiSaveGame.banner.generic.y = 92; + uiSaveGame.banner.generic.width = 690; + uiSaveGame.banner.generic.height = 120; uiSaveGame.banner.pic = ART_BANNER; - uiSaveGame.back.generic.id = ID_BACK; - uiSaveGame.back.generic.type = QMTYPE_BITMAP; - uiSaveGame.back.generic.x = 310; - uiSaveGame.back.generic.y = 656; - uiSaveGame.back.generic.width = 198; - uiSaveGame.back.generic.height = 38; - uiSaveGame.back.generic.callback = UI_SaveGame_Callback; - uiSaveGame.back.generic.ownerdraw = UI_SaveGame_Ownerdraw; - uiSaveGame.back.pic = UI_BACKBUTTON; - uiSaveGame.save.generic.id = ID_SAVE; - uiSaveGame.save.generic.type = QMTYPE_BITMAP; - uiSaveGame.save.generic.x = 516; - uiSaveGame.save.generic.y = 656; - uiSaveGame.save.generic.width = 198; - uiSaveGame.save.generic.height = 38; + uiSaveGame.save.generic.type = QMTYPE_ACTION; + uiSaveGame.save.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW; + uiSaveGame.save.generic.x = 72; + uiSaveGame.save.generic.y = 230; + uiSaveGame.save.generic.name = "Save"; + uiSaveGame.save.generic.statusText = "Save saved game"; uiSaveGame.save.generic.callback = UI_SaveGame_Callback; - uiSaveGame.save.generic.ownerdraw = UI_SaveGame_Ownerdraw; - uiSaveGame.save.pic = UI_SAVEBUTTON; - uiSaveGame.listBack.generic.id = ID_LISTBACK; - uiSaveGame.listBack.generic.type = QMTYPE_BITMAP; - uiSaveGame.listBack.generic.flags = QMF_INACTIVE; - uiSaveGame.listBack.generic.x = 42; - uiSaveGame.listBack.generic.y = 146; - uiSaveGame.listBack.generic.width = 462; - uiSaveGame.listBack.generic.height = 476; - uiSaveGame.listBack.pic = ART_LISTBACK; + uiSaveGame.delete.generic.id = ID_DELETE; + uiSaveGame.delete.generic.type = QMTYPE_ACTION; + uiSaveGame.delete.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW; + uiSaveGame.delete.generic.x = 72; + uiSaveGame.delete.generic.y = 280; + uiSaveGame.delete.generic.name = "Delete"; + uiSaveGame.delete.generic.statusText = "Delete saved game"; + uiSaveGame.delete.generic.callback = UI_SaveGame_Callback; - uiSaveGame.gameTitle.generic.id = ID_GAMETITLE; - uiSaveGame.gameTitle.generic.type = QMTYPE_ACTION; - uiSaveGame.gameTitle.generic.flags = QMF_INACTIVE; - uiSaveGame.gameTitle.generic.x = 42; - uiSaveGame.gameTitle.generic.y = 146; - uiSaveGame.gameTitle.generic.width = 462; - uiSaveGame.gameTitle.generic.height = 24; - uiSaveGame.gameTitle.generic.ownerdraw = UI_SaveGame_Ownerdraw; + uiSaveGame.cancel.generic.id = ID_CANCEL; + uiSaveGame.cancel.generic.type = QMTYPE_ACTION; + uiSaveGame.cancel.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW; + uiSaveGame.cancel.generic.x = 72; + uiSaveGame.cancel.generic.y = 330; + uiSaveGame.cancel.generic.name = "Cancel"; + uiSaveGame.cancel.generic.statusText = "Return back to main menu"; + uiSaveGame.cancel.generic.callback = UI_SaveGame_Callback; - for( i = 0, y = 182; i < UI_MAXGAMES; i++, y += 32 ) - { - uiSaveGame.listGames[i].generic.id = ID_LISTGAMES+i; - uiSaveGame.listGames[i].generic.type = QMTYPE_ACTION; - uiSaveGame.listGames[i].generic.flags = QMF_SILENT; - uiSaveGame.listGames[i].generic.x = 42; - uiSaveGame.listGames[i].generic.y = y; - uiSaveGame.listGames[i].generic.width = 462; - uiSaveGame.listGames[i].generic.height = 24; - uiSaveGame.listGames[i].generic.callback = UI_SaveGame_Callback; - uiSaveGame.listGames[i].generic.ownerdraw = UI_SaveGame_Ownerdraw; - } + uiSaveGame.hintMessage.generic.id = ID_TABLEHINT; + uiSaveGame.hintMessage.generic.type = QMTYPE_ACTION; + uiSaveGame.hintMessage.generic.flags = QMF_INACTIVE|QMF_SMALLFONT; + uiSaveGame.hintMessage.generic.color = uiColorLtGrey; + uiSaveGame.hintMessage.generic.name = uiSaveGame.hintText; + uiSaveGame.hintMessage.generic.x = 360; + uiSaveGame.hintMessage.generic.y = 225; uiSaveGame.levelShot.generic.id = ID_LEVELSHOT; uiSaveGame.levelShot.generic.type = QMTYPE_BITMAP; uiSaveGame.levelShot.generic.flags = QMF_INACTIVE; - uiSaveGame.levelShot.generic.x = 568; - uiSaveGame.levelShot.generic.y = 208; - uiSaveGame.levelShot.generic.width = 414; - uiSaveGame.levelShot.generic.height = 206; + uiSaveGame.levelShot.generic.x = LEVELSHOT_X; + uiSaveGame.levelShot.generic.y = LEVELSHOT_Y; + uiSaveGame.levelShot.generic.width = LEVELSHOT_W; + uiSaveGame.levelShot.generic.height = LEVELSHOT_H; uiSaveGame.levelShot.generic.ownerdraw = UI_SaveGame_Ownerdraw; uiSaveGame.levelShot.pic = ART_LEVELSHOTBLUR; - uiSaveGame.newGame.generic.id = ID_NEWGAME; - uiSaveGame.newGame.generic.type = QMTYPE_BITMAP; - uiSaveGame.newGame.generic.x = 676; - uiSaveGame.newGame.generic.y = 468; - uiSaveGame.newGame.generic.width = 198; - uiSaveGame.newGame.generic.height = 38; - uiSaveGame.newGame.generic.callback = UI_SaveGame_Callback; - uiSaveGame.newGame.generic.ownerdraw = UI_SaveGame_Ownerdraw; - uiSaveGame.newGame.pic = UI_NEWGAMEBUTTON; - - uiSaveGame.loadGame.generic.id = ID_LOADGAME; - uiSaveGame.loadGame.generic.type = QMTYPE_BITMAP; - uiSaveGame.loadGame.generic.x = 676; - uiSaveGame.loadGame.generic.y = 516; - uiSaveGame.loadGame.generic.width = 198; - uiSaveGame.loadGame.generic.height = 38; - uiSaveGame.loadGame.generic.callback = UI_SaveGame_Callback; - uiSaveGame.loadGame.generic.ownerdraw = UI_SaveGame_Ownerdraw; - uiSaveGame.loadGame.pic = UI_LOADBUTTON; - - uiSaveGame.deleteGame.generic.id = ID_DELETEGAME; - uiSaveGame.deleteGame.generic.type = QMTYPE_BITMAP; - uiSaveGame.deleteGame.generic.x = 676; - uiSaveGame.deleteGame.generic.y = 564; - uiSaveGame.deleteGame.generic.width = 198; - uiSaveGame.deleteGame.generic.height = 38; - uiSaveGame.deleteGame.generic.callback = UI_SaveGame_Callback; - uiSaveGame.deleteGame.generic.ownerdraw = UI_SaveGame_Ownerdraw; - uiSaveGame.deleteGame.pic = UI_DELETEBUTTON; + uiSaveGame.savesList.generic.id = ID_SAVELIST; + uiSaveGame.savesList.generic.type = QMTYPE_SCROLLLIST; + uiSaveGame.savesList.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_SMALLFONT; + uiSaveGame.savesList.generic.x = 360; + uiSaveGame.savesList.generic.y = 255; + uiSaveGame.savesList.generic.width = 640; + uiSaveGame.savesList.generic.height = 440; + uiSaveGame.savesList.generic.callback = UI_SaveGame_Callback; UI_SaveGame_GetGameList(); UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.background ); UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.banner ); - UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.back ); UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.save ); - UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.listBack ); - UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.gameTitle ); - - for( i = 0; i < UI_MAXGAMES; i++ ) - UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.listGames[i] ); - + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.delete ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.cancel ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.hintMessage ); UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.levelShot ); - UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.newGame ); - UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.loadGame ); - UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.deleteGame ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.savesList ); } /* @@ -444,7 +347,6 @@ void UI_SaveGame_Precache( void ) re->RegisterShader( ART_BACKGROUND, SHADER_NOMIP ); re->RegisterShader( ART_BANNER, SHADER_NOMIP ); - re->RegisterShader( ART_LISTBACK, SHADER_NOMIP ); re->RegisterShader( ART_LEVELSHOTBLUR, SHADER_NOMIP ); } @@ -455,6 +357,11 @@ UI_SaveGame_Menu */ void UI_SaveGame_Menu( void ) { + string libpath; + + Com_BuildPath( "server", libpath ); + if( !FS_FileExists( libpath )) return; + UI_SaveGame_Precache(); UI_SaveGame_Init(); diff --git a/engine/uimenu/ui_savegame.old b/engine/uimenu/ui_savegame.old new file mode 100644 index 00000000..2b090e9f --- /dev/null +++ b/engine/uimenu/ui_savegame.old @@ -0,0 +1,462 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "common.h" +#include "ui_local.h" +#include "client.h" + +#define ART_BACKGROUND "gfx/shell/splash" +#define ART_BANNER "gfx/shell/banners/savegame_t" +#define ART_LISTBACK "gfx/shell/segments/files_box" +#define ART_LEVELSHOTBLUR "gfx/shell/segments/sp_mapshot" + +#define ID_BACKGROUND 0 +#define ID_BANNER 1 + +#define ID_BACK 2 +#define ID_SAVE 3 + +#define ID_LISTBACK 4 +#define ID_GAMETITLE 5 +#define ID_LISTGAMES 6 +#define ID_LEVELSHOT 20 + +#define ID_NEWGAME 21 +#define ID_LOADGAME 22 +#define ID_DELETEGAME 23 + +typedef struct +{ + char map[80]; + char time[16]; + char date[16]; + char name[32]; + bool valid; +} uiSaveGameGame_t; + +static rgba_t uiSaveGameColor = { 0, 76, 127, 255 }; + +typedef struct +{ + uiSaveGameGame_t games[UI_MAXGAMES]; + int currentGame; + + int currentLevelShot; + long fadeTime; + + menuFramework_s menu; + + menuBitmap_s background; + menuBitmap_s banner; + menuBitmap_s back; + menuBitmap_s save; + menuBitmap_s listBack; + menuAction_s gameTitle; + menuAction_s listGames[UI_MAXGAMES]; + + menuBitmap_s levelShot; + menuBitmap_s newGame; + menuBitmap_s loadGame; + menuBitmap_s deleteGame; +} uiSaveGame_t; + +static uiSaveGame_t uiSaveGame; + + +/* +================= +UI_SaveGame_GetGameList +================= +*/ +static void UI_SaveGame_GetGameList( void ) +{ + string name; + int i; + + for( i = 0; i < UI_MAXGAMES; i++ ) + { + if( !SV_GetComment( name, i )) + { + // get name string even if not found - SV_GetComment can be mark saves + // as etc + com.strncpy(uiSaveGame.games[i].name, name, sizeof( uiSaveGame.games[i].name )); + com.strncpy(uiSaveGame.games[i].map, "", sizeof( uiSaveGame.games[i].map )); + com.strncpy(uiSaveGame.games[i].time, "", sizeof( uiSaveGame.games[i].time )); + com.strncpy(uiSaveGame.games[i].date, "", sizeof( uiSaveGame.games[i].date )); + uiSaveGame.games[i].valid = false; + continue; + } + + com.strncpy( uiSaveGame.games[i].map, name + CS_SIZE + (CS_TIME * 2), CS_SIZE ); + com.strncpy( uiSaveGame.games[i].time, name + CS_SIZE + CS_TIME, CS_TIME ); + com.strncpy( uiSaveGame.games[i].date, name + CS_SIZE, CS_TIME ); + com.strncpy( uiSaveGame.games[i].name, name, CS_SIZE ); + uiSaveGame.games[i].valid = true; + } + + // select first empty slot + for( i = 0; i < UI_MAXGAMES; i++ ) + { + if( !uiSaveGame.games[i].valid ) + { + uiSaveGame.listGames[i].generic.color = uiSaveGameColor; + uiSaveGame.currentGame = i; + break; + } + } + + // couldn't find an empty slot, so select first + if( i == UI_MAXGAMES ) + { + uiSaveGame.listGames[i].generic.color = uiSaveGameColor; + uiSaveGame.currentGame = 0; + } +} + +/* +================= +UI_SaveGame_Callback +================= +*/ +static void UI_SaveGame_Callback( void *self, int event ) +{ + menuCommon_s *item = (menuCommon_s *)self; + + if( event != QM_ACTIVATED ) + return; + + if( item->type == QMTYPE_ACTION ) + { + // reset color, get current game, set color + uiSaveGame.listGames[uiSaveGame.currentGame].generic.color = uiColorWhite; + uiSaveGame.currentGame = item->id - ID_LISTGAMES; + uiSaveGame.listGames[uiSaveGame.currentGame].generic.color = uiSaveGameColor; + + // restart levelshot animation + uiSaveGame.currentLevelShot = 0; + uiSaveGame.fadeTime = uiStatic.realTime; + return; + } + + switch( item->id ) + { + case ID_BACK: + UI_PopMenu(); + break; + case ID_SAVE: + if( Host_ServerState()) + { + Cbuf_ExecuteText( EXEC_APPEND, va( "save save%i\n", uiSaveGame.currentGame )); + UI_CloseMenu(); + } + break; + case ID_NEWGAME: + UI_NewGame_Menu(); + break; + case ID_LOADGAME: + UI_LoadGame_Menu(); + break; + case ID_DELETEGAME: + Cbuf_ExecuteText( EXEC_NOW, va( "delete save%i\n", uiSaveGame.currentGame )); + UI_SaveGame_GetGameList(); + break; + } +} + +/* +================= +UI_SaveGame_Ownerdraw +================= +*/ +static void UI_SaveGame_Ownerdraw( void *self ) +{ + menuCommon_s *item = (menuCommon_s *)self; + + if( item->type == QMTYPE_ACTION ) + { + rgba_t color; + char *time, *date, *name; + bool centered; + + if( item->id == ID_GAMETITLE ) + { + time = "Time"; + date = "Date"; + name = "Map Name"; + centered = false; + } + else + { + time = uiSaveGame.games[item->id - ID_LISTGAMES].time; + date = uiSaveGame.games[item->id - ID_LISTGAMES].date; + name = uiSaveGame.games[item->id - ID_LISTGAMES].name; + centered = !uiSaveGame.games[item->id - ID_LISTGAMES].valid; + + if( com.strstr( uiSaveGame.games[item->id - ID_LISTGAMES].name, "" )) + centered = true; + if( com.strstr( uiSaveGame.games[item->id - ID_LISTGAMES].name, "" )) + centered = true; + } + + if( !centered ) + { + UI_DrawString( item->x, item->y, 82*uiStatic.scaleX, item->height, time, item->color, true, item->charWidth, item->charHeight, 1, true ); + UI_DrawString( item->x + 83*uiStatic.scaleX, item->y, 82*uiStatic.scaleX, item->height, date, item->color, true, item->charWidth, item->charHeight, 1, true ); + UI_DrawString( item->x + 83*uiStatic.scaleX + 83*uiStatic.scaleX, item->y, 296*uiStatic.scaleX, item->height, name, item->color, true, item->charWidth, item->charHeight, 1, true ); + } + else UI_DrawString( item->x, item->y, item->width, item->height, name, item->color, true, item->charWidth, item->charHeight, 1, true ); + + if( self != UI_ItemAtCursor( item->parent )) + return; + + *(uint *)color = *(uint *)item->color; + color[3] = 255 * (0.5 + 0.5 * com.sin( uiStatic.realTime / UI_PULSE_DIVISOR )); + + if( !centered ) + { + UI_DrawString( item->x, item->y, 82*uiStatic.scaleX, item->height, time, color, true, item->charWidth, item->charHeight, 1, true ); + UI_DrawString( item->x + 83*uiStatic.scaleX, item->y, 82*uiStatic.scaleX, item->height, date, color, true, item->charWidth, item->charHeight, 1, true ); + UI_DrawString( item->x + 83*uiStatic.scaleX + 83*uiStatic.scaleX, item->y, 296*uiStatic.scaleX, item->height, name, color, true, item->charWidth, item->charHeight, 1, true ); + } + else UI_DrawString( item->x, item->y, item->width, item->height, name, color, true, item->charWidth, item->charHeight, 1, true ); + } + else + { + if( item->id == ID_LEVELSHOT ) + { + int x, y, w, h; + int prev; + rgba_t color = { 255, 255, 255, 255 }; + + // draw the levelshot + x = 570; + y = 210; + w = 410; + h = 202; + + UI_ScaleCoords( &x, &y, &w, &h ); + + if( uiSaveGame.games[uiSaveGame.currentGame].map[0] ) + { + string pathJPG; + + if( uiStatic.realTime - uiSaveGame.fadeTime >= 3000 ) + { + uiSaveGame.fadeTime = uiStatic.realTime; + + uiSaveGame.currentLevelShot++; + if( uiSaveGame.currentLevelShot == 3 ) + uiSaveGame.currentLevelShot = 0; + } + + prev = uiSaveGame.currentLevelShot - 1; + if( prev < 0 ) prev = 2; + + color[3] = bound( 0.0f, (float)(uiStatic.realTime - uiSaveGame.fadeTime) * 0.001f, 1.0f ) * 255; + + com.snprintf( pathJPG, sizeof( pathJPG ), "save/save%i.jpg", uiSaveGame.currentGame ); + + if( !FS_FileExists( pathJPG )) + UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); + else UI_DrawPic( x, y, w, h, uiColorWhite, pathJPG ); + } + else UI_DrawPic( x, y, w, h, uiColorWhite, "gfx/hud/static" ); + + // draw the blurred frame + UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); + } + else + { + if( uiSaveGame.menu.items[uiSaveGame.menu.cursor] == self ) + UI_DrawPic(item->x, item->y, item->width, item->height, uiColorWhite, UI_MOVEBOXFOCUS ); + else UI_DrawPic(item->x, item->y, item->width, item->height, uiColorWhite, UI_MOVEBOX ); + + UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic ); + } + } +} + +/* +================= +UI_SaveGame_Init +================= +*/ +static void UI_SaveGame_Init( void ) +{ + int i, y; + + Mem_Set( &uiSaveGame, 0, sizeof( uiSaveGame_t )); + + uiSaveGame.fadeTime = uiStatic.realTime; + + uiSaveGame.background.generic.id = ID_BACKGROUND; + uiSaveGame.background.generic.type = QMTYPE_BITMAP; + uiSaveGame.background.generic.flags = QMF_INACTIVE; + uiSaveGame.background.generic.x = 0; + uiSaveGame.background.generic.y = 0; + uiSaveGame.background.generic.width = 1024; + uiSaveGame.background.generic.height = 768; + uiSaveGame.background.pic = ART_BACKGROUND; + + uiSaveGame.banner.generic.id = ID_BANNER; + uiSaveGame.banner.generic.type = QMTYPE_BITMAP; + uiSaveGame.banner.generic.flags = QMF_INACTIVE; + uiSaveGame.banner.generic.x = 0; + uiSaveGame.banner.generic.y = 66; + uiSaveGame.banner.generic.width = 1024; + uiSaveGame.banner.generic.height = 46; + uiSaveGame.banner.pic = ART_BANNER; + + uiSaveGame.back.generic.id = ID_BACK; + uiSaveGame.back.generic.type = QMTYPE_BITMAP; + uiSaveGame.back.generic.x = 310; + uiSaveGame.back.generic.y = 656; + uiSaveGame.back.generic.width = 198; + uiSaveGame.back.generic.height = 38; + uiSaveGame.back.generic.callback = UI_SaveGame_Callback; + uiSaveGame.back.generic.ownerdraw = UI_SaveGame_Ownerdraw; + uiSaveGame.back.pic = UI_BACKBUTTON; + + uiSaveGame.save.generic.id = ID_SAVE; + uiSaveGame.save.generic.type = QMTYPE_BITMAP; + uiSaveGame.save.generic.x = 516; + uiSaveGame.save.generic.y = 656; + uiSaveGame.save.generic.width = 198; + uiSaveGame.save.generic.height = 38; + uiSaveGame.save.generic.callback = UI_SaveGame_Callback; + uiSaveGame.save.generic.ownerdraw = UI_SaveGame_Ownerdraw; + uiSaveGame.save.pic = UI_SAVEBUTTON; + + uiSaveGame.listBack.generic.id = ID_LISTBACK; + uiSaveGame.listBack.generic.type = QMTYPE_BITMAP; + uiSaveGame.listBack.generic.flags = QMF_INACTIVE; + uiSaveGame.listBack.generic.x = 42; + uiSaveGame.listBack.generic.y = 146; + uiSaveGame.listBack.generic.width = 462; + uiSaveGame.listBack.generic.height = 476; + uiSaveGame.listBack.pic = ART_LISTBACK; + + uiSaveGame.gameTitle.generic.id = ID_GAMETITLE; + uiSaveGame.gameTitle.generic.type = QMTYPE_ACTION; + uiSaveGame.gameTitle.generic.flags = QMF_INACTIVE; + uiSaveGame.gameTitle.generic.x = 42; + uiSaveGame.gameTitle.generic.y = 146; + uiSaveGame.gameTitle.generic.width = 462; + uiSaveGame.gameTitle.generic.height = 24; + uiSaveGame.gameTitle.generic.ownerdraw = UI_SaveGame_Ownerdraw; + + for( i = 0, y = 182; i < UI_MAXGAMES; i++, y += 32 ) + { + uiSaveGame.listGames[i].generic.id = ID_LISTGAMES+i; + uiSaveGame.listGames[i].generic.type = QMTYPE_ACTION; + uiSaveGame.listGames[i].generic.flags = QMF_SILENT; + uiSaveGame.listGames[i].generic.x = 42; + uiSaveGame.listGames[i].generic.y = y; + uiSaveGame.listGames[i].generic.width = 462; + uiSaveGame.listGames[i].generic.height = 24; + uiSaveGame.listGames[i].generic.callback = UI_SaveGame_Callback; + uiSaveGame.listGames[i].generic.ownerdraw = UI_SaveGame_Ownerdraw; + } + + uiSaveGame.levelShot.generic.id = ID_LEVELSHOT; + uiSaveGame.levelShot.generic.type = QMTYPE_BITMAP; + uiSaveGame.levelShot.generic.flags = QMF_INACTIVE; + uiSaveGame.levelShot.generic.x = 568; + uiSaveGame.levelShot.generic.y = 208; + uiSaveGame.levelShot.generic.width = 414; + uiSaveGame.levelShot.generic.height = 206; + uiSaveGame.levelShot.generic.ownerdraw = UI_SaveGame_Ownerdraw; + uiSaveGame.levelShot.pic = ART_LEVELSHOTBLUR; + + uiSaveGame.newGame.generic.id = ID_NEWGAME; + uiSaveGame.newGame.generic.type = QMTYPE_BITMAP; + uiSaveGame.newGame.generic.x = 676; + uiSaveGame.newGame.generic.y = 468; + uiSaveGame.newGame.generic.width = 198; + uiSaveGame.newGame.generic.height = 38; + uiSaveGame.newGame.generic.callback = UI_SaveGame_Callback; + uiSaveGame.newGame.generic.ownerdraw = UI_SaveGame_Ownerdraw; + uiSaveGame.newGame.pic = UI_NEWGAMEBUTTON; + + uiSaveGame.loadGame.generic.id = ID_LOADGAME; + uiSaveGame.loadGame.generic.type = QMTYPE_BITMAP; + uiSaveGame.loadGame.generic.x = 676; + uiSaveGame.loadGame.generic.y = 516; + uiSaveGame.loadGame.generic.width = 198; + uiSaveGame.loadGame.generic.height = 38; + uiSaveGame.loadGame.generic.callback = UI_SaveGame_Callback; + uiSaveGame.loadGame.generic.ownerdraw = UI_SaveGame_Ownerdraw; + uiSaveGame.loadGame.pic = UI_LOADBUTTON; + + uiSaveGame.deleteGame.generic.id = ID_DELETEGAME; + uiSaveGame.deleteGame.generic.type = QMTYPE_BITMAP; + uiSaveGame.deleteGame.generic.x = 676; + uiSaveGame.deleteGame.generic.y = 564; + uiSaveGame.deleteGame.generic.width = 198; + uiSaveGame.deleteGame.generic.height = 38; + uiSaveGame.deleteGame.generic.callback = UI_SaveGame_Callback; + uiSaveGame.deleteGame.generic.ownerdraw = UI_SaveGame_Ownerdraw; + uiSaveGame.deleteGame.pic = UI_DELETEBUTTON; + + UI_SaveGame_GetGameList(); + + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.background ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.banner ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.back ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.save ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.listBack ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.gameTitle ); + + for( i = 0; i < UI_MAXGAMES; i++ ) + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.listGames[i] ); + + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.levelShot ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.newGame ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.loadGame ); + UI_AddItem( &uiSaveGame.menu, (void *)&uiSaveGame.deleteGame ); +} + +/* +================= +UI_SaveGame_Precache +================= +*/ +void UI_SaveGame_Precache( void ) +{ + if( !re ) return; + + re->RegisterShader( ART_BACKGROUND, SHADER_NOMIP ); + re->RegisterShader( ART_BANNER, SHADER_NOMIP ); + re->RegisterShader( ART_LISTBACK, SHADER_NOMIP ); + re->RegisterShader( ART_LEVELSHOTBLUR, SHADER_NOMIP ); +} + +/* +================= +UI_SaveGame_Menu +================= +*/ +void UI_SaveGame_Menu( void ) +{ + UI_SaveGame_Precache(); + UI_SaveGame_Init(); + + UI_PushMenu( &uiSaveGame.menu ); +} \ No newline at end of file diff --git a/launch/stdlib.c b/launch/stdlib.c index f26a98f4..f1867d50 100644 --- a/launch/stdlib.c +++ b/launch/stdlib.c @@ -525,8 +525,8 @@ const char* com_timestamp( int format ) strftime(timestring, sizeof (timestring), "%Y", crt_tm); break; case TIME_FILENAME: - // Build a timestamp that can use for filename (ex: "Nov2006-26 (19.14)"); - strftime(timestring, sizeof (timestring), "%b%Y-%d (%H.%M)", crt_tm); + // Build a timestamp that can use for filename (ex: "Nov2006-26 (19.14.28)"); + strftime(timestring, sizeof (timestring), "%b%Y-%d_%H.%M.%S", crt_tm); break; default: return NULL; } diff --git a/public/render_api.h b/public/render_api.h index 95471b5b..1a114072 100644 --- a/public/render_api.h +++ b/public/render_api.h @@ -141,6 +141,7 @@ typedef struct render_exp_s bool (*RegisterModel)( const char *name, int cl_index ); // also build replacement index table shader_t (*RegisterShader)( const char *name, int shaderType ); void (*EndRegistration)( const char *skyname ); + void (*FreeShader)( const char *shadername ); // prepare frame to rendering bool (*AddRefEntity)( edict_t *pRefEntity, int ed_type ); diff --git a/vid_gl/r_main.c b/vid_gl/r_main.c index a84bbc64..a0075550 100644 --- a/vid_gl/r_main.c +++ b/vid_gl/r_main.c @@ -2390,6 +2390,7 @@ render_exp_t DLLEXPORT *CreateAPI(stdlib_api_t *input, render_imp_t *engfuncs ) re.RegisterModel = R_UploadModel; re.RegisterShader = Mod_RegisterShader; re.EndRegistration = R_EndRegistration; + re.FreeShader = Mod_FreeShader; re.AddLightStyle = R_AddLightStyle; re.AddRefEntity = R_AddEntityToScene; diff --git a/vid_gl/r_shader.c b/vid_gl/r_shader.c index 48f358d8..c77312ce 100644 --- a/vid_gl/r_shader.c +++ b/vid_gl/r_shader.c @@ -2715,6 +2715,38 @@ void Shader_FreeShader( ref_shader_t *shader ) Mem_Set( shader, 0, sizeof( ref_shader_t )); } +void Mod_FreeShader( const char *name ) +{ + ref_shader_t *shader; + string shortname; + ref_script_t *cache = NULL; + uint i, hashKey, length; + + if( !name || !name[0] ) return; + + for( i = ( name[0] == '/' || name[0] == '\\' ), length = 0; name[i] && ( length < sizeof( shortname )-1 ); i++ ) + { + if( name[i] == '\\' ) shortname[length++] = '/'; + else shortname[length++] = com.tolower( name[i] ); + } + + if( !length ) return; + shortname[length] = 0; + + // see if already loaded + hashKey = Com_HashKey( shortname, SHADERS_HASH_SIZE ); + + for( shader = r_shadersHash[hashKey]; shader; shader = shader->nextHash ) + { + if( !com.stricmp( shader->name, shortname )) + { + // remove shader + Shader_FreeShader( shader ); + return; + } + } +} + void R_ShutdownShaders( void ) { int i; diff --git a/vid_gl/r_shader.h b/vid_gl/r_shader.h index fc4415ce..1ebc611c 100644 --- a/vid_gl/r_shader.h +++ b/vid_gl/r_shader.h @@ -347,6 +347,7 @@ void R_ShutdownShaders( void ); void R_ShaderList_f( void ); void R_ShaderDump_f( void ); ref_shader_t *R_LoadShader( const char *name, int type, bool forceDefault, int addFlags, int ignoreType ); +void Mod_FreeShader( const char *name ); // used for delete save menu previews only // misc utilities void R_ShaderFreeUnused( void );