void requestAlliance(uint8_t from, uint8_t to, bool prop, bool allowAudio) { if (prop && bMultiMessages) { sendAlliance(from, to, ALLIANCE_REQUESTED, false); return; // Wait for our message. } syncDebug("Request alliance %d %d", from, to); alliances[from][to] = ALLIANCE_REQUESTED; // We've asked alliances[to][from] = ALLIANCE_INVITATION; // They've been invited CBallFrom = from; CBallTo = to; eventFireCallbackTrigger((TRIGGER_TYPE) CALL_ALLIANCEOFFER); triggerEventAllianceOffer(from, to); if (to == selectedPlayer) { CONPRINTF(ConsoleString, (ConsoleString, _("%s Requests An Alliance With You"), getPlayerName(from))); if (allowAudio) { audio_QueueTrack(ID_ALLIANCE_OFF); } } else if (from == selectedPlayer) { CONPRINTF(ConsoleString, (ConsoleString, _("You Invite %s To Form An Alliance"), getPlayerName(to))); if (allowAudio) { audio_QueueTrack(ID_ALLIANCE_OFF); } } }
// //////////////////////////////////////////////////////////////////////////// // A Remote Player has joined the game. bool MultiPlayerJoin(UDWORD playerIndex) { if(widgGetFromID(psWScreen,IDRET_FORM)) // if ingame. { audio_QueueTrack( ID_CLAN_ENTER ); } if(widgGetFromID(psWScreen,MULTIOP_PLAYERS)) // if in multimenu. { if (!multiRequestUp && (bHosted || ingame.localJoiningInProgress)) { addPlayerBox(true); // update the player box. } } if(NetPlay.isHost) // host responsible for welcoming this player. { // if we've already received a request from this player don't reallocate. if (ingame.JoiningInProgress[playerIndex]) { return true; } ASSERT(NetPlay.playercount <= MAX_PLAYERS, "Too many players!"); // setup data for this player, then broadcast it to the other players. setupNewPlayer(playerIndex); // setup all the guff for that player. sendOptions(); // if skirmish and game full, then kick... if (NetPlay.playercount > game.maxPlayers) { kickPlayer(playerIndex, "the game is already full.", ERROR_FULL); } // send everyone's stats to the new guy { int i; for (i = 0; i < MAX_PLAYERS; i++) { if (NetPlay.players[i].allocated) { setMultiStats(i, getMultiStats(i), false); } } } } return true; }
// //////////////////////////////////////////////////////////////////////////// // A remote player has left the game bool MultiPlayerLeave(UDWORD playerIndex) { if (playerIndex >= MAX_PLAYERS) { ASSERT(false, "Bad player number"); return false; } NETlogEntry("Player leaving game", SYNC_FLAG, playerIndex); debug(LOG_NET, "** Player %u [%s], has left the game at game time %u.", playerIndex, getPlayerName(playerIndex), gameTime); if (ingame.localJoiningInProgress) { addConsolePlayerLeftMessage(playerIndex); clearPlayer(playerIndex, false); } else if (NetPlay.isHost) // If hosting, and game has started (not in pre-game lobby screen, that is). { sendPlayerLeft(playerIndex); } game.skDiff[playerIndex] = 0; if (NetPlay.players[playerIndex].wzFile.isSending) { char buf[256]; ssprintf(buf, _("File transfer has been aborted for %d.") , playerIndex); addConsoleMessage(buf, DEFAULT_JUSTIFY, SYSTEM_MESSAGE); debug(LOG_INFO, "=== File has been aborted for %d ===", playerIndex); NetPlay.players[playerIndex].wzFile.isSending = false; NetPlay.players[playerIndex].needFile = false; } if (widgGetFromID(psWScreen, IDRET_FORM)) { audio_QueueTrack(ID_CLAN_EXIT); } // fire script callback to reassign skirmish players. CBPlayerLeft = playerIndex; eventFireCallbackTrigger((TRIGGER_TYPE)CALL_PLAYERLEFT); triggerEventPlayerLeft(playerIndex); netPlayersUpdated = true; return true; }
bool recvGift(NETQUEUE queue) { uint8_t type, from, to; int audioTrack; uint32_t droidID; NETbeginDecode(queue, GAME_GIFT); NETuint8_t(&type); NETuint8_t(&from); NETuint8_t(&to); NETuint32_t(&droidID); NETend(); // Handle the gift depending on what it is switch (type) { case RADAR_GIFT: audioTrack = ID_SENSOR_DOWNLOAD; giftRadar(from, to, false); break; case DROID_GIFT: audioTrack = ID_UNITS_TRANSFER; recvGiftDroids(from, to, droidID); break; case RESEARCH_GIFT: audioTrack = ID_TECHNOLOGY_TRANSFER; giftResearch(from, to, false); break; case POWER_GIFT: audioTrack = ID_POWER_TRANSMIT; giftPower(from, to, droidID, false); break; default: debug(LOG_ERROR, "recvGift: Unknown Gift recvd"); return false; break; } // If we are on the recieving end play an audio alert if (to == selectedPlayer) { audio_QueueTrack(audioTrack); } return true; }
bool sendGift(uint8_t type, uint8_t to) { int audioTrack; switch (type) { case RADAR_GIFT: audioTrack = ID_SENSOR_DOWNLOAD; giftRadar(selectedPlayer, to, true); break; case DROID_GIFT: audioTrack = ID_UNITS_TRANSFER; sendGiftDroids(selectedPlayer, to); break; case RESEARCH_GIFT: audioTrack = ID_TECHNOLOGY_TRANSFER; giftResearch(selectedPlayer, to, true); break; case POWER_GIFT: audioTrack = ID_POWER_TRANSMIT; giftPower(selectedPlayer, to, 0, true); break; case AUTOGAME_GIFT: giftAutoGame(selectedPlayer, to, true); return true; break; case STRUCTURE_GIFT: // not implemented default: debug(LOG_ERROR, "Unknown Gift sent"); return false; break; } // Play the appropriate audio track audio_QueueTrack(audioTrack); return true; }
// /////////////////////////////////////////////////////////////// void processMultiPlayerArtifacts(void) { static UDWORD lastCall; FEATURE *pF,*pFN; UDWORD x,y,pl; Position position; BOOL found=false; // only do this every now and again. if(lastCall > gameTime)lastCall= 0; if ( (gameTime - lastCall) <2000) { return; } lastCall = gameTime; for(pF = apsFeatureLists[0]; pF ; pF = pFN) { pFN = pF->psNext; // artifacts if(pF->psStats->subType == FEAT_GEN_ARTE) { found = objectInRange((BASE_OBJECT *)apsDroidLists[selectedPlayer], pF->pos.x, pF->pos.y, (TILE_UNITS+(TILE_UNITS/3)) ); if(found) { position = pF->pos; // Add an effect addEffect(&position,EFFECT_EXPLOSION,EXPLOSION_TYPE_DISCOVERY,false,NULL,false); x = pF->pos.x; y = pF->pos.y; pl= pF->player; removeFeature(pF); // remove artifact+ send info. giftArtifact(pl,x,y); // reward player. pF->player = 0; audio_QueueTrack( ID_SOUND_ARTIFACT_RECOVERED ); } } } }
// /////////////////////////////////////////////////////////////// bool pickupArtefact(int toPlayer, int fromPlayer) { if (fromPlayer < MAX_PLAYERS && bMultiPlayer) { PLAYER_RESEARCH *pR = asPlayerResList[toPlayer]; PLAYER_RESEARCH *pO = asPlayerResList[fromPlayer]; int topic; for (topic = numResearch - 1; topic >= 0; topic--) { if (IsResearchCompleted(&pO[topic]) && !IsResearchPossible(&pR[topic])) { // Make sure the topic can be researched if (asResearch[topic].researchPower && asResearch[topic].researchPoints) { MakeResearchPossible(&pR[topic]); if (toPlayer == selectedPlayer) { CONPRINTF(ConsoleString,(ConsoleString,_("You Discover Blueprints For %s"), getName(asResearch[topic].pName))); } break; } // Invalid topic else { debug(LOG_WARNING, "%s is a invalid research topic?", getName(asResearch[topic].pName)); } } } audio_QueueTrack(ID_SOUND_ARTIFACT_RECOVERED); return true; } return false; }
/* process the results of a completed research topic */ void researchResult(UDWORD researchIndex, UBYTE player, bool bDisplay, STRUCTURE *psResearchFacility, bool bTrigger) { RESEARCH *pResearch = &asResearch[researchIndex]; MESSAGE *pMessage; //the message gets sent to console char consoleMsg[MAX_RESEARCH_MSG_SIZE]; ASSERT_OR_RETURN(, researchIndex < asResearch.size(), "Invalid research index %u", researchIndex); syncDebug("researchResult(%u, %u, …)", researchIndex, player); MakeResearchCompleted(&asPlayerResList[player][researchIndex]); //check for structures to be made available for (unsigned short pStructureResult : pResearch->pStructureResults) { if (apStructTypeLists[player][pStructureResult] != REDUNDANT) { apStructTypeLists[player][pStructureResult] = AVAILABLE; } } //check for structures to be made redundant for (unsigned short pRedStruct : pResearch->pRedStructs) { apStructTypeLists[player][pRedStruct] = REDUNDANT; } //check for component replacement if (!pResearch->componentReplacement.empty()) { for (auto &ri : pResearch->componentReplacement) { COMPONENT_STATS *pOldComp = ri.pOldComponent; replaceComponent(ri.pNewComponent, pOldComp, player); apCompLists[player][pOldComp->compType][pOldComp->index] = REDUNDANT; } } //check for artefacts to be made available for (auto &componentResult : pResearch->componentResults) { //determine the type of artefact COMPONENT_TYPE type = componentResult->compType; //set the component state to AVAILABLE int compInc = componentResult->index; if (apCompLists[player][type][compInc] != REDUNDANT) { apCompLists[player][type][compInc] = AVAILABLE; } //check for default sensor if (type == COMP_SENSOR && (asSensorStats + compInc)->location == LOC_DEFAULT) { aDefaultSensor[player] = compInc; } //check for default ECM else if (type == COMP_ECM && (asECMStats + compInc)->location == LOC_DEFAULT) { aDefaultECM[player] = compInc; } //check for default Repair else if (type == COMP_REPAIRUNIT && (asRepairStats + compInc)->location == LOC_DEFAULT) { aDefaultRepair[player] = compInc; enableSelfRepair(player); } } //check for artefacts to be made redundant for (auto &pRedArtefact : pResearch->pRedArtefacts) { COMPONENT_TYPE type = pRedArtefact->compType; apCompLists[player][type][pRedArtefact->index] = REDUNDANT; } //Add message to player's list if Major Topic if ((pResearch->techCode == TC_MAJOR) && bDisplay) { //only play sound if major topic if (player == selectedPlayer) { audio_QueueTrack(ID_SOUND_MAJOR_RESEARCH); } //check there is viewdata for the research topic - just don't add message if not! if (pResearch->pViewData != nullptr) { pMessage = addMessage(MSG_RESEARCH, false, player); if (pMessage != nullptr) { pMessage->pViewData = pResearch->pViewData; jsDebugMessageUpdate(); } } } else if (player == selectedPlayer && bDisplay) { audio_QueueTrack(ID_SOUND_RESEARCH_COMPLETED); } if (player == selectedPlayer && bDisplay) { //add console text message if (pResearch->pViewData != nullptr) { snprintf(consoleMsg, MAX_RESEARCH_MSG_SIZE, _("Research completed: %s"), _(pResearch->pViewData->textMsg[0].toUtf8().c_str())); addConsoleMessage(consoleMsg, LEFT_JUSTIFY, SYSTEM_MESSAGE); } else { addConsoleMessage(_("Research Completed"), LEFT_JUSTIFY, SYSTEM_MESSAGE); } } if (psResearchFacility) { psResearchFacility->pFunctionality->researchFacility.psSubject = nullptr; // Make sure topic is cleared } if ((bMultiPlayer || player == selectedPlayer) && bTrigger) { psCBLastResearch = pResearch; // Fun with pointers. Throw them into some random global variable, and get Nexus to absorb them. CBResFacilityOwner = player; psCBLastResStructure = psResearchFacility; eventFireCallbackTrigger((TRIGGER_TYPE)CALL_RESEARCHCOMPLETED); psCBLastResStructure = nullptr; CBResFacilityOwner = -1; psCBLastResearch = nullptr; } triggerEventResearched(pResearch, psResearchFacility, player); }
void formAlliance(uint8_t p1, uint8_t p2, BOOL prop, BOOL allowAudio, BOOL allowNotification) { DROID *psDroid; char tm1[128]; // Don't add message if already allied if (bMultiPlayer && alliances[p1][p2] != ALLIANCE_FORMED && allowNotification) { sstrcpy(tm1, getPlayerName(p1)); CONPRINTF(ConsoleString,(ConsoleString,_("%s Forms An Alliance With %s"),tm1,getPlayerName(p2))); } alliances[p1][p2] = ALLIANCE_FORMED; alliances[p2][p1] = ALLIANCE_FORMED; if (game.alliance == ALLIANCES_TEAMS) // this is for shared vision only { alliancebits[p1] |= 1 << p2; alliancebits[p2] |= 1 << p1; } if (allowAudio && (p1 == selectedPlayer || p2== selectedPlayer)) { audio_QueueTrack(ID_ALLIANCE_ACC); } if (bMultiMessages && prop) { sendAlliance(p1, p2, ALLIANCE_FORMED, false); } // Not campaign and alliances are transitive if (game.alliance == ALLIANCES_TEAMS) { giftRadar(p1, p2, false); giftRadar(p2, p1, false); } // Clear out any attacking orders turnOffMultiMsg(true); for (psDroid = apsDroidLists[p1]; psDroid; psDroid = psDroid->psNext) // from -> to { if (psDroid->order == DORDER_ATTACK && psDroid->psTarget && psDroid->psTarget->player == p2) { orderDroid(psDroid, DORDER_STOP); } } for (psDroid = apsDroidLists[p2]; psDroid; psDroid = psDroid->psNext) // to -> from { if (psDroid->order == DORDER_ATTACK && psDroid->psTarget && psDroid->psTarget->player == p1) { orderDroid(psDroid,DORDER_STOP); } } turnOffMultiMsg(false); }