/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 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. Warzone 2100 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 Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * MultiOpt.c * * Alex Lee,97/98, Pumpkin Studios * * Routines for setting the game options and starting the init process. */ #include "lib/framework/frame.h" // for everything #include "map.h" #include "game.h" // for loading maps #include "message.h" // for clearing messages. #include "main.h" #include "display3d.h" // for changing the viewpoint #include "power.h" #include "lib/widget/widget.h" #include "lib/gamelib/gtime.h" #include "lib/netplay/netplay.h" #include "hci.h" #include "configuration.h" // lobby cfg. #include "clparse.h" #include "lib/ivis_common/piestate.h" #include "component.h" #include "console.h" #include "multiplay.h" #include "lib/sound/audio.h" #include "multijoin.h" #include "frontend.h" #include "levels.h" #include "multistat.h" #include "multiint.h" #include "multilimit.h" #include "multigifts.h" #include "aiexperience.h" //for beacon messages #include "multiint.h" #include "multirecv.h" // //////////////////////////////////////////////////////////////////////////// // External Variables extern char buildTime[8]; // //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // send complete game info set! // dpid == 0 for no new players. void sendOptions(uint32_t dest, uint32_t play) { int i, j; NETbeginEncode(NET_OPTIONS, NET_ALL_PLAYERS); // First send information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETstring(game.version, 8); NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETbool(&game.fog); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Now the dpid array for (i = 0; i < MAX_PLAYERS; i++) { // We should probably re-define player2dpid as a uint8_t NETint32_t(&player2dpid[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } // Send dest and play NETuint32_t(&dest); NETuint32_t(&play); // Send over the list of player colours for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&PlayerColour[i]); } // Same goes for the alliances for (i = 0; i < MAX_PLAYERS; i++) { for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } // Send their team (this is apparently not related to alliances for (i = 0; i < MAX_PLAYERS; i++) { // This should also be a uint8_t NETint32_t(&playerTeamGUI[i]); } // Send the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); // Send the structures changed for (i = 0; i < ingame.numStructureLimits; i++) { NETuint8_t(&ingame.pStructureLimits[i].id); NETuint8_t(&ingame.pStructureLimits[i].limit); } // Send ready status of all players for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&bPlayerReadyGUI[i]); } NETend(); } // //////////////////////////////////////////////////////////////////////////// /*! * check the wdg files that are being used. */ static BOOL checkGameWdg(const char *nm) { LEVEL_DATASET *lev; for (lev = psLevels; lev; lev = lev->psNext) { if (strcmp(lev->pName, nm) == 0) { return true; } } return false; } // //////////////////////////////////////////////////////////////////////////// // options for a game. (usually recvd in frontend) void recvOptions() { int i, j; UDWORD play; UDWORD newPl; NETbeginDecode(NET_OPTIONS); // Get general information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETstring(game.version, 8); NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETbool(&game.fog); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Check the version if (strncmp(game.version, buildTime, 8) != 0) { debug(LOG_ERROR, "Host is running a different version of Warzone2100."); } // Now the dpid array for (i = 0; i < MAX_PLAYERS; i++) { NETint32_t(&player2dpid[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } NETuint32_t(&newPl); NETuint32_t(&play); // Player colours for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&PlayerColour[i]); } // Alliances for (i = 0; i < MAX_PLAYERS; i++) { for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } // Teams for (i = 0; i < MAX_PLAYERS; i++) { NETint32_t(&playerTeamGUI[i]); } // Free any structure limits we may have in-place if (ingame.numStructureLimits) { ingame.numStructureLimits = 0; free(ingame.pStructureLimits); ingame.pStructureLimits = NULL; } // Get the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); // If there were any changes allocate memory for them if (ingame.numStructureLimits) { ingame.pStructureLimits = malloc(ingame.numStructureLimits * sizeof(MULTISTRUCTLIMITS)); } for (i = 0; i < ingame.numStructureLimits; i++) { NETuint8_t(&ingame.pStructureLimits[i].id); NETuint8_t(&ingame.pStructureLimits[i].limit); } // Receive ready status of all players for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&bPlayerReadyGUI[i]); } NETend(); // Post process if (newPl != 0) { // If we are the new player if (newPl == NetPlay.dpidPlayer) { selectedPlayer = play; // Select player NETplayerInfo(); // Get player info powerCalculated = false; // Turn off any power requirements } // Someone else is joining. else { setupNewPlayer(newPl, play); } } // Do the skirmish slider settings if they are up for (i = 0; i < MAX_PLAYERS; i++) { if (widgGetFromID(psWScreen, MULTIOP_SKSLIDE + i)) { widgSetSliderPos(psWScreen, MULTIOP_SKSLIDE + i, game.skDiff[i]); } } // See if we have the map or not if (!checkGameWdg(game.map)) { // Request the map from the host NETbeginEncode(NET_REQUESTMAP, NET_ALL_PLAYERS); NETuint32_t(&NetPlay.dpidPlayer); NETend(); addConsoleMessage("MAP REQUESTED!",DEFAULT_JUSTIFY, SYSTEM_MESSAGE); } else { loadMapPreview(); } } // //////////////////////////////////////////////////////////////////////////// // Host Campaign. BOOL hostCampaign(char *sGame, char *sPlayer) { PLAYERSTATS playerStats; UDWORD pl,numpl,i,j; debug(LOG_WZ, "Hosting campaign: '%s', player: '%s'", sGame, sPlayer); freeMessages(); // If we had a single player (i.e. campaign) game before this value will // have been set to 0. So revert back to the default value. if (game.maxPlayers == 0) { game.maxPlayers = 4; } NEThostGame(sGame, sPlayer, game.type, 0, 0, 0, game.maxPlayers); // temporary stuff for (i = 0; i < MAX_PLAYERS; i++) { player2dpid[i] = 0; setPlayerName(i, ""); //Clear custom names (use default ones instead) } pl = rand()%game.maxPlayers; //pick a player player2dpid[pl] = NetPlay.dpidPlayer; // add ourselves to the array. selectedPlayer = pl; ingame.localJoiningInProgress = true; ingame.JoiningInProgress[selectedPlayer] = true; bMultiPlayer = true; // enable messages loadMultiStats(sPlayer,&playerStats); // stats stuff setMultiStats(NetPlay.dpidPlayer,playerStats,false); setMultiStats(NetPlay.dpidPlayer,playerStats,true); bPlayerReadyGUI[0] = false; if(!NetPlay.bComms) { NETplayerInfo(); strcpy(NetPlay.players[0].name,sPlayer); numpl = 1; } else { numpl = NETplayerInfo(); } // may be more than one player already. check and resolve! if(numpl >1) { for(j = 0;jpName = NULL; if (psNew->pName) { psTempl->pName = strdup(psNew->pName); } psTempl->psNext = apsDroidTemplates[player]; apsDroidTemplates[player] = psTempl; return true; } BOOL addTemplateSet(UDWORD from,UDWORD to) { DROID_TEMPLATE *psCurr; if(from == to) { return true; } for(psCurr = apsDroidTemplates[from];psCurr;psCurr= psCurr->psNext) { addTemplate(to, psCurr); } return true; } BOOL copyTemplateSet(UDWORD from,UDWORD to) { DROID_TEMPLATE *psTempl; if(from == to) { return true; } while(apsDroidTemplates[to]) // clear the old template out. { psTempl = apsDroidTemplates[to]->psNext; free(apsDroidTemplates[to]); apsDroidTemplates[to] = psTempl; } return addTemplateSet(from,to); } // //////////////////////////////////////////////////////////////////////////// // setup templates BOOL multiTemplateSetup(void) { UDWORD player, pcPlayer = 0; switch (game.type) { case CAMPAIGN: for(player=0;playerpsNext; //if(psD->droidType != DROID_CONSTRUCT) if (!(psD->droidType == DROID_CONSTRUCT || psD->droidType == DROID_CYBORG_CONSTRUCT)) { killDroid(psD); } psD = psD2; } break; case CAMP_BASE: //just structs, no walls psStruct = apsStructLists[player]; while(psStruct) { if ( (psStruct->pStructureType->type == REF_WALL) ||(psStruct->pStructureType->type == REF_WALLCORNER) ||(psStruct->pStructureType->type == REF_DEFENSE) ||(psStruct->pStructureType->type == REF_BLASTDOOR) ||(psStruct->pStructureType->type == REF_CYBORG_FACTORY) ||(psStruct->pStructureType->type == REF_COMMAND_CONTROL) ) { removeStruct(psStruct, true); psStruct= apsStructLists[player]; //restart,(list may have changed). } else if( (psStruct->pStructureType->type == REF_FACTORY) ||(psStruct->pStructureType->type == REF_RESEARCH) ||(psStruct->pStructureType->type == REF_POWER_GEN)) { if(psStruct->pStructureType->type == REF_FACTORY ) { if(firstFact == true) { firstFact = false; removeStruct(psStruct, true); psStruct= apsStructLists[player]; } else // don't delete, just rejig! { if(((FACTORY*)psStruct->pFunctionality)->capacity != 0) { ((FACTORY*)psStruct->pFunctionality)->capacity = 0; ((FACTORY*)psStruct->pFunctionality)->productionOutput = (UBYTE)((PRODUCTION_FUNCTION*)psStruct->pStructureType->asFuncList[0])->productionOutput; psStruct->sDisplay.imd = psStruct->pStructureType->pIMD; psStruct->body = (UWORD)(structureBody(psStruct)); } psStruct = psStruct->psNext; } } else if(psStruct->pStructureType->type == REF_RESEARCH) { if(firstRes == true) { firstRes = false; removeStruct(psStruct, true); psStruct= apsStructLists[player]; } else { if(((RESEARCH_FACILITY*)psStruct->pFunctionality)->capacity != 0) { // downgrade research ((RESEARCH_FACILITY*)psStruct->pFunctionality)->capacity = 0; ((RESEARCH_FACILITY*)psStruct->pFunctionality)->researchPoints = ((RESEARCH_FUNCTION*)psStruct->pStructureType->asFuncList[0])->researchPoints; psStruct->sDisplay.imd = psStruct->pStructureType->pIMD; psStruct->body = (UWORD)(structureBody(psStruct)); } psStruct=psStruct->psNext; } } else if(psStruct->pStructureType->type == REF_POWER_GEN) { if(((POWER_GEN*)psStruct->pFunctionality)->capacity != 0) { // downgrade powergen. ((POWER_GEN*)psStruct->pFunctionality)->capacity = 0; ((POWER_GEN*)psStruct->pFunctionality)->power = ((POWER_GEN_FUNCTION*)psStruct->pStructureType->asFuncList[0])->powerOutput; ((POWER_GEN*)psStruct->pFunctionality)->multiplier += ((POWER_GEN_FUNCTION*)psStruct->pStructureType->asFuncList[0])->powerMultiplier; psStruct->sDisplay.imd = psStruct->pStructureType->pIMD; psStruct->body = (UWORD)(structureBody(psStruct)); } psStruct=psStruct->psNext; } } else { psStruct=psStruct->psNext; } } break; case CAMP_WALLS: //everything. break; default: debug( LOG_ERROR, "Unknown Campaign Style" ); abort(); break; } // rerev list to get back to normal. // reverseObjectList((BASE_OBJECT**)&apsStructLists[player]); bMultiPlayer = true; return true; } // //////////////////////////////////////////////////////////////////////////// // setup a campaign game static BOOL campInit(void) { UDWORD player; UBYTE newPlayerArray[MAX_PLAYERS]; UDWORD i,j,lastAI; SDWORD newPlayerTeam[MAX_PLAYERS] = {-1,-1,-1,-1,-1,-1,-1,-1}; // if this is from a savegame, stop here! if((getSaveGameType() == GTYPE_SAVE_START) || (getSaveGameType() == GTYPE_SAVE_MIDMISSION) ) { // these two lines are the biggest hack in the world. // the reticule seems to get detached from 'reticuleup' // this forces it back in sync... intRemoveReticule(); intAddReticule(); return true; } //Convert skirmish GUI player ids to in-game ids if(game.type == SKIRMISH) { lastAI = 0; //last used AI slot memset(newPlayerArray,1,MAX_PLAYERS * sizeof(newPlayerArray[0])); //'1' for humans for(i=0;i 7) || game.type == SKIRMISH) { for(player=game.maxPlayers;player