diff --git a/BuGS/game.h b/BuGS/game.h index 0de2e07..77c5b16 100644 --- a/BuGS/game.h +++ b/BuGS/game.h @@ -9,6 +9,12 @@ #ifndef _GUARD_PROJECTBuGS_FILEgame_ #define _GUARD_PROJECTBuGS_FILEgame_ + +#include "tileData.h" + +// These are globals used from assembly. +extern char globalScoreInfo[GAME_NUM_TILES_WIDE + 1]; + // These are assembly functions called from C. extern void game(void); @@ -18,6 +24,7 @@ extern void uploadSpin1(void); extern void uploadSpin2(void); extern void uploadSpin3(void); extern void displayConnectionString(void); +extern void displayScorePosition(void); extern void swapStereoChannels(void); diff --git a/BuGS/gameScorpion.s b/BuGS/gameScorpion.s index 7591ff9..f7dab46 100644 --- a/BuGS/gameScorpion.s +++ b/BuGS/gameScorpion.s @@ -223,7 +223,6 @@ updateScorpion_explosionDone anop setScorpionSpeed entry -; TODO - Call this code with each level to set the scorpion speed cmp #SPRITE_SPEED_FAST beq setScorpionSpeed_fast diff --git a/BuGS/global.macros b/BuGS/global.macros index 469faa1..8ea05ba 100644 --- a/BuGS/global.macros +++ b/BuGS/global.macros @@ -23,12 +23,14 @@ _drawDirtyGameRow_wait&rowNum anop ; offset numbers. ; ; Also according to that technote, it looks like these numbers are different -; in PAL mode. TODO - Do I need something here to handle PAL correctly? I -; switched my GS to 50Hz mode (startup/reboot with option held down to get the -; menu) and I didnt't detect any graphics glitches at all. I did notice that -; the game is noticably easier because things run a bit slower. If I do an -; online score system, I should record whether a score was gotten at 50Hz vs -; 60Hz. +; in PAL mode. Do I need something here to handle PAL correctly? I switched +; my GS to 50Hz mode (startup/reboot with option held down to get the menu) +; and I didnt't detect any graphics glitches at all. I did notice that the +; game is noticably easier because things run a bit slower. If I do an online +; score system, I should record whether a score was gotten at 50Hz vs 60Hz. +; (and in fact, I have since implemented online high scores and record the +; frequency at which the game was played) + lda >VERTICAL_COUNTER ; load the counter value and #$80ff ; mask out the VBL bits asl a ; shift the word around diff --git a/BuGS/globalScores.c b/BuGS/globalScores.c index c79a03c..835d1ad 100644 --- a/BuGS/globalScores.c +++ b/BuGS/globalScores.c @@ -140,6 +140,7 @@ typedef enum tGameNetworkState { typedef struct tGameNetworkGlobals { Boolean networkStartedConnected; + tHighScoreInitParams initParams; tGameNetworkState gameNetworkState; dnrBuffer domainNameResolution; srBuff tcpStatus; @@ -152,7 +153,6 @@ typedef struct tGameNetworkGlobals { tHighScoreRequestWithHash highScoreRequest; Boolean hasHighScoreToSend; tStatusResponse setHighScoreResponse; - tHighScoreInitParams initParams; uint16_t errorCode; uint16_t timeout; } tGameNetworkGlobals; @@ -167,11 +167,11 @@ static tGameNetworkGlobals * networkGlobals = NULL; // The following globals are accessed by name from assembly so are not in the // tGameNetworkGlobals structure. +// TODO - Make real interfaces for these things. Boolean hasGlobalHighScores = FALSE; tScoresResponse highScoreResponse; -Word globalScoreAge = 0; +Word globalScoreAge = 0; // TODO - Replace this with a call to a MiscTool function? tSetHighScoreRequestWithHash setHighScoreRequest; -char globalScoreInfo[26]; // Implementation @@ -383,7 +383,7 @@ void pollNetwork(void) case GAME_NETWORK_UNCONNECTED: if (networkGlobals->initParams.displayConnectionString != NULL) - networkGlobals->initParams.displayConnectionString(); + networkGlobals->initParams.displayConnectionString(TRUE); TCPIPConnect(NULL); if ((!toolerror()) && (TCPIPGetConnectStatus())) { @@ -391,6 +391,8 @@ void pollNetwork(void) } else { networkGlobals->gameNetworkState = GAME_NETWORK_CONNECT_FAILED; } + if (networkGlobals->initParams.displayConnectionString != NULL) + networkGlobals->initParams.displayConnectionString(FALSE); break; case GAME_NETWORK_CONNECTED: @@ -721,16 +723,9 @@ BOOLEAN sendHighScore(void) if (networkGlobals->gameNetworkState != GAME_NETWORK_TCP_UNCONNECTED) return FALSE; - sprintf(globalScoreInfo, " %u OF %u SCORES", networkGlobals->setHighScoreResponse.position, networkGlobals->setHighScoreResponse.numberOfScores); - for (cycleCount = strlen(globalScoreInfo); cycleCount < sizeof(globalScoreInfo); cycleCount++) { - globalScoreInfo[cycleCount] = ' '; - } - globalScoreInfo[25] = '\0'; // TODO - Get rid of hard coded value... - displayScorePosition(); - - for (cycleCount = 4 * 60; cycleCount > 0; cycleCount--) { - networkGlobals->initParams.waitForVbl(); - } + if (networkGlobals->initParams.scorePosition != NULL) + networkGlobals->initParams.scorePosition(networkGlobals->setHighScoreResponse.position, + networkGlobals->setHighScoreResponse.numberOfScores); return TRUE; } diff --git a/BuGS/globalScores.h b/BuGS/globalScores.h index a3d89f2..f7b2989 100644 --- a/BuGS/globalScores.h +++ b/BuGS/globalScores.h @@ -12,6 +12,7 @@ #include + typedef struct tHighScore { char scoreText[10]; @@ -19,31 +20,100 @@ typedef struct tHighScore unsigned long score; } tHighScore; -extern char globalScoreInfo[26]; /* TODO - Get rid of this global and the hard coded length */ typedef struct tHighScoreInitParams { + /* This structure is initialized by the game and passed by pointer to the high score code. It consists of + a series of data members and a series of callbacks which are provided by the game. + */ + + /* This is the memory manager ID of the game. */ unsigned int userId; - const char * scoreServer; /* Pascal string of the hostname of the score server. */ + + /* This is a Pascal string (not a C string) of the hostname of the score server to connect to. */ + const char * scoreServer; + + /* This is the TCP port number of the score server to connect to. */ unsigned int scorePort; + + /* These two 32-bit values are the shared secrets used by the connect between the game and the score server + which is used to try to reduce the amount of game score hacking. + */ unsigned long secret1; unsigned long secret2; - void (*displayConnectionString)(void); /* This function should display a message to the user that the network is being brought up. */ - void (*waitForVbl)(void); /* This function should wait for the next VBL and is used to poll the network and limit upload time for a high score. */ - void (*uploadSpin)(int); /* This argument iterates over 0, 1, 2, 3 and then back to 0, 1, 2, etc and is intended to show some kind of spinner to the user - while uploading a high score. */ + + /* This function should display a message to the user that the network is being brought up and they should + be patient when the argument is TRUE and when the argument is FALSE, it should clear that message. This + function shouldn't block and just put something on the screen to say that the connection is being brought + up or clear that message. It is called sometimes (rarely) by pollNetwork(). It is called with argument + TRUE and will always be called with FALSE sometime after that. + */ + void (*displayConnectionString)(BOOLEAN display); + + /* This function should wait for the next VBL and is used to poll the network and limit upload time for a + high score. + */ + void (*waitForVbl)(void); + + /* This argument iterates over 0, 1, 2, 3 and then back to 0, 1, 2, etc and is intended to show some kind + of spinner to the user while uploading a high score to the server. This function shouldn't block for + any real amount of time and just cause something on the screen to change to make sure the player doesn't + think something has hung while the upload is in progress. It is called when sendHighScore() is called + by the game. + */ + void (*uploadSpin)(int); + + /* When a score is successfully uploaded to the server, this function will be called with the position + of this player's score among the total number of scores recorded for this game. This information + should be displayed to the user. The function can block while this information is being displayed + and that message should be cleaned up before the function returns to the caller. This function is + called by sendHighScore(). + */ + void (*scorePosition)(unsigned int position, unsigned int numberOfScores); } tHighScoreInitParams; -extern void initNetwork(tHighScoreInitParams * params); -extern void disconnectNetwork(void); -extern void pollNetwork(void); -extern void shutdownNetwork(void); -extern BOOLEAN canSendHighScore(void); -extern BOOLEAN sendHighScore(void); -// These are actually assembly functions called from the C code. -extern void displayScorePosition(void); +/* Call this function once at launch. The pointer to the parameters is copied so the structure does not need + to remain valid after the call to this function. + */ +extern void initNetwork(tHighScoreInitParams * params); + + +/* Call this when a game is about to start. It will interrupt any network operation in progress and get ready for + a quiet period where polling will stop until the game is over. That way, all CPU time can be focused on the game. + This function may call the waitForVbl() callback. + */ +extern void disconnectNetwork(void); + + +/* Call this every frame refresh period when a game is _not_ in progress. This does any network operations required + to download high scores. During this function call, the displayConnectionString() callback may be called. + */ +extern void pollNetwork(void); + + +/* Call this function once when the game is quitting. */ +extern void shutdownNetwork(void); + + +/* Call this function when the player has a high score that should be recorded online. This function will return + TRUE if the network is up and a high score can be sent and if so, the game should call sendHighScore() to send + the high score. If FALSE is returned, then the user is playing while offline and no attempt should be made to + send the high score. + */ +extern BOOLEAN canSendHighScore(void); + + +/* Assuming canSendHighScore() returned TRUE, the game can call this function to actually try to send the high score + to the server. If this function returns TRUE, then the score was successfully sent to the server. If FALSE + is returned, then an error has occurred. The game can offer the user the option to retry to the upload of the + score and if the user would like to retry, just call sendHighScore() again. During this function call, the + waitForVbl(), uploadSpin() and scorePosition() callbacks may be called. + + TODO - Pass the score as an argument rather than through globals. + */ +extern BOOLEAN sendHighScore(void); #endif /* define _GUARD_PROJECTBuGS_FILEglobalScores_ */ diff --git a/BuGS/main.c b/BuGS/main.c index 8475d34..f4d6bee 100644 --- a/BuGS/main.c +++ b/BuGS/main.c @@ -31,6 +31,9 @@ unsigned int myUserId; unsigned int randomSeed; +// This symbol is used also from assembly directly so be careful with it... +char globalScoreInfo[GAME_NUM_TILES_WIDE + 1]; + /* Implementation */ @@ -66,6 +69,30 @@ void uploadSpin(int val) } +void scorePosition(unsigned int position, unsigned int numberOfScores) +{ + int i; + + sprintf(globalScoreInfo, " %u OF %u SCORES", position, numberOfScores); + for (i = strlen(globalScoreInfo); i < sizeof(globalScoreInfo); i++) { + globalScoreInfo[i] = ' '; + } + globalScoreInfo[GAME_NUM_TILES_WIDE] = '\0'; + displayScorePosition(); + + for (i = 6 * 60; i > 0; i--) { + waitForVbl(); + } +} + + +void showConnectionString(BOOLEAN display) +{ + if (display) + displayConnectionString(); +} + + int main(void) { static tHighScoreInitParams highScoreInitParams; @@ -102,9 +129,10 @@ int main(void) highScoreInitParams.secret1 = NETWORK_SERVERSECRET1; highScoreInitParams.secret2 = NETWORK_SERVERSECRET2; - highScoreInitParams.displayConnectionString = displayConnectionString; + highScoreInitParams.displayConnectionString = showConnectionString; highScoreInitParams.waitForVbl = waitForVbl; highScoreInitParams.uploadSpin = uploadSpin; + highScoreInitParams.scorePosition = scorePosition; initNetwork(&highScoreInitParams);