gamestate.c

Go to the documentation of this file.
00001 
00025 #include "states.h"
00026 #include "game.h"
00027 #include "color.h"
00028 #include "gameconfig.h"
00029 #include "obstacle.h"
00030 #include "playernet.h"
00031 #include "tanksprite.h"
00032 #include "name.h"
00033 #include "net.h"
00034 #include "notice.h"
00035 #include "menustate.h"
00036 #include <assert.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #ifdef USE_SDLIMAGE
00040 #include <SDL_image.h>
00041 #endif
00042 #ifdef USE_AUDIO
00043 #include "audio.h"
00044 #endif
00045 
00051 Uint8 eventState[EC_MAX];
00052 
00053 extern Uint32 frameTime;
00054 
00055 RenderItem background;
00056 
00057 static Sint16 scores[2];
00058 
00062 #define BORDER_WIDTH  4
00063 
00067 static RenderItem border[4];
00068 
00077 static void RenderScore(Bool forceRender) {
00078     // for now, support only two teams
00079     int loop;
00080     for (loop = 0; loop < 2; loop++) {
00081         // has this team's score changed?
00082         if (forceRender ||
00083         (scores[loop] != gameOpts.teamScore[TEAM_RED + loop])) {
00084             // record the new displayed score
00085             scores[loop] = gameOpts.teamScore[TEAM_RED + loop];
00086             // render the text
00087             TextRenderString(&textItems[TEXTITEM_REDSCORE + loop], FONT_SCORE,
00088             0, colorFmtInd[COLOR_RED + loop],
00089             #ifdef USE_GETTEXT
00090             L"%d",
00091             #else
00092             "%d",
00093             #endif
00094             scores[loop]);
00095             // place the score on screen
00096             textItems[TEXTITEM_REDSCORE + loop].loc.y = 5;
00097             if (loop) {
00098                 textItems[TEXTITEM_BLUESCORE].loc.x =
00099                 dInfo.pw - textItems[TEXTITEM_BLUESCORE].loc.w - 5;
00100             }
00101             else {
00102                 textItems[TEXTITEM_REDSCORE].loc.x = 5;
00103             }
00104             PlaceRenderItem(&textItems[TEXTITEM_REDSCORE + loop]);
00105         }
00106     }
00107 }
00108 
00116 static void SetupScrolling() {
00117     // check for a field that can fit on screen
00118     if ((dInfo.lw >= gameOpts.width) && (dInfo.lh >= gameOpts.height)) {
00119         // it fits; no scrolling
00120         dInfo.scrollField = FALSE;
00121         // reset layer offsets
00122         SetLayerOffset(&(layers[LAYER_OBSTACLES]), 0, 0);
00123         SetLayerOffset(&(layers[LAYER_NAMES]), 0, 0);
00124         SetLayerOffset(&(layers[LAYER_TANKS]), 0, 0);
00125     }
00126     else {
00127         // it doesn't fit; use scrolling
00128         dInfo.scrollField = TRUE;
00129         // no need to set layer offsets -- this must be done every frame
00130     }
00131 }
00132 
00140 static Bool GraphicReset() {
00141     // assure everything is rendered
00142     ForceRenderAll();
00143     #ifdef USE_SDLIMAGE
00144     // load an image
00145     if ((back = IMG_Load("back.jpg")) == NULL) {
00146     #endif
00147         // put the solid color backgound in place
00148         background.loc.w = dInfo.pw;
00149         background.loc.h = dInfo.ph;
00150         background.renderer = SolidFillRenderer;
00151         background.data = &(colorVals[COLOR_REGBACK]);
00152     #ifdef USE_SDLIMAGE
00153     }
00154     else {
00155         SDL_Surface *back;
00156         // Convert the image to the same format the display is using to
00157         // improve performance. Use a software surface if there is no hardware
00158         // acceleration because that will be faster.
00159         if ((background.data = SDL_ConvertSurface(back, dInfo.display->format,
00160         FASTEST_SURFACE)) == NULL) {
00161             // couldn't do it, so keep the loaded surface
00162             background.data = back;
00163         }
00164         else {
00165             // get rid of the less desirable surface
00166             SDL_FreeSurface(back);
00167         }
00168         // put the backgound image in place
00169         background.loc.w = ((SDL_Surface*)(background.data))->w;
00170         background.loc.h = ((SDL_Surface*)(background.data))->h;
00171         background.renderer = GenericSurfaceRenderer;
00172     }
00173     #endif
00174     // assure good color values
00175     SetupColorVals();
00176     // set the text color
00177     colorFmtInd[COLOR_TEXT] = colorFmtInd[COLOR_WHITE];
00178     // initalize the text system
00179     TextSetupStatePlay();
00180     // place the score
00181     RenderScore(TRUE);
00182     SetupScrolling();
00183     return TRUE;
00184 }
00185 
00186 int GameInit() {
00187     Player *p;
00188     int loop;
00189     // initialize global data
00190     memset(eventState, 0, EC_MAX);
00191     memset(&background, 0, sizeof(background));
00192     // setup position of screen parts
00193     dInfo.luw = dInfo.luh = 3;
00194     dInfo.lw = dInfo.pw / dInfo.luw;
00195     dInfo.lh = dInfo.ph / dInfo.luh;
00196     // flag the need to render the game
00197     dInfo.showGame = TRUE;
00198     // initialize the score
00199     textItems[TEXTITEM_REDSCORE].renderer =
00200     textItems[TEXTITEM_BLUESCORE].renderer = GenericSurfaceRenderer;
00201     AddRenderItem(&textItems[TEXTITEM_REDSCORE], LAYER_TEXT);
00202     AddRenderItem(&textItems[TEXTITEM_BLUESCORE], LAYER_TEXT);
00203     // run through stuff that may need to be run again later
00204     if (!GraphicReset()) return STATE_ERROR;
00205     
00206     if (networkState == NETSTATE_SERVER) {
00207         // ---- test code ----
00208         // load up a battlefield
00209         if (!InitGameConfig("field.bfld")) {
00210             TextUnsetState();
00211             // change to the state that displays the error
00212             return STATE_BFLDERR;
00213         }
00214         // advance the target time of the next frame
00215         frameTime += SRV_FRAME_OFFSET;
00216         
00217         // ---- not so testy ----
00218         // initialize the first player
00219         localPlayer = 0;
00220     }
00221     // configure scrolling
00222     SetupScrolling();
00223     // if scrolling is needed . . .
00224     if (dInfo.scrollField) {
00225         // put view in the center of the field.
00226         // calculate the offsets
00227         int x = -(gameOpts.width >> 1) * dInfo.luw + (dInfo.pw >> 1);
00228         int y = -(gameOpts.height >> 1) * dInfo.luh + (dInfo.ph >> 1);
00229         // move the layers to match
00230         SetLayerOffset(&(layers[LAYER_OBSTACLES]), x, y);
00231         SetLayerOffset(&(layers[LAYER_NAMES]), x, y);
00232         SetLayerOffset(&(layers[LAYER_TANKS]), x, y);
00233     }
00234     // setup obstacles
00235     ObstacleInit();
00236     // put in the background
00237     AddRenderItem(&background, LAYER_BACKGROUND);
00238     PlaceRenderItem(&background);
00239     
00240     // setup local player data
00241     players[localPlayer].pid = localPlayer;
00242     players[localPlayer].flags |= PLAYER_ACTIVE | PLAYER_UPDATE_IDENT |
00243     PLAYER_DEAD;
00244     players[localPlayer].team = menuSettings->team;
00245     //players[localPlayer].deadTime = DEAD_TIME + 1;
00246     players[localPlayer].x = 1 << 8;
00247     players[localPlayer].y = (12 * localPlayer) << 8;
00248     // grab the local player's name
00249     GetLocalPlayerName(players[localPlayer].name);
00250     // add the player to the game
00251     AddPlayer(&players[localPlayer]);
00252     // update times
00253     //players[localPlayer].localUpdate++;
00254 
00255     if (networkState != NETSTATE_SERVER) {
00256         MsgDescr *msg;
00257         // assemble a message to inform the server about this client's ident
00258         if ((msg = AppendPlayerData(NULL, &players[localPlayer], 1)) == NULL) {
00259             // failed to build the message
00260             return STATE_ERROR;
00261         }
00262         // add the message to the system
00263         if (!AddMessage(msg)) {
00264             // failed to send the message
00265             return STATE_ERROR;
00266         }
00267     }
00268     // grab the current time
00269     frameTime = SDL_GetTicks();
00270     // record now as the time when all players are updated
00271     
00272     // fixup all player positions and sizes
00273     for (p = players, loop = 0; loop < MAX_PLAYERS; loop++, p++) {
00274         // only need to adjust active players
00275         if (p->flags & PLAYER_ACTIVE) {
00276             p->localUpdate = frameTime;
00277             p->tank.loc.x = (p->x >> 8) * dInfo.luw;
00278             p->tank.loc.y = (p->y >> 8) * dInfo.luh;
00279             p->tank.loc.w = dInfo.luw * 8;
00280             p->tank.loc.h = dInfo.luh * 8;
00281             //p->tank.renderer = DrawTank;
00282             p->shell.loc.w = dInfo.luw;
00283             p->shell.loc.h = dInfo.luh;
00284             p->shell.data = &(colorVals[p->team]);
00285             
00286             if (!(p->flags & PLAYER_DEAD)) {
00287                 assert(p->pid == loop);
00288                 //QueuePlaceTank(loop);
00289                 //QueuePlaceName(loop);
00290                 PlaceRenderItem(&(p->tank));
00291                 PlacePlayerLabel(p);
00292                 if (p->flags & PLAYER_SHOT) {
00293                     PlaceRenderItem(&(p->shell));
00294                 }
00295             }
00296         }
00297     }
00298     // eliminate the menu settings
00299     free(menuSettings);
00300     menuSettings = NULL;
00301 
00302     // create a border around the battlefield
00303     border[0].renderer = border[1].renderer = border[2].renderer =
00304     border[3].renderer = SolidFillRenderer;
00305     border[0].data = border[1].data = border[2].data = border[3].data =
00306     &(colorVals[COLOR_BLACK]);
00307     // top
00308     border[0].loc.x = -BORDER_WIDTH * dInfo.luw;
00309     border[0].loc.y = -BORDER_WIDTH * dInfo.luh;
00310     border[0].loc.w = (gameOpts.width + (BORDER_WIDTH << 1)) * dInfo.luw;
00311     border[0].loc.h = BORDER_WIDTH * dInfo.luh;
00312     // bottom
00313     border[1].loc.x = -BORDER_WIDTH * dInfo.luw;
00314     border[1].loc.y = gameOpts.height * dInfo.luh;
00315     border[1].loc.w = (gameOpts.width + (BORDER_WIDTH << 1)) * dInfo.luw;
00316     border[1].loc.h = BORDER_WIDTH * dInfo.luh;
00317     // left
00318     border[2].loc.x = -BORDER_WIDTH * dInfo.luw;
00319     border[2].loc.y = 0;
00320     border[2].loc.w = BORDER_WIDTH * dInfo.luw;
00321     border[2].loc.h = gameOpts.height * dInfo.luh;
00322     // right
00323     border[3].loc.x = gameOpts.width * dInfo.luw;
00324     border[3].loc.y = 0;
00325     border[3].loc.w = BORDER_WIDTH * dInfo.luw;
00326     border[3].loc.h = gameOpts.height * dInfo.luh;
00327     // give them to the renderer
00328     AddRenderItem(&border[0], LAYER_OBSTACLES);
00329     AddRenderItem(&border[1], LAYER_OBSTACLES);
00330     AddRenderItem(&border[2], LAYER_OBSTACLES);
00331     AddRenderItem(&border[3], LAYER_OBSTACLES);
00332     PlaceRenderItem(&border[0]);
00333     PlaceRenderItem(&border[1]);
00334     PlaceRenderItem(&border[2]);
00335     PlaceRenderItem(&border[3]);
00336 
00337     // success
00338     return STATE_OK;
00339 }
00340 
00341 int GameUninit() {
00342     int loop;
00343     Player *p;
00344     #ifdef USE_AUDIO
00345     // stop all sounds
00346     StopAllSounds();
00347     #endif
00348     // loop through players to eliminate thier dynamic data
00349     for (p = players, loop = MAX_PLAYERS; loop > 0; loop--, p++) {
00350         RemovePlayer(p);
00351     }
00352     UninitGameConfig();
00353     // reset layer offsets to zero
00354     SetupScrolling();
00355     TextUnsetState();
00356     // see if a background image, rather than a color, was used
00357     if (background.renderer == GenericSurfaceRenderer) {
00358         // remove the background image
00359         SDL_FreeSurface(background.data);
00360     }
00361     RemoveRenderItem(&background);
00362     // take out the border
00363     RemoveRenderItem(&border[0]);
00364     RemoveRenderItem(&border[1]);
00365     RemoveRenderItem(&border[2]);
00366     RemoveRenderItem(&border[3]);
00367     // notices
00368     RemoveAllNotices();
00369     // re-render whole next frame
00370     ForceRenderAll();
00371     // remove all surfaces from the overlays
00373     // clear the game render flag
00374     dInfo.showGame = FALSE;
00375     // reset the target time for the next frame to remove the difference
00376     // between client and server
00377     loop = SDL_GetTicks();
00378     frameTime = loop - (loop % FRAME_DURATION) + FRAME_DURATION;
00379     return STATE_OK;
00380 }
00381 
00382 int GameRun() {
00383     static Bool enableLables = FALSE;
00384     static int animTest = 0;
00385     SDL_Event event;
00386     
00387     // handle a change in the network state
00388     if ((networkState != NETSTATE_CLIENT)
00389     && (networkState != NETSTATE_SERVER)) {
00390         // there is no longer a running game, so go back to the menu
00391         return STATE_MENUROOT;
00392     }
00393     // loop through all waiting events
00394     while (SDL_PollEvent(&event)) {
00395         // figure event type
00396         switch (event.type) {
00397             case SDL_QUIT:
00398                 return STATE_QUIT;
00399                 break;
00400             case SDL_ACTIVEEVENT:
00401                 // some strange focus thing
00402                 /*
00403                 if (event.active.gain == 0)
00404                     eventState[EC_INACTIVE] = TRUE;
00405                 else
00406                     eventState[EC_INACTIVE] = FALSE;
00407                 */
00408                 break;
00409             case SDL_KEYDOWN:
00410                 // handle key presses
00411                 switch (event.key.keysym.sym) {
00412                     case SDLK_ESCAPE:
00413                         return STATE_QUIT;
00414                         break;
00415                     case SDLK_UP:
00416                     case SDLK_w:
00417                         eventState[EC_FORWARD] |= 1;
00418                         break;
00419                     case SDLK_DOWN:
00420                     case SDLK_s:
00421                         eventState[EC_BACKWARD] |= 1;
00422                         break;
00423                     case SDLK_LEFT:
00424                     case SDLK_a:
00425                         eventState[EC_ROTLEFT] |= 1;
00426                         break;
00427                     case SDLK_RIGHT:
00428                     case SDLK_d:
00429                         eventState[EC_ROTRIGHT] |= 1;
00430                         break;
00431                     case SDLK_RETURN:
00432                     case SDLK_SPACE:
00433                         eventState[EC_FIRE] |= 1;
00434                         break;
00435                     // case SDLK_l:
00436                     //   use for score listing display toggle
00437                     case SDLK_n:
00438                         if (dInfo.showLabels) {
00439                             DisablePlayerLabels();
00440                         }
00441                         else {
00442                             enableLables = TRUE;
00443                             dInfo.showLabels = TRUE;
00444                         }
00445                         break;
00446                     case SDLK_KP_PLUS:
00447                         animTest++;
00448                         break;
00449                     case SDLK_KP_MINUS:
00450                         animTest--;
00451                         break;
00452                     case SDLK_f:
00453                         #ifndef WIN32
00454                         if (!eventState[EC_FS_TOGGLE]) {
00455                             SDL_WM_ToggleFullScreen(dInfo.display);
00456                             ForceRenderAll();
00457                             RenderScore(TRUE);
00458                             SetupScrolling();
00459                         }
00460                         #else
00461                         // ???
00462                         // will need to call GraphicReset()
00463                         //GraphicReset();
00464                         #endif
00465                     default:
00466                         break;
00467                 }
00468                 break;
00469             case SDL_KEYUP:
00470                 switch (event.key.keysym.sym) {
00471                     case SDLK_UP:
00472                     case SDLK_w:
00473                         eventState[EC_FORWARD] = 0;
00474                         break;
00475                     case SDLK_DOWN:
00476                     case SDLK_s:
00477                         eventState[EC_BACKWARD] = 0;
00478                         break;
00479                     case SDLK_LEFT:
00480                     case SDLK_a:
00481                         eventState[EC_ROTLEFT] = 0;
00482                         break;
00483                     case SDLK_RIGHT:
00484                     case SDLK_d:
00485                         eventState[EC_ROTRIGHT] = 0;
00486                         break;
00487                     case SDLK_RETURN:
00488                     case SDLK_SPACE:
00489                         eventState[EC_FIRE] = 0;
00490                         break;
00491                     case SDLK_c:
00492                         ClearRenderItem(&(players[0].tank));
00493                         break;
00494                     case SDLK_v:
00495                         PlaceRenderItem(&(players[0].tank));
00496                         break;
00497                     case SDLK_r:
00498                         ForceRenderAll();
00499                     default:
00500                         break;
00501                 }
00502                 break;
00503             case SDL_JOYAXISMOTION:
00504                 if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) break;
00505                 // x-axis
00506                 if (event.jaxis.axis == 0) {
00507                     // right
00508                     if (event.jaxis.value > 16384) {
00509                         eventState[EC_ROTRIGHT] |= 1;
00510                         eventState[EC_ROTLEFT] = 0;
00511                     }
00512                     // left
00513                     else if (event.jaxis.value < -16384) {
00514                         eventState[EC_ROTLEFT] |= 1;
00515                         eventState[EC_ROTRIGHT] = 0;
00516                     }
00517                     // center
00518                     else {
00519                         eventState[EC_ROTLEFT] = 0;
00520                         eventState[EC_ROTRIGHT] = 0;
00521                     }
00522                 }
00523                 // y-axis
00524                 else if (event.jaxis.axis == 1) {
00525                     // down
00526                     if (event.jaxis.value > 16384) {
00527                         eventState[EC_BACKWARD] |= 1;
00528                         eventState[EC_FORWARD] = 0;
00529                     }
00530                     // up
00531                     else if (event.jaxis.value < -16384) {
00532                         eventState[EC_FORWARD] |= 1;
00533                         eventState[EC_BACKWARD] = 0;
00534                     }
00535                     // center
00536                     else {
00537                         eventState[EC_FORWARD] = 0;
00538                         eventState[EC_BACKWARD] = 0;
00539                     }
00540                 }
00541                 break;
00542             case SDL_JOYBUTTONDOWN:
00543                 if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) break;
00544                 eventState[EC_FIRE] |= 1;
00545                 break;
00546             case SDL_JOYBUTTONUP:
00547                 if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) break;
00548                 eventState[EC_FIRE] = 0;
00549                 break;
00550             case SDL_VIDEORESIZE:
00551                 ResizeWindow(event.resize.w, event.resize.h);
00552                 // for solid color backgrounds . . .
00553                 if (background.renderer == SolidFillRenderer) {
00554                     // match the size of the window
00555                     background.loc.w = dInfo.pw;
00556                     background.loc.h = dInfo.ph;
00557                 }
00558                 RenderScore(TRUE);
00559                 SetupScrolling();
00560                 // extra steps to configure to the new size
00561             default:
00562                 break;
00563         }
00564     }
00565     // respond to events
00566     HandleInput(eventState);
00567     // ----- run network code ----
00568     ProcessNetworkMessages();
00569     // additional to send data
00570     if ((networkState == NETSTATE_CLIENT) &&
00571     (players[localPlayer].flags & PLAYER_CLIENT_MASK)) {
00573         MsgDescr *msg;
00574         // assemble a message to inform the server about this client
00575         if ((msg = AppendPlayerData(NULL, &players[localPlayer], frameTime))
00576         == NULL) {
00577             // failed to build the message
00578             return STATE_ERROR;
00579         }
00580         // add the message to the system
00581         if (!AddMessage(msg)) {
00582             // failed to send the message
00583             return STATE_ERROR;
00584         }
00585         // clear the update flags
00586         players[localPlayer].flags &= ~PLAYER_UPDATE_ITEMS;
00587     }
00588     else if (networkState == NETSTATE_SERVER) {
00589         MsgDescr *msg = NULL;
00590         Player *p;
00591         int loop;
00592         // loop through the clients
00593         for (p = players, loop = 0; loop < MAX_PLAYERS; loop++, p++) {
00594             // only deal with active players and players requiring an update
00595             if (!(p->flags & PLAYER_ACTIVE) ||
00596             !(p->flags & PLAYER_UPDATE_ITEMS)) continue;
00597             // assemble a message to inform clients about this player
00598             if ((msg = AppendPlayerData(msg, p, frameTime)) == NULL) {
00599                 // failed to build the message
00600                 return STATE_ERROR;
00601             }
00602             // clear the update flags
00603             p->flags &= ~PLAYER_UPDATE_ITEMS; //~0x700;
00604         }
00605         // check for an unsent message
00606         if (msg) {
00607             AddMessage(msg);
00608         }
00609     }
00610     // score update?
00611     if (gameOpts.flags & OPT_UPDATESCORE) {
00612         // clear the update flag
00613         gameOpts.flags &= ~OPT_UPDATESCORE;
00614         //  render
00615         RenderScore(FALSE);
00616     }
00617     // scrollable field and living player?
00618     if (dInfo.scrollField && !(players[localPlayer].flags & PLAYER_DEAD)) {
00619         // compute the offsets needed to put the player in the middle
00620         int x = -(players[localPlayer].x >> 8) * dInfo.luw + (dInfo.pw >> 1);
00621         int y = -(players[localPlayer].y >> 8) * dInfo.luh + (dInfo.ph >> 1);
00622         // see if the offsets differ from the layer
00623         if ((x != layers[LAYER_OBSTACLES].offX) ||
00624         (y != layers[LAYER_OBSTACLES].offY)) {
00625             // move the layers to match the player's new position
00626             SetLayerOffset(&(layers[LAYER_OBSTACLES]), x, y);
00627             SetLayerOffset(&(layers[LAYER_NAMES]), x, y);
00628             SetLayerOffset(&(layers[LAYER_TANKS]), x, y);
00629         }
00630     }
00631     // show respawn message?
00632     if ((players[localPlayer].flags & PLAYER_DEAD) &&
00633     (players[localPlayer].deadTime > DEAD_TIME)) {
00634         if (!textItems[TEXTITEM_RESPAWN].enableDraw) {
00635             // set position to mid-screen
00636             textItems[TEXTITEM_RESPAWN].loc.x =
00637             (dInfo.pw - textItems[TEXTITEM_RESPAWN].loc.w) >> 1;
00638             textItems[TEXTITEM_RESPAWN].loc.y =
00639             (dInfo.ph - textItems[TEXTITEM_RESPAWN].loc.h) >> 1;
00640             // show the message
00641             PlaceRenderItem(&textItems[TEXTITEM_RESPAWN]);
00642         }
00643     }
00644     else if (textItems[TEXTITEM_RESPAWN].enableDraw) {
00645         ClearRenderItem(&textItems[TEXTITEM_RESPAWN]);
00646     }
00647     // if requested, re-enable player labels
00648     if (enableLables) {
00649         enableLables = FALSE;
00650         EnablePlayerLabels();
00651     }
00652     
00653     // handle notices
00654     UpdateNotices(frameTime);
00655         
00656     // stay in this state
00657     return STATE_PLAY;
00658 }
00659 

Generated on Mon May 28 04:41:39 2007 for Retro Tank Super Attack by  doxygen 1.5.2