From 24a33f940df2cce689ae3e66a4dcecfec5ffa9af Mon Sep 17 00:00:00 2001 From: jsalmon3 <> Date: Thu, 1 Aug 2002 16:22:02 +0000 Subject: [PATCH] Added initial support for game replay --- src/include/commands.h | 9 +- src/network/commands.cpp | 370 ++++++++++++++++++++++++++--- src/stratagus/mainloop.cpp | 3 + src/stratagus/stratagus.cpp | 2 +- src/ui/menus.cpp | 447 +++++++++++++++++++++++++++++++++++- 5 files changed, 793 insertions(+), 38 deletions(-) diff --git a/src/include/commands.h b/src/include/commands.h index 64738db5c..9fc709b7d 100644 --- a/src/include/commands.h +++ b/src/include/commands.h @@ -32,12 +32,19 @@ -- Variables ----------------------------------------------------------------------------*/ -extern int CommandLogEnabled; /// True, if command log is on +extern int CommandLogDisabled; /// True, if command log is off /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ + /// Replay user commands from log each cycle +extern void ReplayEachCycle(void); + /// Load replay +extern int LoadReplay(char* name); + /// End logging +extern void EndReplayLog(void); + /* ** The send command functions sends a command, if needed over the ** Network, this is only for user commands. Automatic reactions which diff --git a/src/network/commands.cpp b/src/network/commands.cpp index e06dd958a..668bc5a3e 100644 --- a/src/network/commands.cpp +++ b/src/network/commands.cpp @@ -44,6 +44,10 @@ #include "network.h" #include "netconnect.h" #include "campaign.h" // for CurrentMapPath +#include "ccl.h" +#include "commands.h" +#include "interface.h" +#include "iocompat.h" //---------------------------------------------------------------------------- // Declaration @@ -53,7 +57,12 @@ // Variables //---------------------------------------------------------------------------- -global int CommandLogEnabled; /// True if command log is on +global int CommandLogDisabled; /// True if command log is off +local int DisabledLog; /// Disabled log for replay +local SCM ReplayLog; /// Replay log +local FILE* LogFile; /// Replay log file +local int NextLogCycle; /// Next log cycle number + //---------------------------------------------------------------------------- // Log commands @@ -79,9 +88,7 @@ global int CommandLogEnabled; /// True if command log is on local void CommandLog(const char* name,const Unit* unit,int flag, unsigned x,unsigned y,const Unit* dest,const char* value,int num) { - static FILE* logf; - - if( !CommandLogEnabled ) { // No log wanted + if( CommandLogDisabled ) { // No log wanted return; } @@ -89,15 +96,25 @@ local void CommandLog(const char* name,const Unit* unit,int flag, // Create and write header of log file. The player number is added // to the save file name, to test more than one player on one computer. // - if( !logf ) { + if( !LogFile ) { time_t now; - char buf[256]; + char buf[PATH_MAX]; char* s; char* s1; - sprintf(buf,"log_of_freecraft_%d.log",ThisPlayer->Player); - logf=fopen(buf,"wb"); - if( !logf ) { +#ifdef USE_WIN32 + strcpy(buf,"logs"); + mkdir(buf); +#else + sprintf(buf,"%s/%s",getenv("HOME"),FREECRAFT_HOME_PATH); + mkdir(buf,0777); + strcat(buf,"/logs"); + mkdir(buf,0777); +#endif + + sprintf(buf,"%s/log_of_freecraft_%d.log",buf,ThisPlayer->Player); + LogFile=fopen(buf,"wb"); + if( !LogFile ) { return; } @@ -109,34 +126,35 @@ local void CommandLog(const char* name,const Unit* unit,int flag, // // Parseable header + // TODO: add custom start options (eg high resources, game type, etc) // - fprintf(logf,";;;(replay-log\n"); - fprintf(logf,";;; 'comment\t\"Generated by FreeCraft Version " VERSION "\"\n"); - fprintf(logf,";;; 'comment\t\"Visit http://FreeCraft.Org for more informations\"\n"); - fprintf(logf,";;; 'comment\t\"$Id$\"\n"); - fprintf(logf,";;; 'type\t\"%s\"\n","single-player"); - fprintf(logf,";;; 'date\t\"%s\"\n",s); - fprintf(logf,";;; 'map\t\"%s\"\n",TheMap.Description); - fprintf(logf,";;; 'map-id\t%u\n",TheMap.Info->MapUID); - fprintf(logf,";;; 'map-path\t\"%s\"\n",CurrentMapPath); - fprintf(logf,";;; 'engine\t'(%d %d %d)\n", + fprintf(LogFile,"(replay-log\n"); + fprintf(LogFile," 'comment\t\"Generated by FreeCraft Version " VERSION "\"\n"); + fprintf(LogFile," 'comment\t\"Visit http://FreeCraft.Org for more informations\"\n"); + fprintf(LogFile," 'comment\t\"$Id$\"\n"); + fprintf(LogFile," 'type\t\"%s\"\n","single-player"); + fprintf(LogFile," 'date\t\"%s\"\n",s); + fprintf(LogFile," 'map\t\"%s\"\n",TheMap.Description); + fprintf(LogFile," 'map-id\t%u\n",TheMap.Info->MapUID); + fprintf(LogFile," 'map-path\t\"%s\"\n",CurrentMapPath); + fprintf(LogFile," 'engine\t'(%d %d %d)\n", FreeCraftMajorVersion,FreeCraftMinorVersion,FreeCraftPatchLevel); - fprintf(logf,";;; 'network\t'(%d %d %d)\n", + fprintf(LogFile," 'network\t'(%d %d %d)\n", NetworkProtocolMajorVersion, NetworkProtocolMinorVersion, NetworkProtocolPatchLevel); - fprintf(logf,";;; )\n"); + fprintf(LogFile," )\n"); } // // Frame, unit, (type-ident only to be better readable). // if( unit ) { - fprintf(logf,"(log %lu 'U%d '%s '%s '%s", + fprintf(LogFile,"(log %lu 'unit %d 'ident '%s 'name '%s 'flag '%s", GameCycle,UnitNumber(unit),unit->Type->Ident,name, flag ? "flush" : "append"); } else { - fprintf(logf,"(log %lu '%s '%s", + fprintf(LogFile,"(log %lu 'name '%s 'flag '%s", GameCycle,name, flag ? "flush" : "append"); } @@ -144,35 +162,325 @@ local void CommandLog(const char* name,const Unit* unit,int flag, // Coordinates given. // if( x!=-1 || y!=-1 ) { - fprintf(logf," '(%d %d)",x,y); + fprintf(LogFile," 'pos '(%d %d)",x,y); } // // Destination given. // if( dest ) { - fprintf(logf," 'U%d",UnitNumber(dest)); + fprintf(LogFile," 'dest '%d",UnitNumber(dest)); } // // Value given. // if( value ) { - fprintf(logf," '%s",value); + fprintf(LogFile," 'value '%s",value); } // // Number given. // if( num!=-1 ) { - fprintf(logf," %d",num); + fprintf(LogFile," 'num %d",num); } if( unit ) { - fprintf(logf,") ;%d:%d %X",unit->Player->Player,unit->Refs, + fprintf(LogFile,") ;%d:%d %X",unit->Player->Player,unit->Refs, SyncRandSeed); } else { - fprintf(logf,") ;-:- %X",SyncRandSeed); + fprintf(LogFile,") ;-:- %X",SyncRandSeed); } - fprintf(logf,"\n"); - fflush(logf); + fprintf(LogFile,"\n"); + fflush(LogFile); +} + +/** +** +*/ +local SCM CclLog(SCM list) +{ + SCM var; + + var=gh_symbol2scm("*replay_log*"); + if( gh_null_p(symbol_value(var,NIL)) ) { + setvar(var,cons(list,NIL),NIL); + } else { + SCM tmp; + tmp=symbol_value(var,NIL); + while( !gh_null_p(gh_cdr(tmp)) ) { + tmp=gh_cdr(tmp); + } + setcdr(tmp,cons(list,NIL)); + } + + return SCM_UNSPECIFIED; +} + +/** +** +*/ +local SCM CclReplayLog(SCM list) +{ + SCM value; + SCM sublist; + const char* comment; + const char* logtype; + const char* logdate; + const char* map; + unsigned int mapid; + const char* mappath; + int ever1; + int ever2; + int ever3; + int nver1; + int nver2; + int nver3; + + while( !gh_null_p(list) ) { + value=gh_car(list); + list=gh_cdr(list); + + if( gh_eq_p(value,gh_symbol2scm("comment")) ) { + comment=get_c_string(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("type")) ) { + logtype=get_c_string(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("date")) ) { + logdate=get_c_string(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("map")) ) { + map=get_c_string(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("map-id")) ) { + mapid=gh_scm2int(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("map-path")) ) { + mappath=get_c_string(gh_car(list)); + strcpy(CurrentMapPath,mappath); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("engine")) ) { + sublist=gh_car(list); + ever1=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + ever2=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + ever3=gh_scm2int(gh_car(sublist)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("network")) ) { + sublist=gh_car(list); + nver1=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + nver2=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + nver3=gh_scm2int(gh_car(sublist)); + list=gh_cdr(list); + } + } + + return SCM_UNSPECIFIED; +} + +/** +** Load a log file to replay a game +** +** @param name name of file to load. +*/ +global int LoadReplay(char* name) +{ + gh_new_procedureN("log",CclLog); + gh_new_procedureN("replay-log",CclReplayLog); + gh_define("*replay_log*",NIL); + vload(name,0,1); + + ReplayLog=symbol_value(gh_symbol2scm("*replay_log*"),NIL); + NextLogCycle=-1; + if( !CommandLogDisabled ) { + CommandLogDisabled=1; + DisabledLog=1; + } + + return 0; +} + +/** +** End logging +*/ +global void EndReplayLog(void) +{ + if( LogFile ) { + fclose(LogFile); + LogFile=NULL; + } + if( DisabledLog ) { + CommandLogDisabled=0; + DisabledLog=0; + } +} + +/** +** Do next replay +*/ +local void DoNextReplay(void) +{ + SCM value; + SCM list; + int unit; + const char* ident; + const char* name; + const char* flag; + int flags; + int posx; + int posy; + int dest; + const char* val; + int num; + + list=gh_car(ReplayLog); + + NextLogCycle=gh_scm2int(gh_car(list)); + list=gh_cdr(list); + + if( NextLogCycle!=GameCycle ) { + return; + } + + NextLogCycle=-1; + unit=-1; + name=NULL; + flags=0; + posx=-1; + posy=-1; + dest=-1; + val=NULL; + num=-1; + while( !gh_null_p(list) ) { + value=gh_car(list); + list=gh_cdr(list); + + if( gh_eq_p(value,gh_symbol2scm("unit")) ) { + unit=gh_scm2int(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("ident")) ) { + ident=get_c_string(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("name")) ) { + name=get_c_string(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("flag")) ) { + flag=get_c_string(gh_car(list)); + if( !strcmp(flag,"flush") ) { + flags=1; + } + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("pos")) ) { + SCM sublist; + sublist=gh_car(list); + posx=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + posy=gh_scm2int(gh_car(sublist)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("dest")) ) { + dest=gh_scm2int(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("value")) ) { + val=get_c_string(gh_car(list)); + list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("num")) ) { + num=gh_scm2int(gh_car(list)); + list=gh_cdr(list); + } + } + + if( !strcmp(name,"stop") ) { + SendCommandStopUnit(Units[unit]); + } else if( !strcmp(name,"stand-ground") ) { + SendCommandStandGround(Units[unit],flags); + } else if( !strcmp(name,"follow") ) { + SendCommandFollow(Units[unit],Units[dest],flags); + } else if( !strcmp(name,"move") ) { + SendCommandMove(Units[unit],posx,posy,flags); + } else if( !strcmp(name,"repair") ) { + SendCommandRepair(Units[unit],posx,posy,Units[dest],flags); + } else if( !strcmp(name,"attack") ) { + SendCommandAttack(Units[unit],posx,posy,Units[dest],flags); + } else if( !strcmp(name,"attack-ground") ) { + SendCommandAttackGround(Units[unit],posx,posy,flags); + } else if( !strcmp(name,"patrol") ) { + SendCommandPatrol(Units[unit],posx,posy,flags); + } else if( !strcmp(name,"board") ) { + SendCommandBoard(Units[unit],posx,posy,Units[dest],flags); + } else if( !strcmp(name,"unload") ) { + SendCommandUnload(Units[unit],posx,posy,dest!=-1?Units[dest]:NoUnitP,flags); + } else if( !strcmp(name,"build") ) { + SendCommandBuildBuilding(Units[unit],posx,posy,UnitTypeByIdent(val),flags); + } else if( !strcmp(name,"cancel-build") ) { + SendCommandCancelBuilding(Units[unit],Units[dest]); + } else if( !strcmp(name,"harvest") ) { + SendCommandHarvest(Units[unit],posx,posy,flags); + } else if( !strcmp(name,"mine") ) { + SendCommandMineGold(Units[unit],Units[dest],flags); + } else if( !strcmp(name,"haul") ) { + SendCommandHaulOil(Units[unit],Units[dest],flags); + } else if( !strcmp(name,"return") ) { + SendCommandReturnGoods(Units[unit],Units[dest],flags); + } else if( !strcmp(name,"train") ) { + SendCommandTrainUnit(Units[unit],UnitTypeByIdent(val),flags); + } else if( !strcmp(name,"cancel-train") ) { + SendCommandCancelTraining(Units[unit],num,UnitTypeByIdent(val)); + } else if( !strcmp(name,"upgrade-to") ) { + SendCommandUpgradeTo(Units[unit],UnitTypeByIdent(val),flags); + } else if( !strcmp(name,"cancel-upgrade-to") ) { + SendCommandCancelUpgradeTo(Units[unit]); + } else if( !strcmp(name,"research") ) { + SendCommandResearch(Units[unit],UpgradeByIdent(val),flags); + } else if( !strcmp(name,"cancel-research") ) { + SendCommandCancelResearch(Units[unit]); + } else if( !strcmp(name,"demolish") ) { + SendCommandDemolish(Units[unit],posx,posy,Units[dest],flags); + } else if( !strcmp(name,"spell-cast") ) { + SendCommandSpellCast(Units[unit],posx,posy,Units[dest],num,flags); + } else if( !strcmp(name,"diplomacy") ) { + int state; + if( !strcmp(val,"neutral") ) { + state=DiplomacyNeutral; + } else if( !strcmp(val,"allied") ) { + state=DiplomacyAllied; + } else if( !strcmp(val,"enemy") ) { + state=DiplomacyEnemy; + } else if( !strcmp(val,"crazy") ) { + state=DiplomacyCrazy; + } else { + DebugLevel0Fn("Invalid diplomacy command: %s" _C_ val); + state=-1; + } + SendCommandDiplomacy(posx,state,posy); + } else { + DebugLevel0Fn("Invalid name: %s" _C_ name); + } + + ReplayLog=gh_cdr(ReplayLog); +} + +/** +** Replay user commands from log each cycle +*/ +global void ReplayEachCycle(void) +{ + if( gh_null_p(ReplayLog) ) { + return; + } + + if( NextLogCycle!=-1 && NextLogCycle!=GameCycle ) { + return; + } + + do { + DoNextReplay(); + } while( !gh_null_p(ReplayLog) && (NextLogCycle==-1 || NextLogCycle==GameCycle) ); + + if( gh_null_p(ReplayLog) ) { + SetMessage("End of replay"); + } } //@} diff --git a/src/stratagus/mainloop.cpp b/src/stratagus/mainloop.cpp index d82260303..5b3ece5c1 100644 --- a/src/stratagus/mainloop.cpp +++ b/src/stratagus/mainloop.cpp @@ -58,6 +58,7 @@ #include "campaign.h" #include "sound_server.h" #include "settings.h" +#include "commands.h" #if defined(USE_SDLCD) || defined(USE_LIBCDA) #include "sound_server.h" @@ -740,6 +741,7 @@ global void GameMainLoop(void) fprintf(stderr,"FIXME: *** round robin ***\n"); fprintf(stderr,"FIXME: *** round robin ***\n"); } + ReplayEachCycle(); NetworkCommands(); // Get network commands UnitActions(); // handle units MissileActions(); // handle missiles @@ -865,6 +867,7 @@ global void GameMainLoop(void) // Game over // NetworkQuit(); + EndReplayLog(); if( GameResult==GameDefeat ) { fprintf(stderr,"You have lost!\n"); SetStatusLine("You have lost!"); diff --git a/src/stratagus/stratagus.cpp b/src/stratagus/stratagus.cpp index bea096d5c..b66983857 100644 --- a/src/stratagus/stratagus.cpp +++ b/src/stratagus/stratagus.cpp @@ -1339,7 +1339,7 @@ global int main(int argc,char** argv) AiCostFactor=atoi(optarg); continue; case 'l': - CommandLogEnabled=1; + CommandLogDisabled=1; continue; case 'P': NetworkPort=atoi(optarg); diff --git a/src/ui/menus.cpp b/src/ui/menus.cpp index 0390f47c1..4a0e3f20f 100644 --- a/src/ui/menus.cpp +++ b/src/ui/menus.cpp @@ -62,6 +62,7 @@ #include "sound.h" #include "ccl.h" #include "editor.h" +#include "commands.h" #if defined(USE_SDLCD) || defined(USE_SDLA) #include "SDL.h" @@ -279,6 +280,18 @@ local void EditorSaveConfirmOk(void); local void EditorSaveConfirmCancel(void); local void EditorQuitMenu(void); +local void ReplayGameMenu(void); +local void ReplayGameInit(Menuitem *mi); +local void ReplayGameLBInit(Menuitem *mi); +local void ReplayGameLBExit(Menuitem *mi); +local int ReplayGameRDFilter(char *pathbuf, FileList *fl); +local void ReplayGameLBAction(Menuitem *mi, int i); +local unsigned char *ReplayGameLBRetrieve(Menuitem *mi, int i); +local void ReplayGameVSAction(Menuitem *mi, int i); +local void ReplayGameFolder(void); +local void ReplayGameOk(void); +local void ReplayGameCancel(void); + /*---------------------------------------------------------------------------- -- Variables ----------------------------------------------------------------------------*/ @@ -580,6 +593,18 @@ global void InitMenuFuncHash(void) { HASHADD(EditorSaveConfirmInit,"editor-save-confirm-init"); HASHADD(EditorSaveConfirmOk,"editor-save-confirm-ok"); HASHADD(EditorSaveConfirmCancel,"editor-save-confirm-cancel"); + +// Replay game + HASHADD(ReplayGameMenu,"replay-game-menu"); + HASHADD(ReplayGameInit,"replay-game-init"); + HASHADD(ReplayGameLBInit,"replay-game-lb-init"); + HASHADD(ReplayGameLBExit,"replay-game-lb-exit"); + HASHADD(ReplayGameLBAction,"replay-game-lb-action"); + HASHADD(ReplayGameLBRetrieve,"replay-game-lb-retrieve"); + HASHADD(ReplayGameVSAction,"replay-game-vs-action"); + HASHADD(ReplayGameFolder,"replay-game-folder"); + HASHADD(ReplayGameOk,"replay-game-ok"); + HASHADD(ReplayGameCancel,"replay-game-cancel"); } /*---------------------------------------------------------------------------- @@ -905,7 +930,8 @@ local void SaveVSAction(Menuitem *mi, int i) } DebugCheck(mi->d.listbox.startline < 0); - DebugCheck(mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); + DebugCheck(mi->d.listbox.noptions > 0 && + mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); SaveLBAction(mi, mi->d.listbox.curopt + mi->d.listbox.startline); MustRedraw |= RedrawMenu; @@ -1178,7 +1204,8 @@ local void LoadVSAction(Menuitem *mi, int i) } DebugCheck(mi->d.listbox.startline < 0); - DebugCheck(mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); + DebugCheck(mi->d.listbox.noptions > 0 && + mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); LoadLBAction(mi, mi->d.listbox.curopt + mi->d.listbox.startline); MustRedraw |= RedrawMenu; @@ -2842,7 +2869,8 @@ local void ScenSelectVSAction(Menuitem *mi, int i) } DebugCheck(mi->d.listbox.startline < 0); - DebugCheck(mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); + DebugCheck(mi->d.listbox.noptions > 0 && + mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); ScenSelectLBAction(mi, mi->d.listbox.curopt + mi->d.listbox.startline); MustRedraw |= RedrawMenu; @@ -4668,7 +4696,8 @@ local void EditorMainLoadVSAction(Menuitem *mi, int i) } DebugCheck(mi->d.listbox.startline < 0); - DebugCheck(mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); + DebugCheck(mi->d.listbox.noptions > 0 && + mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); EditorMainLoadLBAction(mi, mi->d.listbox.curopt + mi->d.listbox.startline); MustRedraw |= RedrawMenu; @@ -5344,7 +5373,8 @@ local void EditorSaveVSAction(Menuitem *mi, int i) } DebugCheck(mi->d.listbox.startline < 0); - DebugCheck(mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); + DebugCheck(mi->d.listbox.noptions > 0 && + mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); EditorSaveLBAction(mi, mi->d.listbox.curopt + mi->d.listbox.startline); MustRedraw |= RedrawMenu; @@ -5416,6 +5446,413 @@ local void EditorEndMenu(void) MustRedraw = RedrawMenu; } +/** +** Replay game menu +*/ +local void ReplayGameMenu(void) +{ + char buf[PATH_MAX]; + +#ifdef USE_WIN32 + strcpy(buf,"logs"); + mkdir(buf); + + sprintf(ScenSelectPath, "logs/"); +#else + sprintf(buf,"%s/%s",getenv("HOME"),FREECRAFT_HOME_PATH); + mkdir(buf,0777); + strcat(buf,"/logs"); + mkdir(buf,0777); + + sprintf(ScenSelectPath,"%s/%s/logs/", getenv("HOME"), FREECRAFT_HOME_PATH); +#endif + *ScenSelectDisplayPath = '\0'; + + VideoLockScreen(); + VideoClearScreen(); + MenusSetBackground(); + VideoUnlockScreen(); + Invalidate(); + + GuiGameStarted = 0; + ProcessMenu("menu-replay-game",1); + if (GuiGameStarted) { + GameMenuReturn(); + } +} + +/** +** Replay game menu init callback +*/ +local void ReplayGameInit(Menuitem *mi) +{ + DebugCheck(!*ScenSelectPath); + mi->menu->items[5].flags = + *ScenSelectDisplayPath ? 0 : MenuButtonDisabled; + mi->menu->items[5].d.button.text = ScenSelectDisplayPath; + DebugLevel0Fn("Start path: %s\n" _C_ ScenSelectPath); +} + +/** +** Replay game listbox init callback +*/ +local void ReplayGameLBInit(Menuitem *mi) +{ + int i; + + ReplayGameLBExit(mi); + i = mi->d.listbox.noptions = ReadDataDirectory(ScenSelectPath, ReplayGameRDFilter, + (FileList **)&(mi->d.listbox.options)); + + if (i == 0) { + mi->menu->items[3].d.button.text = "OK"; + mi->menu->items[3].flags |= MenuButtonDisabled; + } else { + ReplayGameLBAction(mi, 0); + mi->menu->items[3].flags &= ~MenuButtonDisabled; + if (i > 5) { + mi[1].flags &= ~MenuButtonDisabled; + } + } +} + +/** +** Replay game listbox exit callback +*/ +local void ReplayGameLBExit(Menuitem *mi) +{ + FileList *fl; + + if (mi->d.listbox.noptions) { + fl = mi->d.listbox.options; + free(fl); + mi->d.listbox.options = NULL; + mi->d.listbox.noptions = 0; + mi[1].flags |= MenuButtonDisabled; + } +} + +/** +** Replay game read directory filter +*/ +local int ReplayGameRDFilter(char *pathbuf, FileList *fl) +{ + char *suf; + char *np; + char *cp; + char *lcp; +#ifdef USE_ZZIPLIB + int sz; + ZZIP_FILE *zzf; +#endif + + suf = ".log"; + np = strrchr(pathbuf, '/'); + if (np) { + np++; + } else { + np = pathbuf; + } + cp = np; + cp--; + fl->type = -1; +#ifdef USE_ZZIPLIB + if ((zzf = zzip_open(pathbuf, O_RDONLY|O_BINARY))) { + sz = zzip_file_real(zzf); + zzip_close(zzf); + if (!sz) { + goto usezzf; + } + } +#endif + do { + lcp = cp++; + cp = strcasestr(cp, suf); + } while (cp != NULL); + if (lcp >= np) { + cp = lcp + strlen(suf); +#ifdef USE_ZLIB + if (strcmp(cp, ".gz") == 0) { + *cp = 0; + } +#endif +#ifdef USE_BZ2LIB + if (strcmp(cp, ".bz2") == 0) { + *cp = 0; + } +#endif + if (*cp == 0) { +#ifdef USE_ZZIPLIB +usezzf: +#endif + if (strcasestr(pathbuf, ".log")) { + fl->type = 1; + fl->name = strdup(np); + return 1; + } + } + } + return 0; +} + +/** +** Replay game listbox action +*/ +local void ReplayGameLBAction(Menuitem *mi, int i) +{ + FileList *fl; + + DebugCheck(i<0); + if (i < mi->d.listbox.noptions) { + fl = mi->d.listbox.options; + if (fl[i].type) { + mi->menu->items[3].d.button.text = "OK"; + } else { + mi->menu->items[3].d.button.text = "Open"; + } + if (mi->d.listbox.noptions > 5) { + mi[1].d.vslider.percent = (i * 100) / (mi->d.listbox.noptions - 1); + mi[1].d.hslider.percent = (i * 100) / (mi->d.listbox.noptions - 1); + } + } +} + +/** +** Replay game listbox retrieve +*/ +local unsigned char *ReplayGameLBRetrieve(Menuitem *mi, int i) +{ + FileList *fl; + static char buffer[1024]; + + if (i < mi->d.listbox.noptions) { + fl = mi->d.listbox.options; + if (fl[i].type) { + strcpy(buffer, " "); + } else { + strcpy(buffer, "\260 "); + } + strcat(buffer, fl[i].name); + return buffer; + } + return NULL; +} + +/** +** Replay game vertical scroll action +*/ +local void ReplayGameVSAction(Menuitem *mi, int i) +{ + int op; + int d1; + int d2; + + mi--; + switch (i) { + case 0: // click - down + case 2: // key - down + if (mi[1].d.vslider.cflags&MI_CFLAGS_DOWN) { + if (mi->d.listbox.curopt+mi->d.listbox.startline+1 < mi->d.listbox.noptions) { + mi->d.listbox.curopt++; + if (mi->d.listbox.curopt >= mi->d.listbox.nlines) { + mi->d.listbox.curopt--; + mi->d.listbox.startline++; + } + MustRedraw |= RedrawMenu; + } + } else if (mi[1].d.vslider.cflags&MI_CFLAGS_UP) { + if (mi->d.listbox.curopt+mi->d.listbox.startline > 0) { + mi->d.listbox.curopt--; + if (mi->d.listbox.curopt < 0) { + mi->d.listbox.curopt++; + mi->d.listbox.startline--; + } + MustRedraw |= RedrawMenu; + } + } + ReplayGameLBAction(mi, mi->d.listbox.curopt + mi->d.listbox.startline); + if (i == 2) { + mi[1].d.vslider.cflags &= ~(MI_CFLAGS_DOWN|MI_CFLAGS_UP); + } + break; + case 1: // mouse - move + if (mi[1].d.vslider.cflags&MI_CFLAGS_KNOB && (mi[1].flags&MenuButtonClicked)) { + if (mi[1].d.vslider.curper > mi[1].d.vslider.percent) { + if (mi->d.listbox.curopt+mi->d.listbox.startline+1 < mi->d.listbox.noptions) { + for (;;) { + op = ((mi->d.listbox.curopt + mi->d.listbox.startline + 1) * 100) / + (mi->d.listbox.noptions - 1); + d1 = mi[1].d.vslider.curper - mi[1].d.vslider.percent; + d2 = op - mi[1].d.vslider.curper; + if (d2 >= d1) + break; + mi->d.listbox.curopt++; + if (mi->d.listbox.curopt >= mi->d.listbox.nlines) { + mi->d.listbox.curopt--; + mi->d.listbox.startline++; + } + if (mi->d.listbox.curopt+mi->d.listbox.startline+1 == mi->d.listbox.noptions) + break; + } + } + } else if (mi[1].d.vslider.curper < mi[1].d.vslider.percent) { + if (mi->d.listbox.curopt+mi->d.listbox.startline > 0) { + for (;;) { + op = ((mi->d.listbox.curopt + mi->d.listbox.startline - 1) * 100) / + (mi->d.listbox.noptions - 1); + d1 = mi[1].d.vslider.percent - mi[1].d.vslider.curper; + d2 = mi[1].d.vslider.curper - op; + if (d2 >= d1) + break; + mi->d.listbox.curopt--; + if (mi->d.listbox.curopt < 0) { + mi->d.listbox.curopt++; + mi->d.listbox.startline--; + } + if (mi->d.listbox.curopt+mi->d.listbox.startline == 0) + break; + } + } + } + + DebugCheck(mi->d.listbox.startline < 0); + DebugCheck(mi->d.listbox.noptions > 0 && + mi->d.listbox.startline+mi->d.listbox.curopt >= mi->d.listbox.noptions); + + ReplayGameLBAction(mi, mi->d.listbox.curopt + mi->d.listbox.startline); + MustRedraw |= RedrawMenu; + } + break; + default: + break; + } +} + +/** +** Replay game folder button callback +*/ +local void ReplayGameFolder(void) +{ + Menu *menu; + Menuitem *mi; + char *cp; + + menu = FindMenu("menu-replay-game"); + mi = &menu->items[1]; + + if (ScenSelectDisplayPath[0]) { + cp = strrchr(ScenSelectDisplayPath, '/'); + if (cp) { + *cp = 0; + } else { + ScenSelectDisplayPath[0] = 0; + menu->items[5].flags |= MenuButtonDisabled; + menu->items[5].d.button.text = NULL; + } + cp = strrchr(ScenSelectPath, '/'); + if (cp) { + *cp = 0; + ReplayGameLBInit(mi); + mi->d.listbox.cursel = -1; + mi->d.listbox.startline = 0; + mi->d.listbox.curopt = 0; + mi[1].d.vslider.percent = 0; + mi[1].d.hslider.percent = 0; + MustRedraw |= RedrawMenu; + } + } +} + +/** +** Replay game ok button callback +*/ +local void ReplayGameOk(void) +{ + Menu *menu; + Menuitem *mi; + FileList *fl; + int i; + + menu = FindMenu("menu-replay-game"); + mi = &menu->items[1]; + i = mi->d.listbox.curopt + mi->d.listbox.startline; + if (i < mi->d.listbox.noptions) { + fl = mi->d.listbox.options; + if (fl[i].type == 0) { + strcat(ScenSelectPath, "/"); + strcat(ScenSelectPath, fl[i].name); + if (menu->items[5].flags&MenuButtonDisabled) { + menu->items[5].flags &= ~MenuButtonDisabled; + menu->items[5].d.button.text = ScenSelectDisplayPath; + } else { + strcat(ScenSelectDisplayPath, "/"); + } + strcat(ScenSelectDisplayPath, fl[i].name); + ReplayGameLBInit(mi); + mi->d.listbox.cursel = -1; + mi->d.listbox.startline = 0; + mi->d.listbox.curopt = 0; + mi[1].d.vslider.percent = 0; + mi[1].d.hslider.percent = 0; + MustRedraw |= RedrawMenu; + } else { + strcpy(ScenSelectFileName, fl[i].name); // Final map name + + if (ScenSelectPath[0]) { + strcat(ScenSelectPath, "/"); + strcat(ScenSelectPath, ScenSelectFileName); + } else { + strcpy(ScenSelectPath, ScenSelectFileName); + } + + LoadReplay(ScenSelectPath); + + for (i = 0; i < MAX_OBJECTIVES; i++) { + if (GameIntro.Objectives[i]) { + free(GameIntro.Objectives[i]); + GameIntro.Objectives[i] = NULL; + } + } + GameIntro.Objectives[0] = strdup(DefaultObjective); + + GuiGameStarted = 1; + EndMenu(); + } + } +} + +/** +** Replay game cancel button callback +*/ +local void ReplayGameCancel(void) +{ + char* s; + + // + // Use last selected map. + // + DebugLevel0Fn("Map path: %s\n" _C_ CurrentMapPath); + strcpy(ScenSelectPath, FreeCraftLibPath); + if (*ScenSelectPath) { + strcat(ScenSelectPath, "/"); + } + strcat(ScenSelectPath, CurrentMapPath); + if ((s = strrchr(ScenSelectPath, '/'))) { + strcpy(ScenSelectFileName, s + 1); + *s = '\0'; + } + strcpy(ScenSelectDisplayPath, CurrentMapPath); + if ((s = strrchr(ScenSelectDisplayPath, '/'))) { + *s = '\0'; + } else { + *ScenSelectDisplayPath = '\0'; + } + + DebugLevel0Fn("Start path: %s\n" _C_ ScenSelectPath); + + EndMenu(); +} + /*---------------------------------------------------------------------------- -- Init functions ----------------------------------------------------------------------------*/