/* 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 */ /* * MultiStat.c * * Alex Lee , pumpkin studios, EIDOS * * load / update / store multiplayer statistics for league tables etc... * Also handle the force save/loads and default teams for each tech level. */ #include #include #include "lib/framework/frame.h" #include "objmem.h" #include "power.h" #include "map.h" #include "lib/widget/widget.h" #include "text.h" #include "effects.h" // for discovery flash #include "lib/netplay/netplay.h" #include "cmddroid.h" #include "multiplay.h" #include "multirecv.h" #include "multistat.h" #include "multiint.h" #include "fpath.h" extern char MultiPlayersPath[255]; // //////////////////////////////////////////////////////////////////////////// // Force defs. BOOL removeFromForce (UDWORD number); // remove a droid from force BOOL addToForce (DROID_TEMPLATE *templ); // add a droid (templ) to force VOID useTheForce (BOOL bAddTempl); // place the force in the game BOOL saveForce (char *name,FORCE *pfForce); BOOL loadForce (char *name); FORCE Force; // the selected force. // //////////////////////////////////////////////////////////////////////////// // Player Stat defs. BOOL saveMultiStats (STRING *sFName, STRING *sPlayerName, PLAYERSTATS *plStats); BOOL loadMultiStats (STRING *sPlayerName, PLAYERSTATS *plStats); PLAYERSTATS getMultiStats (UDWORD player, BOOL bLocal); BOOL setMultiStats (DWORD playerDPID, PLAYERSTATS plStats,BOOL bLocal); VOID updateMultiStatsDamage (UDWORD attacker, UDWORD defender, UDWORD inflicted); VOID updateMultiStatsGames (void); VOID updateMultiStatsWins (void); VOID updateMultiStatsLoses (void); VOID updateMultiStatsKills (BASE_OBJECT *psKilled, UDWORD player); // //////////////////////////////////////////////////////////////////////////// // FORCE SELECT STUFF // //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // funcs to edit the force. BOOL addToForce(DROID_TEMPLATE *templ) { FORCE_MEMBER *pF; DROID_TEMPLATE *pT,*psTempl; if (!usePower(selectedPlayer, templ->powerPoints)) //subtract the power required to build { return FALSE; // go no further } // add template to list. if it doesn't exist. for( psTempl = Force.pForceTemplates; // find relevant template psTempl && (psTempl->ref != templ->ref); psTempl = psTempl->psNext); if(!psTempl) { pT = (DROID_TEMPLATE*)MALLOC(sizeof(DROID_TEMPLATE)); if ( !pT) { return FALSE; } memcpy(pT, templ, sizeof(DROID_TEMPLATE)); // calculate power etc for this template // initTemplatePoints(); pT->buildPoints = calcTemplateBuild(pT); pT->powerPoints = calcTemplatePower(pT); pT->psNext = Force.pForceTemplates; Force.pForceTemplates=pT; } else { pT = psTempl; // set up to point to existing template. } // add droid. pF = MALLOC(sizeof(FORCE_MEMBER)); // create a slot in the force. if (!pF) { return FALSE; } pF->pTempl = pT; // add this droid. pF->psNext = Force.pMembers; Force.pMembers = pF; return TRUE; } // //////////////////////////////////////////////////////////////////////////// BOOL removeFromForce(UDWORD number) { UDWORD i,templateid; FORCE_MEMBER *pF; FORCE_MEMBER *pF2=NULL; DROID_TEMPLATE *psCurr,*psPrev; BOOL inuse; pF= Force.pMembers; for(i=0;ipsNext; // goto that force element; addPower(selectedPlayer, pF->pTempl->powerPoints); // return that much power. if(number==0) // if first remove it, { pF = Force.pMembers; Force.pMembers = pF->psNext; } else { pF = Force.pMembers; // not first so linked list for(i=0;ipsNext; } pF2->psNext = pF->psNext; } templateid = pF->pTempl->ref; FREE(pF); // now check if template is still in use. inuse = FALSE; for(pF = Force.pMembers;pF;pF=pF->psNext) { if(pF->pTempl->ref == templateid) { inuse = TRUE; } } if(!inuse) // remove template, no longer needed. { psPrev = NULL; for(psCurr = Force.pForceTemplates; psCurr;psCurr = psCurr->psNext) { if( psCurr->ref == templateid ) { break; } psPrev = psCurr; } if (psCurr) // if we found itthen delete it. { if(psPrev) // Update list pointers. { psPrev->psNext = psCurr->psNext; // It's down the list somewhere } else { Force.pForceTemplates = psCurr->psNext; // It's at the root } FREE(psCurr); // Delete the template. } } return TRUE; } /* // find a place for the force VOID chooseForceLoc(UDWORD *pX,UDWORD *pY) { FEATURE *pFeat; UDWORD x,y,chose,tcount=0; // pick a boulder on the arena map for(pFeat=apsFeatureLists[0];pFeat;pFeat=pFeat->psNext)//count boulders { if(pFeat->psStats->subType == FEAT_BOULDER) { tcount++; } } if(tcount) // use boulders { chose = 0; while(chose == 0) // dont pick zeroth boulder. { chose = (rand()%(tcount+1)); } tcount= 0; pFeat=apsFeatureLists[0]; while(pFeat && (tcount!=chose)) { if(pFeat->psStats->subType == FEAT_BOULDER) { tcount++; } if(tcount != chose) { pFeat = pFeat->psNext; } } x = pFeat->x >>TILE_SHIFT; y = pFeat->y >>TILE_SHIFT; } else //dont use boulders { tcount = 0; x = rand()%mapWidth; //choose an intitial x/y pos. y = rand()%mapHeight; } // set result *pX = x; *pY = y; } */ // //////////////////////////////////////////////////////////////////////////// // place the force on the map. VOID useTheForce(BOOL bAddTempl)//Luke { DROID *pDr; DROID_TEMPLATE *pTempl; iVector position; UDWORD x1,y1,x,y; DROID_TEMPLATE *psTempl; // if(game.type == DMATCH) // { // chooseForceLoc(&x,&y); // } // if(game.type == CAMPAIGN) // { if(apsDroidLists[selectedPlayer]) { //set drop off point to pos of other droids. x = apsDroidLists[selectedPlayer]->x >>TILE_SHIFT; y = apsDroidLists[selectedPlayer]->y >>TILE_SHIFT; // send each of the extra templates used in the force for(psTempl=Force.pForceTemplates;psTempl;psTempl=psTempl->psNext) { sendTemplate(psTempl); // OUCH! REALLY COM HEAVY } } // } x1 = x; // now we have a coord, place droids y1 = y; while(Force.pMembers) // for each force member { x = x1; y = y1; if (!pickATileGen(&x,&y,LOOK_FOR_EMPTY_TILE,zonedPAT)) { ASSERT( FALSE, "UseTheForce: Unable to find a free location" ); } debug( LOG_NEVER, "force droid dropping at :%d,%d\n", x, y ); // copy template psTempl = NameToTemplate(Force.pMembers->pTempl->aName,selectedPlayer); if(!psTempl) { Force.pMembers->pTempl->ref = REF_TEMPLATE_START; addTemplate(selectedPlayer, Force.pMembers->pTempl); } // end of template copy pTempl = Force.pMembers->pTempl; /* if(!psTempl) // already exists. { if (HEAP_ALLOC(psTemplateHeap, &psTempl)) { memcpy(psTempl, Force.pMembers->pTempl, sizeof(DROID_TEMPLATE)); psTempl->ref = REF_TEMPLATE_START; // templates are the odd one out! if (apsDroidTemplates[selectedPlayer]) // Add it to the list { for(psCurr = apsDroidTemplates[selectedPlayer]; psCurr->psNext != NULL; psCurr = psCurr->psNext ); psCurr->psNext = psTempl; psTempl->psNext = NULL; } } } else { apsDroidTemplates[selectedPlayer]=psTempl; psTempl->psNext = NULL; } */ // if(usePower(selectedPlayer, pTempl->powerPoints)) // { pDr= buildDroid(pTempl, x<x; // Add an effect position.z = pDr->y; position.y = pDr->z; addEffect(&position,EFFECT_EXPLOSION,EXPLOSION_TYPE_DISCOVERY,FALSE,NULL,FALSE); } // } } } // //////////////////////////////////////////////////////////////////////////// // save out force to a file. // file format is as follows. // number of templates // number of droids in force // templates // droids BOOL saveForce(char *name,FORCE *pfForce) { STRING fileName[255]=""; PHYSFS_file *pFileHandle; DROID_TEMPLATE *pT; FORCE_MEMBER *pCount,*pMember; UDWORD count=0; strcpy(fileName,name); pFileHandle = PHYSFS_openWrite(fileName); // open the file if (!pFileHandle) { debug(LOG_ERROR, "saveForce: Couldn't open %s: %s", fileName, PHYSFS_getLastError()); return FALSE; } // save header for force file. count =0; for(pT = pfForce->pForceTemplates;pT;pT=pT->psNext) count++; // count templates if (PHYSFS_write( pFileHandle, &count, sizeof(UDWORD), 1 ) != 1) { debug(LOG_ERROR, "saveForce: Write failed for %s: %s", fileName, PHYSFS_getLastError()); return FALSE; } count=0; for(pCount=pfForce->pMembers;pCount;pCount=pCount->psNext) count++; // count droids if (PHYSFS_write( pFileHandle, &count, sizeof(UDWORD), 1 ) != 1) { debug(LOG_ERROR, "saveForce: Write failed for %s: %s", fileName, PHYSFS_getLastError()); return FALSE; } #if 0 // This code (and similar below for loadForce) can probably be removed. Commented out pre-GPL era. - Per // old method. save whole template for(pT = pfForce->pForceTemplates;pT;pT=pT->psNext) // save templates { if (fwrite(pT, sizeof(DROID_TEMPLATE), 1, pFileHandle) != 1) // template { debug( LOG_ERROR, "Write failed for %s", fileName ); abort(); return FALSE; } fputc(10,pFileHandle); //seperator. } // save force for(pMember = pfForce->pMembers;pMember;pMember =pMember->psNext) { if (fwrite(&(pMember->pTempl->ref) ,sizeof(pMember->pTempl->ref), 1, pFileHandle) != 1) { debug( LOG_ERROR, "Write failed for %s", fileName ); // force type abort(); return FALSE; } } #else // new method. refs to templates. USED FOR MULITLANG SUPP. for(pMember = pfForce->pMembers;pMember;pMember =pMember->psNext) { if ( PHYSFS_write( pFileHandle, &(pMember->pTempl->multiPlayerID), sizeof(pMember->pTempl->multiPlayerID), 1 ) != 1 ) { debug(LOG_ERROR, "saveForce: Write failed for %s: %s", fileName, PHYSFS_getLastError()); return FALSE; } } #endif if (!PHYSFS_close(pFileHandle)) { debug(LOG_ERROR, "saveForce: Close failed for %s: %s", fileName, PHYSFS_getLastError()); return FALSE; } return TRUE; } // //////////////////////////////////////////////////////////////////////////// // load a force from a file. BOOL loadForce(char *name) { STRING fileName[255]=""; // STRING tname[255]=""; PHYSFS_file *pFileHandle; UDWORD tcount,fcount=0,ref; DROID_TEMPLATE *psTempl; strcpy(fileName,name); debug(LOG_WZ, "loadForce: %s", fileName); pFileHandle = PHYSFS_openRead(fileName); // check file exists if (pFileHandle == NULL) { debug(LOG_ERROR, "loadForce: Failed to open %s: %s", fileName, PHYSFS_getLastError()); return FALSE; // failed } while(Force.pMembers) // clear current force { removeFromForce(0); } // DBERROR(("tem %d : mem %d",Force.pForceTemplates , Force.pMembers)); Force.pForceTemplates = NULL; Force.pMembers = NULL; // load in new force. if ( PHYSFS_read( pFileHandle, &tcount, sizeof(UDWORD), 1 ) != 1 ) // get number of templates { debug(LOG_ERROR, "loadForce: Read failed for %s: %s", fileName, PHYSFS_getLastError()); PHYSFS_close(pFileHandle); return FALSE; } if ( PHYSFS_read( pFileHandle, &fcount, sizeof(UDWORD), 1 ) != 1 ) // get number of droids in force { debug(LOG_ERROR, "loadForce: read failed for %s: %s", fileName, PHYSFS_getLastError()); PHYSFS_close(pFileHandle); return FALSE; } #if 0 // old method for(tcount;tcount!=0;tcount--) // get templates { psTempl = MALLOC(sizeof(DROID_TEMPLATE)); if (psTempl == NULL) // !HEAP_ALLOC(psTemplateHeap, &psTempl)) { debug( LOG_ERROR, "Couldn't allocate template for %s", fileName ); abort(); return FALSE; } if (fread(psTempl, sizeof(DROID_TEMPLATE), 1, pFileHandle) != 1) // read in a template. { debug( LOG_ERROR, "read failed for %s", fileName ); abort(); fclose(pFileHandle); return FALSE; } psTempl->pName = (CHAR*)&psTempl->aName; fgetc(pFileHandle); // remove the template separator in the force file. psTempl->psNext = Force.pForceTemplates; Force.pForceTemplates=psTempl; } for(fcount;fcount!=0;fcount--) // get forces. { if (fread(&ref, sizeof(ref), 1, pFileHandle) != 1) // read in a template ref code. { debug( LOG_ERROR, "read failed for %s", fileName ); abort(); fclose(pFileHandle); return FALSE; } for( psTempl = Force.pForceTemplates; // find relevant template psTempl && (psTempl->ref != ref); psTempl = psTempl->psNext); if(!psTempl) { debug( LOG_ERROR, "failed to load. invalid file." ); abort(); fclose(pFileHandle); return FALSE; } addToForce(psTempl); // add it to the force. } #else // new method. // get forces. while (fcount > 0) { if ( PHYSFS_read( pFileHandle, &ref, sizeof(ref), 1 ) != 1) // read in a template ref code. { debug(LOG_ERROR, "loadForce: read failed for %s: %s", fileName, PHYSFS_getLastError()); PHYSFS_close(pFileHandle); return FALSE; } psTempl = IdToTemplate(ref,DEATHMATCHTEMPLATES); if(psTempl) { addToForce(psTempl); // add it to the force. } fcount--; } #endif if (!PHYSFS_close(pFileHandle)) { debug(LOG_ERROR, "loadForce: Close failed for %s: %s", fileName, PHYSFS_getLastError()); return FALSE; } return TRUE; } // //////////////////////////////////////////////////////////////////////////// // STATS STUFF // //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // Get Player's stats PLAYERSTATS getMultiStats(UDWORD player,BOOL bLocal) { static PLAYERSTATS stat; DWORD statSize = sizeof(PLAYERSTATS); DPID playerDPID; playerDPID = player2dpid[player]; if(bLocal) { NETgetLocalPlayerData(playerDPID,&stat,&statSize); } else { NETgetGlobalPlayerData(playerDPID,&stat,&statSize); } return stat; } // //////////////////////////////////////////////////////////////////////////// // Set Player's stats BOOL setMultiStats(DWORD dp, PLAYERSTATS plStats, BOOL bLocal) { DPID playerDPID = (DPID) dp; if(bLocal) { NETsetLocalPlayerData(playerDPID,&plStats,sizeof(PLAYERSTATS)); } else { NETsetGlobalPlayerData(playerDPID,&plStats,sizeof(PLAYERSTATS)); } return TRUE; } // //////////////////////////////////////////////////////////////////////////// // Load Player Stats BOOL loadMultiStats(STRING *sPlayerName,PLAYERSTATS *playerStats) { STRING fileName[255]=""; UDWORD size; char *pFileData; PLAYERSTATS blankstats = {0}; SAVEDPLAYERSTATS st,*codedst; UDWORD tmp[4]; strcpy(fileName,MultiPlayersPath); strcat(fileName,sPlayerName); strcat(fileName,".sta"); debug(LOG_WZ, "loadMultiStats: %s", fileName); // check player already exists // FIXME: integrate with physfs stuff, and add basic sanity if ( !PHYSFS_exists( fileName ) ) { saveMultiStats(sPlayerName,sPlayerName,&blankstats); // didnt exist so create. } loadFile(fileName,&pFileData,&size); codedst = ((SAVEDPLAYERSTATS*)pFileData); //decode packet; memcpy(&tmp,&NetPlay.cryptKey,sizeof(tmp)); NETsetKey(11974,224351,2023901,21080); NETunmangleData((long int*)codedst,(long int*)&st,sizeof(SAVEDPLAYERSTATS)); NETsetKey(tmp[0],tmp[1],tmp[2],tmp[3]); //set stats. memcpy(playerStats,&(st.stats), sizeof(PLAYERSTATS)); // get //set the name. ASSUME STRING IS LONG ENOUGH! strcpy(sPlayerName,st.name); FREE(pFileData); // reset recent scores playerStats->recentKills = 0; playerStats->recentScore = 0; playerStats->killsToAdd = 0; playerStats->scoreToAdd = 0; // clear any skirmish stats. for(size = 0;size