// update players damage stats. void updateMultiStatsDamage(UDWORD attacker, UDWORD defender, UDWORD inflicted) { PLAYERSTATS st; if (Cheated) { return; } // FIXME: Why in the world are we using two different structs for stats when we can use only one? // Host controls self + AI, so update the scores for them as well. if (attacker < MAX_PLAYERS) { if (myResponsibility(attacker) && NetPlay.bComms) { st = getMultiStats(attacker); // get stats if (NetPlay.bComms) { st.scoreToAdd += (2 * inflicted); } else { st.recentScore += (2 * inflicted); } setMultiStats(attacker, st, true); } else { ingame.skScores[attacker][0] += (2 * inflicted); // increment skirmish players rough score. } } // FIXME: Why in the world are we using two different structs for stats when we can use only one? // Host controls self + AI, so update the scores for them as well. if (defender < MAX_PLAYERS) { if (myResponsibility(defender) && NetPlay.bComms) { st = getMultiStats(defender); // get stats if (NetPlay.bComms) { st.scoreToAdd -= inflicted; } else { st.recentScore -= inflicted; } setMultiStats(defender, st, true); } else { ingame.skScores[defender][0] -= inflicted; // increment skirmish players rough score. } } }
// //////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////// // Score // We use setMultiStats() to broadcast the score when needed. bool sendScoreCheck(void) { static UDWORD lastsent = 0; if (lastsent > gameTime) { lastsent= 0; } if (gameTime - lastsent < SCORE_FREQUENCY) { return true; } lastsent = gameTime; // Broadcast any changes in other players, but not in FRONTEND!!! if (titleMode != MULTIOPTION && titleMode != MULTILIMIT) { uint8_t i; for (i = 0; i < game.maxPlayers; i++) { // Host controls AI's scores + his own... if (myResponsibility(i)) { // Send score to everyone else setMultiStats(i, getMultiStats(i), false); } } } return true; }
void recvMultiStats(NETQUEUE queue) { uint32_t playerIndex; NETbeginDecode(queue, NET_PLAYER_STATS); // Retrieve the ID number of the player for which we need to // update the stats NETuint32_t(&playerIndex); if (playerIndex >= MAX_PLAYERS) { return; } // we don't what to update ourselves, we already know our score (FIXME: rewrite setMultiStats()) if (!myResponsibility(playerIndex)) { // Retrieve the actual stats NETuint32_t(&playerStats[playerIndex].played); NETuint32_t(&playerStats[playerIndex].wins); NETuint32_t(&playerStats[playerIndex].losses); NETuint32_t(&playerStats[playerIndex].totalKills); NETuint32_t(&playerStats[playerIndex].totalScore); NETuint32_t(&playerStats[playerIndex].recentKills); NETuint32_t(&playerStats[playerIndex].recentScore); NETuint32_t(&playerStats[playerIndex].killsToAdd); NETuint32_t(&playerStats[playerIndex].scoreToAdd); } NETend(); }
// //////////////////////////////////////////////////////////////////////////// // Send a new Droid to the other players bool SendDroid(DROID_TEMPLATE *pTemplate, uint32_t x, uint32_t y, uint8_t player, uint32_t id, const INITIAL_DROID_ORDERS *initialOrdersP) { if (!bMultiMessages) { return true; } ASSERT_OR_RETURN(false, x != 0 && y != 0, "SendDroid: Invalid droid coordinates"); ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "invalid player %u", player); // Dont send other droids during campaign setup if (ingame.localJoiningInProgress) { return true; } // Only send the droid if we are responsible if (!myResponsibility(player)) { // Don't build if we are not responsible return false; } debug(LOG_SYNC, "Droid sent with id of %u", id); NETbeginEncode(NETgameQueue(selectedPlayer), GAME_DEBUG_ADD_DROID); { Position pos(x, y, 0); bool haveInitialOrders = initialOrdersP != NULL; int32_t droidType = pTemplate->droidType; NETuint8_t(&player); NETuint32_t(&id); NETPosition(&pos); NETqstring(pTemplate->name); NETint32_t(&droidType); NETuint8_t(&pTemplate->asParts[COMP_BODY]); NETuint8_t(&pTemplate->asParts[COMP_BRAIN]); NETuint8_t(&pTemplate->asParts[COMP_PROPULSION]); NETuint8_t(&pTemplate->asParts[COMP_REPAIRUNIT]); NETuint8_t(&pTemplate->asParts[COMP_ECM]); NETuint8_t(&pTemplate->asParts[COMP_SENSOR]); NETuint8_t(&pTemplate->asParts[COMP_CONSTRUCT]); NETint8_t(&pTemplate->numWeaps); for (int i = 0; i < pTemplate->numWeaps; i++) { NETuint8_t(&pTemplate->asWeaps[i]); } NETbool(&haveInitialOrders); if (haveInitialOrders) { INITIAL_DROID_ORDERS initialOrders = *initialOrdersP; NETuint32_t(&initialOrders.secondaryOrder); NETint32_t(&initialOrders.moveToX); NETint32_t(&initialOrders.moveToY); NETuint32_t(&initialOrders.factoryId); // For making scripts happy. } } debug(LOG_LIFE, "===> sending Droid from %u id of %u ", player, id); return NETend(); }
// //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // New research stuff, so you can see what others are up to! // inform others that I'm researching this. bool sendResearchStatus(STRUCTURE *psBuilding, uint32_t index, uint8_t player, bool bStart) { if (!myResponsibility(player) || gameTime < 5) { return true; } NETbeginEncode(NETgameQueue(selectedPlayer), GAME_RESEARCHSTATUS); NETuint8_t(&player); NETbool(&bStart); // If we know the building researching it then send its ID if (psBuilding) { NETuint32_t(&psBuilding->id); } else { uint32_t zero = 0; NETuint32_t(&zero); } // Finally the topic in question NETuint32_t(&index); NETend(); // Tell UI to remove from the list of available research. MakeResearchStartedPending(&asPlayerResList[player][index]); return true; }
// //////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////// // Power Checking. Send a power level check every now and again. static BOOL sendPowerCheck() { uint8_t player; if (powerCheckLastSent > gameTime) { powerCheckLastSent = 0; } // Only send if not done recently if (gameTime - powerCheckLastSent < POWER_PERIOD) { return true; } powerCheckLastSent = gameTime; for (player = 0; player < MAX_PLAYERS; ++player) { powerCheckLastPower[player] = getPrecisePower(player); if (myResponsibility(player)) { if (!isInSync()) // Don't really send anything, unless out of synch. { NETbeginEncode(NETgameQueue(selectedPlayer), GAME_CHECK_POWER); NETuint8_t(&player); NETuint32_t(&gameTime); NETint64_t(&powerCheckLastPower[player]); NETend(); } } } return true; }
// update kills void updateMultiStatsKills(BASE_OBJECT *psKilled,UDWORD player) { PLAYERSTATS st; if (Cheated) { return; } // FIXME: Why in the world are we using two different structs for stats when we can use only one? // Host controls self + AI, so update the scores for them as well. if(myResponsibility(player) && NetPlay.bComms) { st = getMultiStats(player); if(NetPlay.bComms) { st.killsToAdd++; // increase kill count; } else { st.recentKills++; } setMultiStats(player, st, true); } else { ingame.skScores[player][1]++; } }
//AI multiplayer message, send from a certain player index to another player index bool sendAIMessage(char *pStr, UDWORD player, UDWORD to) { UDWORD sendPlayer; //check if this is one of the local players, don't need net send then if (to == selectedPlayer || myResponsibility(to)) //(the only) human on this machine or AI on this machine { triggerEventChat(player, to, pStr); //Just show "him" the message displayAIMessage(pStr, player, to); //Received a console message from a player callback //store and call later //------------------------------------------------- if (!msgStackPush(CALL_AI_MSG,player,to,pStr,-1,-1,NULL)) { debug(LOG_ERROR, "sendAIMessage() - msgStackPush - stack failed"); return false; } } else //not a local player (use multiplayer mode) { if (!ingame.localOptionsReceived) { return true; } //find machine that is hosting this human or AI sendPlayer = whosResponsible(to); if (sendPlayer >= MAX_PLAYERS) { debug(LOG_ERROR, "sendAIMessage() - sendPlayer >= MAX_PLAYERS"); return false; } if (!isHumanPlayer(sendPlayer)) //NETsend can't send to non-humans { debug(LOG_ERROR, "sendAIMessage() - player is not human."); return false; } //send to the player who is hosting 'to' player (might be himself if human and not AI) NETbeginEncode(NETnetQueue(sendPlayer), NET_AITEXTMSG); NETuint32_t(&player); //save the actual sender //save the actual player that is to get this msg on the source machine (source can host many AIs) NETuint32_t(&to); //save the actual receiver (might not be the same as the one we are actually sending to, in case of AIs) NETstring(pStr, MAX_CONSOLE_STRING_LENGTH); // copy message in. NETend(); } return true; }
// //////////////////////////////////////////////////////////////////////////// // Droid update information void sendDroidInfo(DROID *psDroid, DroidOrder const &order, bool add) { if (!myResponsibility(psDroid->player)) { return; } QueuedDroidInfo info; memset(&info, 0x00, sizeof(info)); // Suppress uninitialised warnings. (The uninitialised values in the queue would be ignored when reading the queue.) info.player = psDroid->player; info.droidId = psDroid->id; info.subType = order.psObj != NULL? ObjOrder : LocOrder; info.order = order.type; if (info.subType == ObjOrder) { info.destId = order.psObj->id; info.destType = order.psObj->type; } else { info.pos = order.pos; } if (order.type == DORDER_BUILD || order.type == DORDER_LINEBUILD) { info.structRef = order.psStats->ref; info.direction = order.direction; if (!isConstructionDroid(psDroid)) { return; // No point ordering things to build if they can't build anything. } } if (order.type == DORDER_LINEBUILD) { info.pos2 = order.pos2; } if (order.type == DORDER_BUILDMODULE) { info.index = order.index; } info.add = add; // Send later, grouped by order, so multiple droids with the same order can be encoded to much less data. queuedOrders.push_back(info); // Update pending orders, so the UI knows it happened. DROID_ORDER_DATA sOrder = infoToOrderData(info, order.psStats); if (!add) { psDroid->listPendingBegin = psDroid->asOrderList.size(); } orderDroidAddPending(psDroid, &sOrder); }
void recvMultiStats(NETQUEUE queue) { uint32_t playerIndex; NETbeginDecode(queue, NET_PLAYER_STATS); // Retrieve the ID number of the player for which we need to // update the stats NETuint32_t(&playerIndex); if (playerIndex >= MAX_PLAYERS) { NETend(); return; } if (playerIndex != queue.index && queue.index != NET_HOST_ONLY) { HandleBadParam("NET_PLAYER_STATS given incorrect params.", playerIndex, queue.index); NETend(); return; } // we don't what to update ourselves, we already know our score (FIXME: rewrite setMultiStats()) if (!myResponsibility(playerIndex)) { // Retrieve the actual stats NETuint32_t(&playerStats[playerIndex].played); NETuint32_t(&playerStats[playerIndex].wins); NETuint32_t(&playerStats[playerIndex].losses); NETuint32_t(&playerStats[playerIndex].totalKills); NETuint32_t(&playerStats[playerIndex].totalScore); NETuint32_t(&playerStats[playerIndex].recentKills); NETuint32_t(&playerStats[playerIndex].recentScore); EcKey::Key identity; NETbytes(&identity); EcKey::Key prevIdentity; if (!playerStats[playerIndex].identity.empty()) { prevIdentity = playerStats[playerIndex].identity.toBytes(EcKey::Public); } playerStats[playerIndex].identity.clear(); if (!identity.empty()) { playerStats[playerIndex].identity.fromBytes(identity, EcKey::Public); } if (identity != prevIdentity) { ingame.PingTimes[playerIndex] = PING_LIMIT; } } NETend(); }
// //////////////////////////////////////////////////////////////////////////// // add an artifact on destruction if required. void technologyGiveAway(const STRUCTURE *pS) { int i; uint8_t count = 1; uint32_t x, y; FEATURE *pF = NULL; FEATURE_TYPE type = FEAT_GEN_ARTE; // If a fully built factory (or with modules under construction) which is our responsibility got destroyed if (pS->pStructureType->type == REF_FACTORY && (pS->status == SS_BUILT || pS->currentBuildPts >= pS->body) && myResponsibility(pS->player)) { x = map_coord(pS->pos.x); y = map_coord(pS->pos.y); // Pick a tile to place the artifact if (!pickATileGen(&x, &y, LOOK_FOR_EMPTY_TILE, zonedPAT)) { ASSERT(false, "technologyGiveAway: Unable to find a free location"); } // Get the feature offset for(i = 0; i < numFeatureStats && asFeatureStats[i].subType != FEAT_GEN_ARTE; i++); // 'Build' the artifact pF = buildFeature((asFeatureStats + i), world_coord(x), world_coord(y), false); if (pF) { pF->player = pS->player; } NETbeginEncode(NET_ARTIFACTS, NET_ALL_PLAYERS); { /* Make sure that we don't have to violate the constness of pS. * Since the nettype functions aren't const correct when sending */ uint8_t player = pS->player; NETuint8_t(&count); NETenum(&type); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&pF->id); NETuint8_t(&player); } NETend(); } return; }
// //////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////// // Score // We use setMultiStats() to broadcast the score when needed. BOOL sendScoreCheck(void) { static UDWORD lastsent = 0; if (lastsent > gameTime) { lastsent= 0; } if (gameTime - lastsent < SCORE_FREQUENCY) { return true; } lastsent = gameTime; // Broadcast any changes in other players, but not in FRONTEND!!! if (titleMode != MULTIOPTION && titleMode != MULTILIMIT) { uint8_t i; for (i = 0; i < MAX_PLAYERS; i++) { PLAYERSTATS stats; // Host controls AI's scores + his own... if (myResponsibility(i)) { // Update score stats = getMultiStats(i); // Add recently scored points stats.recentKills += stats.killsToAdd; stats.totalKills += stats.killsToAdd; stats.recentScore += stats.scoreToAdd; stats.totalScore += stats.scoreToAdd; // Zero them out stats.killsToAdd = stats.scoreToAdd = 0; // Send score to everyone else setMultiStats(i, stats, false); } } } return true; }
// //////////////////////////////////////////////////////////////////////////// // Send a new Droid to the other players BOOL SendDroid(const DROID_TEMPLATE* pTemplate, uint32_t x, uint32_t y, uint8_t player, uint32_t id, const INITIAL_DROID_ORDERS *initialOrdersP) { if (!bMultiMessages) return true; ASSERT(x != 0 && y != 0, "SendDroid: Invalid droid coordinates"); ASSERT( player < MAX_PLAYERS, "invalid player %u", player); // Dont send other droids during campaign setup if (ingame.localJoiningInProgress) { return true; } // Only send the droid if we are responsible if (!myResponsibility(player)) { // Don't build if we are not responsible return false; } debug(LOG_SYNC, "Droid sent with id of %u", id); NETbeginEncode(NETgameQueue(selectedPlayer), GAME_DROID); { Position pos = { x, y, 0 }; uint32_t templateID = pTemplate->multiPlayerID; BOOL haveInitialOrders = initialOrdersP != NULL; NETuint8_t(&player); NETuint32_t(&id); NETPosition(&pos); NETuint32_t(&templateID); NETbool(&haveInitialOrders); if (haveInitialOrders) { INITIAL_DROID_ORDERS initialOrders = *initialOrdersP; NETuint32_t(&initialOrders.secondaryOrder); NETint32_t(&initialOrders.moveToX); NETint32_t(&initialOrders.moveToY); NETuint32_t(&initialOrders.factoryId); // For making scripts happy. } } debug(LOG_LIFE, "===> sending Droid from %u id of %u ",player,id); return NETend(); }
// //////////////////////////////////////////////////////////////////////////// // Droid update information BOOL SendDroidInfo(const DROID* psDroid, DROID_ORDER order, uint32_t x, uint32_t y, const BASE_OBJECT* psObj, const BASE_STATS *psStats, uint32_t x2, uint32_t y2, uint16_t direction) { if (!bMultiMessages) return true; if (!myResponsibility(psDroid->player)) { return true; } QueuedDroidInfo info; memset(&info, 0x00, sizeof(info)); // Suppress uninitialised warnings. (The uninitialised values in the queue would be ignored when reading the queue.) info.player = psDroid->player; info.droidId = psDroid->id; info.order = order; info.subType = psObj != NULL; if (info.subType) { info.destId = psObj->id; info.destType = psObj->type; } else { info.x = x; info.y = y; } if (order == DORDER_BUILD || order == DORDER_LINEBUILD) { info.structRef = psStats->ref; info.direction = direction; } if (order == DORDER_LINEBUILD) { info.x2 = x2; info.y2 = y2; } // Send later, grouped by order, so multiple droids with the same order can be encoded to much less data. queuedOrders.push_back(info); return true; }
// //////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////// // Score // We use setMultiStats() to broadcast the score when needed. bool sendScoreCheck(void) { // Broadcast any changes in other players, but not in FRONTEND!!! if (titleMode != MULTIOPTION && titleMode != MULTILIMIT) { uint8_t i; for (i = 0; i < game.maxPlayers; i++) { // Host controls AI's scores + his own... if (myResponsibility(i)) { // Send score to everyone else setMultiStats(i, getMultiStats(i), false); } } } return true; }
void sendPlayerGameTime() { unsigned player; uint32_t latencyTicks = discreteChosenLatency / GAME_TICKS_PER_UPDATE; uint32_t checkTime = gameTime; uint32_t checkCrc = nextDebugSync(); for (player = 0; player < game.maxPlayers; ++player) { if (!myResponsibility(player) && whosResponsible(player) != realSelectedPlayer) { continue; } NETbeginEncode(NETgameQueue(player), GAME_GAME_TIME); NETuint32_t(&latencyTicks); NETuint32_t(&checkTime); NETuint32_tLarge(&checkCrc); NETuint16_t(&wantedLatency); NETend(); } }
// //////////////////////////////////////////////////////////////////////////// // add an artifact on destruction if required. void technologyGiveAway(const STRUCTURE *pS) { uint8_t count = 1; FEATURE_TYPE type = FEAT_GEN_ARTE; // If a fully built factory (or with modules under construction) which is our responsibility got destroyed if (pS->pStructureType->type == REF_FACTORY && (pS->status == SS_BUILT || pS->currentBuildPts >= pS->body) && myResponsibility(pS->player)) { uint32_t x = map_coord(pS->pos.x); uint32_t y = map_coord(pS->pos.y); uint32_t id = generateNewObjectId(); // Pick a tile to place the artifact if (!pickATileGen(&x, &y, LOOK_FOR_EMPTY_TILE, zonedPAT)) { ASSERT(false, "technologyGiveAway: Unable to find a free location"); } NETbeginEncode(NETgameQueue(selectedPlayer), GAME_ARTIFACTS); { /* Make sure that we don't have to violate the constness of pS. * Since the nettype functions aren't const correct when sending */ uint8_t player = pS->player; NETuint8_t(&count); NETenum(&type); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&id); NETuint8_t(&player); } NETend(); } return; }
void recvMultiStats(NETQUEUE queue) { uint32_t playerIndex; NETbeginDecode(queue, NET_PLAYER_STATS); // Retrieve the ID number of the player for which we need to // update the stats NETuint32_t(&playerIndex); if (playerIndex >= MAX_PLAYERS) { NETend(); return; } if (playerIndex != queue.index && queue.index != NET_HOST_ONLY) { HandleBadParam("NET_PLAYER_STATS given incorrect params.", playerIndex, queue.index); NETend(); return; } // we don't what to update ourselves, we already know our score (FIXME: rewrite setMultiStats()) if (!myResponsibility(playerIndex)) { // Retrieve the actual stats NETuint32_t(&playerStats[playerIndex].played); NETuint32_t(&playerStats[playerIndex].wins); NETuint32_t(&playerStats[playerIndex].losses); NETuint32_t(&playerStats[playerIndex].totalKills); NETuint32_t(&playerStats[playerIndex].totalScore); NETuint32_t(&playerStats[playerIndex].recentKills); NETuint32_t(&playerStats[playerIndex].recentScore); } NETend(); }
// /////////////////////////////////////////////////////////////////////////// // send a droid info packet. static BOOL sendDroidCheck(void) { DROID *pD, **ppD; uint8_t i, count; static UDWORD lastSent = 0; // Last time a struct was sent. UDWORD toSend = 12; if (lastSent > gameTime) { lastSent= 0; } // Only send a struct send if not done recently if (gameTime - lastSent < DROID_PERIOD) { return true; } lastSent = gameTime; if (!isInSync()) // Don't really send anything, unless out of synch. { NETbeginEncode(NETgameQueue(selectedPlayer), GAME_CHECK_DROID); } // Allocate space for the list of droids to send ppD = alloca(sizeof(DROID *) * toSend); // Get the list of droids to sent for (i = 0, count = 0; i < toSend; i++) { pD = pickADroid(); if (pD == NULL || (pD->gameCheckDroid != NULL && ((PACKAGED_CHECK *)pD->gameCheckDroid)->gameTime > gameTime)) { continue; // Didn't find a droid, or droid was synched recently. } // If the droid is ours add it to the list if (myResponsibility(pD->player)) { ppD[count++] = pD; } free(pD->gameCheckDroid); pD->gameCheckDroid = (PACKAGED_CHECK *)malloc(sizeof(PACKAGED_CHECK)); *(PACKAGED_CHECK *)pD->gameCheckDroid = packageCheck(pD); } if (!isInSync()) // Don't really send anything, unless out of synch. { // Send the number of droids to expect NETuint8_t(&count); NETuint32_t(&gameTime); // Send game time. // Add the droids to the packet for (i = 0; i < count; i++) { NETPACKAGED_CHECK((PACKAGED_CHECK *)ppD[i]->gameCheckDroid); } } if (!isInSync()) // Don't really send anything, unless out of synch. { return NETend(); } return true; }
static void displayMultiPlayer(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGHT *pColours) { char str[128]; UDWORD x = xOffset+psWidget->x; UDWORD y = yOffset+psWidget->y; UDWORD player = psWidget->UserData; //get the in game player number. Position position; Vector3i rotation; if( responsibleFor(player,0) ) { displayExtraGubbins(widgGetFromID(psWScreen,MULTIMENU_FORM)->height); } iV_SetFont(font_regular); // font iV_SetTextColour(WZCOL_TEXT_BRIGHT); if(isHumanPlayer(player) || (game.type == SKIRMISH && player<game.maxPlayers) ) { ssprintf(str, "%d: %s", NetPlay.players[player].position, getPlayerName(player)); if (isHumanPlayer(player)) { SetPlayerTextColor(alliances[selectedPlayer][player], player); } else { SetPlayerTextColor(alliances[selectedPlayer][player], player); } while(iV_GetTextWidth(str) >= (MULTIMENU_C0-MULTIMENU_C2-10) ) { str[strlen(str)-1]='\0'; } iV_DrawText(str, x+MULTIMENU_C2, y+MULTIMENU_FONT_OSET); //c3-7 alliance //manage buttons by showing or hiding them. gifts only in campaign, { if(game.alliance != NO_ALLIANCES) { if(alliances[selectedPlayer][player] == ALLIANCE_FORMED) { if(player != selectedPlayer && !giftsUp[player] ) { if (game.alliance != ALLIANCES_TEAMS) { widgReveal(psWScreen,MULTIMENU_GIFT_RAD+ player); widgReveal(psWScreen,MULTIMENU_GIFT_RES+ player); } widgReveal(psWScreen,MULTIMENU_GIFT_DRO+ player); widgReveal(psWScreen,MULTIMENU_GIFT_POW+ player); giftsUp[player] = true; } } else { if(player != selectedPlayer && giftsUp[player]) { if (game.alliance != ALLIANCES_TEAMS) { widgHide(psWScreen,MULTIMENU_GIFT_RAD+ player); widgHide(psWScreen,MULTIMENU_GIFT_RES+ player); } widgHide(psWScreen,MULTIMENU_GIFT_DRO+ player); widgHide(psWScreen,MULTIMENU_GIFT_POW+ player); giftsUp[player] = false; } } } } } if(isHumanPlayer(player)) { SetPlayerTextColor(alliances[selectedPlayer][player], player); // Let's use the real score for MP games if (NetPlay.bComms) { //c8:score, if (Cheated) { sprintf(str,"(cheated)"); } else { sprintf(str,"%d",getMultiStats(player).recentScore); } iV_DrawText(str, x+MULTIMENU_C8, y+MULTIMENU_FONT_OSET); //c9:kills, sprintf(str,"%d",getMultiStats(player).recentKills); iV_DrawText(str, x+MULTIMENU_C9, y+MULTIMENU_FONT_OSET); } else { // estimate of score for skirmish games sprintf(str,"%d",ingame.skScores[player][0]); iV_DrawText(str, x+MULTIMENU_C8, y+MULTIMENU_FONT_OSET); // estimated kills sprintf(str,"%d",ingame.skScores[player][1]); iV_DrawText(str, x+MULTIMENU_C9, y+MULTIMENU_FONT_OSET); } if(!getDebugMappingStatus()) { //only show player's units, and nobody elses. //c10:units if (myResponsibility(player)) { SetPlayerTextColor(alliances[selectedPlayer][player], player); sprintf(str, "%d", getNumDroids(player) + getNumTransporterDroids(player)); iV_DrawText(str, x+MULTIMENU_C10, y+MULTIMENU_FONT_OSET); } if (runningMultiplayer()) { //c11:ping if (player != selectedPlayer) { if (ingame.PingTimes[player] >= 2000) { sprintf(str,"???"); } else { sprintf(str, "%d", ingame.PingTimes[player]); } iV_DrawText(str, x+MULTIMENU_C11, y+MULTIMENU_FONT_OSET); } } else { int num; STRUCTURE *temp; // NOTE, This tallys up *all* the structures you have. Test out via 'start with no base'. for (num = 0, temp = apsStructLists[player]; temp != NULL;num++,temp = temp->psNext); //c11: Structures sprintf(str, "%d", num); iV_DrawText(str, x+MULTIMENU_C11, y+MULTIMENU_FONT_OSET); } } } else { SetPlayerTextColor(alliances[selectedPlayer][player], player); // Let's use the real score for MP games if (NetPlay.bComms) { //c8:score, if (Cheated) { sprintf(str,"(cheated)"); } else { sprintf(str,"%d",getMultiStats(player).recentScore); } iV_DrawText(str, x+MULTIMENU_C8, y+MULTIMENU_FONT_OSET); //c9:kills, sprintf(str,"%d",getMultiStats(player).recentKills); iV_DrawText(str, x+MULTIMENU_C9, y+MULTIMENU_FONT_OSET); } else { // estimate of score for skirmish games sprintf(str,"%d",ingame.skScores[player][0]); iV_DrawText(str, x+MULTIMENU_C8, y+MULTIMENU_FONT_OSET); // estimated kills sprintf(str,"%d",ingame.skScores[player][1]); iV_DrawText(str, x+MULTIMENU_C9, y+MULTIMENU_FONT_OSET); } } /* Display player power instead of number of played games * and number of units instead of ping when in debug mode */ if(getDebugMappingStatus()) //Won't pass this when in both release and multiplayer modes { //c10: Total number of player units in possession sprintf(str,"%d",getNumDroids(player) + getNumTransporterDroids(player)); iV_DrawText(str, x+MULTIMENU_C10, y+MULTIMENU_FONT_OSET); //c11: Player power sprintf(str, "%u", (int)getPower(player)); iV_DrawText(str, MULTIMENU_FORM_X+MULTIMENU_C11, y+MULTIMENU_FONT_OSET); } // a droid of theirs. if(apsDroidLists[player]) { pie_SetGeometricOffset( MULTIMENU_FORM_X+MULTIMENU_C1 ,y+MULTIMENU_PLAYER_H); rotation.x = -15; rotation.y = 45; rotation.z = 0; position.x = 0; position.y = 0; position.z = 2000; //scale them! displayComponentButtonObject(apsDroidLists[player],&rotation,&position,false, 100); } // clean up widgets if player leaves while menu is up. if(!isHumanPlayer(player) && !(game.type == SKIRMISH && player<game.maxPlayers)) { if(widgGetFromID(psWScreen,MULTIMENU_CHANNEL+player)) { widgDelete(psWScreen,MULTIMENU_CHANNEL+ player); } if(widgGetFromID(psWScreen,MULTIMENU_ALLIANCE_BASE+player) ) { widgDelete(psWScreen,MULTIMENU_ALLIANCE_BASE+ player); widgDelete(psWScreen,MULTIMENU_GIFT_RAD+ player); widgDelete(psWScreen,MULTIMENU_GIFT_RES+ player); widgDelete(psWScreen,MULTIMENU_GIFT_DRO+ player); widgDelete(psWScreen,MULTIMENU_GIFT_POW+ player); giftsUp[player] = false; } } }
// //////////////////////////////////////////////////////////////////////// // Send structure information. static BOOL sendStructureCheck(void) { uint8_t player; if (structureCheckLastSent > gameTime) { structureCheckLastSent = 0; } // Only send a struct send if not done recently if (gameTime - structureCheckLastSent < STRUCT_PERIOD) { return true; } structureCheckLastSent = gameTime; for (player = 0; player < MAX_PLAYERS; ++player) { STRUCTURE *pS = pickAStructure(player); bool hasCapacity = true; uint8_t capacity; // Only send info about complete buildings if (pS == NULL || pS->status != SS_BUILT) { structureCheckLastId[player] = 0; continue; } switch (pS->pStructureType->type) { case REF_RESEARCH: capacity = pS->pFunctionality->researchFacility.capacity; break; case REF_FACTORY: case REF_VTOL_FACTORY: capacity = pS->pFunctionality->factory.capacity; break; case REF_POWER_GEN: capacity = pS->pFunctionality->powerGenerator.capacity; default: hasCapacity = false; break; } structureCheckLastId[player] = pS->id; structureCheckLastBody[player] = pS->body; structureCheckLastDirection[player] = pS->rot; structureCheckLastType[player] = pS->pStructureType->type; if (myResponsibility(player)) { if (!isInSync()) // Don't really send anything, unless out of synch. { NETbeginEncode(NETgameQueue(selectedPlayer), GAME_CHECK_STRUCT); NETuint8_t(&player); NETuint32_t(&gameTime); NETuint32_t(&pS->id); NETuint32_t(&pS->body); NETuint32_t(&pS->pStructureType->type); NETRotation(&pS->rot); if (hasCapacity) { NETuint8_t(&capacity); } NETend(); } } } return true; }
bool stageTwoInitialise(void) { int i; debug(LOG_WZ, "== stageTwoInitalise =="); // make sure we clear on loading; this a bad hack to fix a bug when // loading a savegame where we are building a lassat for (i = 0; i < MAX_PLAYERS; i++) { setLasSatExists(false, i); } if(bMultiPlayer) { if (!multiTemplateSetup()) { return false; } } if (!dispInitialise()) /* Initialise the display system */ { return false; } if(!initMiscImds()) /* Set up the explosions */ { iV_ShutDown(); debug( LOG_FATAL, "Can't find all the explosions graphics?" ); abort(); return false; } if (!cmdDroidInit()) { return false; } /* Shift the interface initialisation here temporarily so that it can pick up the stats after they have been loaded */ if (!intInitialise()) { return false; } if (!initMessage()) /* Initialise the message heaps */ { return false; } if (!gwInitialise()) { return false; } // keymappings keyClearMappings(); keyInitMappings(false); // Set the default uncoloured cursor here, since it looks slightly // better for menus and such. wzSetCursor(CURSOR_DEFAULT); SetFormAudioIDs(ID_SOUND_WINDOWOPEN,ID_SOUND_WINDOWCLOSE); // Setup game queues. // Don't ask why this doesn't go in stage three. In fact, don't even ask me what stage one/two/three is supposed to mean, it seems about as descriptive as stage doStuff, stage doMoreStuff and stage doEvenMoreStuff... debug(LOG_MAIN, "Init game queues, I am %d.", selectedPlayer); sendQueuedDroidInfo(); // Discard any pending orders which could later get flushed into the game queue. for (i = 0; i < MAX_PLAYERS; ++i) { NETinitQueue(NETgameQueue(i)); if (!myResponsibility(i)) { NETsetNoSendOverNetwork(NETgameQueue(i)); } } debug(LOG_MAIN, "stageTwoInitialise: done"); return true; }
/* Do the AI for a droid */ void aiUpdateDroid(DROID *psDroid) { BASE_OBJECT *psTarget; BOOL lookForTarget,updateTarget; ASSERT(psDroid != NULL, "Invalid droid pointer"); if (!psDroid || isDead((BASE_OBJECT *)psDroid)) { return; } // HACK: we always want to update orders when NOT running a MP game, // and we don't want to update when the droid belongs to another human player if (!myResponsibility(psDroid->player) && bMultiPlayer && isHumanPlayer(psDroid->player)) { return; // we should not order this droid around } lookForTarget = false; updateTarget = false; // look for a target if doing nothing if (orderState(psDroid, DORDER_NONE) || orderState(psDroid, DORDER_GUARD) || orderState(psDroid, DORDER_TEMP_HOLD)) { lookForTarget = true; } // but do not choose another target if doing anything while guarding if (orderState(psDroid, DORDER_GUARD) && (psDroid->action != DACTION_NONE)) { lookForTarget = false; } // except when self-repairing if (psDroid->action == DACTION_DROIDREPAIR && psDroid->psActionTarget[0] == (BASE_OBJECT *)psDroid) { lookForTarget = true; } // don't look for a target if sulking if (psDroid->action == DACTION_SULK) { lookForTarget = false; } /* Only try to update target if already have some target */ if (psDroid->action == DACTION_ATTACK || psDroid->action == DACTION_MOVEFIRE || psDroid->action == DACTION_MOVETOATTACK || psDroid->action == DACTION_ROTATETOATTACK) { updateTarget = true; } if ((orderState(psDroid, DORDER_OBSERVE) || orderState(psDroid, DORDER_ATTACKTARGET)) && psDroid->psTarget && aiObjectIsProbablyDoomed(psDroid->psTarget)) { lookForTarget = true; updateTarget = false; } /* Don't update target if we are sent to attack and reached attack destination (attacking our target) */ if (orderState(psDroid, DORDER_ATTACK) && psDroid->psActionTarget[0] == psDroid->psTarget) { updateTarget = false; } // don't look for a target if there are any queued orders if (psDroid->listSize > 0) { lookForTarget = false; updateTarget = false; } // horrible check to stop droids looking for a target if // they would switch to the guard order in the order update loop if ((psDroid->order == DORDER_NONE) && (psDroid->player == selectedPlayer) && !isVtolDroid(psDroid) && secondaryGetState(psDroid, DSO_HALTTYPE) == DSS_HALT_GUARD) { lookForTarget = false; updateTarget = false; } // don't allow units to start attacking if they will switch to guarding the commander if(hasCommander(psDroid)) { lookForTarget = false; updateTarget = false; } if(bMultiPlayer && isVtolDroid(psDroid) && isHumanPlayer(psDroid->player)) { lookForTarget = false; updateTarget = false; } // do not look for a target if droid is currently under direct control. if(driveModeActive() && (psDroid == driveGetDriven())) { lookForTarget = false; updateTarget = false; } // CB and VTOL CB droids can't autotarget. if (psDroid->droidType == DROID_SENSOR && !standardSensorDroid(psDroid)) { lookForTarget = false; updateTarget = false; } // do not attack if the attack level is wrong if (secondaryGetState(psDroid, DSO_ATTACK_LEVEL) != DSS_ALEV_ALWAYS) { lookForTarget = false; } /* For commanders and non-assigned non-commanders: look for a better target once in a while */ if(!lookForTarget && updateTarget) { if((psDroid->numWeaps > 0) && !hasCommander(psDroid)) //not assigned to commander { if((psDroid->id + gameTime)/TARGET_UPD_SKIP_FRAMES != (psDroid->id + gameTime - deltaGameTime)/TARGET_UPD_SKIP_FRAMES) { unsigned int i; (void)updateAttackTarget((BASE_OBJECT*)psDroid, 0); // this function always has to be called on weapon-slot 0 (even if ->numWeaps == 0) //updates all targets for (i = 1; i < psDroid->numWeaps; ++i) { (void)updateAttackTarget((BASE_OBJECT*)psDroid, i); } } } } /* Null target - see if there is an enemy to attack */ if (lookForTarget && !updateTarget) { if (psDroid->droidType == DROID_SENSOR) { if (aiChooseSensorTarget((BASE_OBJECT *)psDroid, &psTarget)) { orderDroidObj(psDroid, DORDER_OBSERVE, psTarget); } } else { if (aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget, 0, true, NULL)) { orderDroidObj(psDroid, DORDER_ATTACKTARGET, psTarget); } } } }