/* 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 */ /* * Multijoin.c * * Alex Lee, pumpkin studios, bath, * * Stuff to handle the comings and goings of players. */ #include // for sprintf #include "lib/framework/frame.h" #include "objmem.h" #include "statsdef.h" #include "droiddef.h" #include "lib/ivis_common/textdraw.h" #include "lib/gamelib/gtime.h" #include "game.h" #include "projectile.h" #include "droid.h" #include "map.h" #include "power.h" #include "game.h" // for loading maps #include "player.h" #include "message.h" // for clearing game messages #include "text.h" // for string resources. #include "order.h" #include "console.h" #include "orderdef.h" // for droid_order_data #include "hci.h" #include "component.h" #include "research.h" #include "lib/sound/audio.h" #include "audio_id.h" #include "wrappers.h" #include "intimage.h" #include "data.h" #include "lib/script/script.h" #include "scripttabs.h" #include "lib/netplay/netplay.h" #include "multiplay.h" #include "multijoin.h" #include "multirecv.h" #include "multiint.h" #include "multistat.h" #include "multigifts.h" // //////////////////////////////////////////////////////////////////////////// // External Variables extern BOOL bHosted; extern BOOL multiRequestUp; // //////////////////////////////////////////////////////////////////////////// //external functions // //////////////////////////////////////////////////////////////////////////// // Local Functions BOOL sendVersionCheck (); BOOL recvVersionCheck (NETMSG *pMsg); BOOL intDisplayMultiJoiningStatus (UBYTE joinCount); VOID clearPlayer (UDWORD player,BOOL quietly,BOOL removeOil);// what to do when a arena player leaves. BOOL MultiPlayerLeave (DPID dp); // remote player has left. BOOL MultiPlayerJoin (DPID dp); // remote player has just joined. VOID setupNewPlayer (DPID dpid,UDWORD player); // stuff to do when player joins. //BOOL multiPlayerRequest (NETMSG *pMsg); // remote player has requested info //BOOL UpdateClient (DPID dest, UDWORD playerToSend); // send information to a remote player //BOOL ProcessDroidOrders (VOID); // ince setup, this player issues each droid order. //BOOL SendFeatures (FEATURE *pFeature, DPID player); //BOOL recvFeatures (NETMSG *pMsg); VOID resetMultiVisibility(UDWORD player); // //////////////////////////////////////////////////////////////////////////// // Version Check BOOL sendVersionCheck() { NETMSG msg; msg.size = 0; NetAdd(msg,0,selectedPlayer); NetAdd(msg,1,cheatHash); msg.size = sizeof(cheatHash)+1; msg.type = NET_VERSION; return NETbcast(&msg,TRUE); } BOOL recvVersionCheck(NETMSG *pMsg) { UDWORD extCheat[CHEAT_MAXCHEAT]; UBYTE pl; CHAR sTmp[128]; NetGet(pMsg,1,extCheat); if( memcmp(extCheat,cheatHash, CHEAT_MAXCHEAT*4) != 0) { goto FAILURE; } return TRUE; FAILURE: // what should we do now ? NetGet(pMsg,0,pl); // DBERROR(("%s has different data to you CHEATING?")); sprintf(sTmp,"%s has different data. CHEATING or wrong version",getPlayerName(pl)); addConsoleMessage(sTmp,DEFAULT_JUSTIFY); sendTextMessage(sTmp,TRUE); /* // We need to disable this for now. It is in any case trivial to circumvent, and // may give a false sense of security. - Per if(NetPlay.bHost) { kickPlayer( player2dpid[pl] ); } */ // setPlayerHasLost(TRUE); return TRUE; } // //////////////////////////////////////////////////////////////////////////// // Wait For Players BOOL intDisplayMultiJoiningStatus(UBYTE joinCount) { UDWORD x,y,w,h; CHAR sTmp[6]; w = RET_FORMWIDTH; h = RET_FORMHEIGHT; x = RET_X; y = RET_Y; // cameraToHome(selectedPlayer); // home the camera to the player. RenderWindowFrame(&FrameNormal, x, y ,w, h); // draw a wee blu box. // display how far done.. pie_DrawText(strresGetString(psStringRes, STR_GAM_JOINING), x+(w/2)-(iV_GetTextWidth(strresGetString(psStringRes,STR_GAM_JOINING))/2), y+(h/2)-8 ); sprintf(sTmp,"%d%%", PERCENT((NetPlay.playercount-joinCount),NetPlay.playercount) ); pie_DrawText(sTmp ,x + (w / 2) - 10, y + (h / 2) + 10); return TRUE; } // //////////////////////////////////////////////////////////////////////////// // when a remote player leaves an arena game do this! VOID clearPlayer(UDWORD player,BOOL quietly,BOOL removeOil) { UDWORD i; BOOL bTemp; STRUCTURE *psStruct,*psNext; player2dpid[player] = 0; // remove player, make computer controlled ingame.JoiningInProgress[player] = FALSE; // if they never joined, reset the flag for(i = 0;ipsNext; bTemp = FALSE; if(removeOil) { if (psStruct->pStructureType->type == REF_RESOURCE_EXTRACTOR) { bTemp = TRUE; } } if(quietly) { removeStruct(psStruct, TRUE); } else { if( (psStruct->pStructureType->type != REF_WALL && psStruct->pStructureType->type != REF_WALLCORNER ) ) { destroyStruct(psStruct); } } if(bTemp) { if(apsFeatureLists[0]->psStats->subType == FEAT_OIL_RESOURCE) { removeFeature(apsFeatureLists[0]); } } psStruct = psNext; } return; } // Reset visibilty, so a new player can't see the old stuff!! VOID resetMultiVisibility(UDWORD player) { UDWORD owned; DROID *pDroid; STRUCTURE *pStruct; for(owned = 0 ; owned psNext) { pDroid->visible[player] = FALSE; } //structures for(pStruct= apsStructLists[owned];pStruct;pStruct=pStruct->psNext) { pStruct->visible[player] = FALSE; } } } return; } // //////////////////////////////////////////////////////////////////////////// // A remote player has left the game BOOL MultiPlayerLeave( DPID dp) { UDWORD i = 0; char buf[255]; while((player2dpid[i] != dp) && (i game.maxPlayers ) { kickPlayer(dpid); } } return TRUE; } // //////////////////////////////////////////////////////////////////////////// // Setup Stuff for a new player. void setupNewPlayer(DPID dpid,UDWORD player) { UDWORD i;//,col; char buf[255]; player2dpid[player] = dpid; // assign them a player too. ingame.PingTimes[player] =0; // reset ping time ingame.JoiningInProgress[player] = TRUE; // note that player is now joining. // if (game.type == DMATCH) // { // // set the power level for that player.. // setPower(player, game.power); // } for(i=0;ipsNext) // **DROIDS** { sendWholeDroid(pD,dest); } if(playerToSend == 0) // **FEATURES** SEND ONLY ONCE (BIG MSG!) { SendFeatures(apsFeatureLists[0],dest); } // pR = asPlayerResList[playerToSend]; // **RESEARCH** // for(i=0; ipName );//the name to give the template // m.size =(UWORD)(sizeof(DROID_TEMPLATE)+ strlen(pT->pName) +2); // m.type=NET_TEMPLATE; // NETsend(m,dest,TRUE); // pT=pT->psNext; // onto next template // } // for(pS=apsStructLists[playerToSend]; pS; pS=pS->psNext) // **STRUCTURES** // { // NOTE SEND WHOLE STRUCTURE DOESNT WORK! need to do as sendwholedroids. // SendWholeStructure(pS,dest); // } m.type = NET_PLAYERCOMPLETE; // completed message NetAdd(m,0,player2dpid[selectedPlayer]); m.size = sizeof(player2dpid[selectedPlayer]); NETsend(m,dest,TRUE); return TRUE; } // //////////////////////////////////////////////////////////////////////////// // Send/Recv Features when a player joins the game. // send a list of features on this machine. BOOL SendFeatures(FEATURE *pFeature, DPID player) { NETMSG msg; msg.size = 0; msg.type = NET_FEATURES; for(;pFeature;pFeature=pFeature->psNext) { NetAdd(msg,msg.size,pFeature->id); //id msg.size += sizeof(pFeature->id); NetAdd(msg,msg.size,pFeature->x); // x msg.size += sizeof(pFeature->x); NetAdd(msg,msg.size,pFeature->y); // y msg.size += sizeof(pFeature->y); NetAdd(msg,msg.size,pFeature->psStats->ref); // type msg.size += sizeof(pFeature->psStats->ref); NetAdd(msg,msg.size,pFeature->body); // body points msg.size += sizeof(pFeature->body); NetAdd(msg,msg.size,((UBYTE)pFeature->player) ); // player. msg.size += 1; } return( NETsend(msg,player,TRUE)); } // //////////////////////////////////////////////////////////////////////////// // features not found on other machines. BOOL recvFeatures(NETMSG *pMsg) { UDWORD id,type,msgdepth,i,body,player; UWORD x,y; for(msgdepth=0; msgdepthsize ;msgdepth+=17) // go down the list { NetGet(pMsg,msgdepth, id); // Recv Msg. NetGet(pMsg,msgdepth+4, x); NetGet(pMsg,msgdepth+6, y); NetGet(pMsg,msgdepth+8,type); NetGet(pMsg,msgdepth+12,body); player = pMsg->body[msgdepth+16]; for(i=0; (iid = id; //set id, naughty i know. apsFeatureLists[0]->body = body; apsFeatureLists[0]->player = (UBYTE)player; } } return TRUE; } // //////////////////////////////////////////////////////////////////////////// // Process the droids we previously recevied, issuing the orders.. BOOL ProcessDroidOrders(VOID) { UDWORD i; DROID *pD; UDWORD TarRef; DROID_ORDER_DATA sOrder; DROIDSTORE *pStore; if(tempDroidList == NULL) { DBPRINTF(("MULTIJOIN: NO DROIDS AT ALL! GULP!\n")); return TRUE; } pD = tempDroidList->psDroid; //apsDroidLists[i]; // look at player i's droids. while( pD ) { if(pD->psTarget) // check for a target. { TarRef = (UDWORD) pD->psTarget; pD->psTarget = IdToPointer(TarRef,ANYPLAYER); if(pD->psTarget == NULL) // target wasn't found. check list of things to add. { for(pStore = tempDroidList;pStore;pStore = pStore->psNext) { if(pStore->psDroid->id == TarRef) { pD->psTarget = (BASE_OBJECT*)pStore->psDroid; break; } } } } if( pD->psTarStats ) // if target has stats then find them { TarRef = (UDWORD) pD->psTarStats; pD->psTarStats = NULL; for(i=0; ( ipsTarStats = (BASE_STATS *) &(asStructureStats[i]); } else {// wasn't a structure target. Must be a feature for(i=0; (ipsTarStats = (BASE_STATS *) &(asFeatureStats[i]); } } } sOrder.order = pD->order; // set the order sOrder.x = pD->orderX; sOrder.y = pD->orderY; sOrder.psObj = pD->psTarget; sOrder.psStats = pD->psTarStats; if(pD->psTarget) // double check to see if we caught it! { orderDroidBase(pD,&sOrder); // issue the order. } else if(pD->order == DORDER_BUILD) // building has no { // target in the early stages. orderDroidBase(pD,&sOrder); } addDroid(pD, apsDroidLists); // add the droid to the world. pStore = tempDroidList->psNext; // goto next droid. FREE(tempDroidList); tempDroidList = pStore; if(tempDroidList) { pD=tempDroidList->psDroid; } else { pD=NULL; } } return TRUE; } */ // //////////////////////////////////////////////////////////////////////////// // reduce the amount of oil that can be extracted. void modifyResources(POWER_GEN_FUNCTION* psFunction) { switch(game.power) { case LEV_LOW: psFunction->powerMultiplier = psFunction->powerMultiplier * 3/4 ; // careful with the brackets! (do mul before div) break; case LEV_MED: psFunction->powerMultiplier = psFunction->powerMultiplier * 1; break; case LEV_HI: psFunction->powerMultiplier = psFunction->powerMultiplier * 5/4 ; break; default: break; } return; }