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); } } }
bool recvPowerCheck(NETQUEUE queue) { uint8_t player; uint32_t synchTime; int64_t power; NETbeginDecode(queue, GAME_CHECK_POWER); NETuint8_t(&player); NETuint32_t(&synchTime); NETint64_t(&power); NETend(); if (powerCheckLastSent != synchTime) { debug(LOG_ERROR, "Power synch check out of synch!"); return false; } if (player >= MAX_PLAYERS) { debug(LOG_ERROR, "Bad GAME_CHECK_POWER packet: player is %d : %s", (int)player, isHumanPlayer(player) ? "Human" : "AI"); return false; } if (power != powerCheckLastPower[player]) { int64_t powerFrom = getPrecisePower(player); int64_t powerTo = powerFrom + power - powerCheckLastPower[player]; debug(LOG_SYNC, "GAME_CHECK_POWER: Adjusting power for player %d (%s) from %lf to %lf", (int)player, isHumanPlayer(player) ? "Human" : "AI", powerFrom/4294967296., powerTo/4294967296.); syncDebug("Adjusting power for player %d (%s) from %"PRId64" to %"PRId64"", (int)player, isHumanPlayer(player) ? "Human" : "AI", powerFrom, powerTo); setPrecisePower(player, powerTo); } return true; }
/* 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); }
// //////////////////////////////////////////////////////////////////////////// // receive droid information form other players. BOOL recvDroidInfo(NETQUEUE queue) { NETbeginDecode(queue, GAME_DROIDINFO); { QueuedDroidInfo info; memset(&info, 0x00, sizeof(info)); NETQueuedDroidInfo(&info); STRUCTURE_STATS *psStats = NULL; if (info.order == DORDER_BUILD || info.order == DORDER_LINEBUILD) { // Find structure target for (unsigned typeIndex = 0; typeIndex < numStructureStats; typeIndex++) { if (asStructureStats[typeIndex].ref == info.structRef) { psStats = asStructureStats + typeIndex; break; } } } if (info.subType) { syncDebug("Order=%s,%d(%d)", getDroidOrderName(info.order), info.destId, info.destType); } else { syncDebug("Order=%s,(%d,%d)", getDroidOrderName(info.order), info.x, info.y); } DROID_ORDER_DATA sOrder; memset(&sOrder, 0x00, sizeof(sOrder)); sOrder.order = info.order; sOrder.x = info.x; sOrder.y = info.y; sOrder.x2 = info.x2; sOrder.y2 = info.y2; sOrder.direction = info.direction; sOrder.psObj = processDroidTarget(info.destType, info.destId); sOrder.psStats = (BASE_STATS *)psStats; uint32_t num = 0; NETuint32_t(&num); for (unsigned n = 0; n < num; ++n) { // Get the next droid ID which is being given this order. uint32_t deltaDroidId = 0; NETuint32_t(&deltaDroidId); info.droidId += deltaDroidId; DROID *psDroid = NULL; if (!IdToDroid(info.droidId, ANYPLAYER, &psDroid)) { debug(LOG_NEVER, "Packet from %d refers to non-existent droid %u, [%s : p%d]", queue.index, info.droidId, isHumanPlayer(info.player) ? "Human" : "AI", info.player); syncDebug("Droid %d missing", info.droidId); continue; // Can't find the droid, so skip this droid. } CHECK_DROID(psDroid); syncDebugDroid(psDroid, '<'); psDroid->waitingForOwnReceiveDroidInfoMessage = false; /* * If the current order not is a command order and we are not a * commander yet are in the commander group remove us from it. */ if (hasCommander(psDroid)) { grpLeave(psDroid->psGroup, psDroid); } if (sOrder.psObj != TargetMissing) // Only do order if the target didn't die. { orderDroidBase(psDroid, &sOrder); } syncDebugDroid(psDroid, '>'); CHECK_DROID(psDroid); } } NETend(); return true; }
QNetworkReply* QWebDAV::sendWebdavRequest(QUrl url, DAVType type, QByteArray verb, QIODevice *data, QString extra, QString extra2) { // Prepare the network request and headers QNetworkRequest request; QNetworkReply *reply; request.setUrl(url); request.setRawHeader(QByteArray("Host"),url.host().toUtf8()); qDebug() << "Sending " << type << "with url: " << url; // First, find out what type we want if( type == DAVLIST ) { // A PROPFIND can include 0, 1 or infinity QString depthString = extra; request.setRawHeader(QByteArray("Depth"), QByteArray(depthString.toLatin1())); request.setAttribute(QNetworkRequest::User, QVariant("list")); request.setAttribute(QNetworkRequest::Attribute(QNetworkRequest::User+ ATTDATA) ,QVariant(mRequestNumber)); request.setRawHeader(QByteArray("Content-Type"), QByteArray("text/xml; charset=\"utf-8\"")); request.setRawHeader(QByteArray("Content-Length"),QByteArray("99999")); reply = sendCustomRequest(request,verb,data); } else if ( type == DAVGET ) { request.setRawHeader("User-Agent", "QWebDAV 0.1"); request.setAttribute(QNetworkRequest::User, QVariant("get")); reply = QNetworkAccessManager::get(request); connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>))); } else if ( type == DAVPUT ) { request.setAttribute(QNetworkRequest::User, QVariant("put")); if ( mRequestFile.value(mRequestNumber) ) { request.setAttribute(QNetworkRequest::Attribute( QNetworkRequest::User+ATTFILE) ,QVariant(mRequestNumber)); request.setAttribute(QNetworkRequest::Attribute( QNetworkRequest::User+ATTPREFIX) ,QVariant(extra.toLatin1())); if( extra2 != 0 ) { // We were given a lock token. request.setRawHeader(QByteArray("If"), QByteArray(extra2.toLatin1())); } } else { request.setAttribute(QNetworkRequest::Attribute( QNetworkRequest::User+ATTDATA) ,QVariant(mRequestNumber)); } reply = QNetworkAccessManager::put(request,data); } else if ( type == DAVMKCOL ) { request.setAttribute(QNetworkRequest::User, QVariant("mkcol")); reply = sendCustomRequest(request,verb,0); } else if ( type == DAVDELETE ) { request.setAttribute(QNetworkRequest::User, QVariant("delete")); reply = sendCustomRequest(request, verb,0); } else if ( type == DAVMOVE ) { request.setAttribute(QNetworkRequest::User, QVariant("move")); request.setRawHeader(QByteArray("Destination"), QByteArray(extra.toLatin1())); request.setRawHeader(QByteArray("Overwrite"), QByteArray("T")); if( extra2 != 0 ) { // We were given (a) lock token(s). request.setRawHeader(QByteArray("If"), QByteArray(extra2.toLatin1())); request.setAttribute(QNetworkRequest::Attribute(QNetworkRequest::User+ ATTLOCKTYPE) ,QVariant(extra.replace(mHostname,"").toLatin1())); } reply = sendCustomRequest(request, verb,0); } else if ( type == DAVLOCK) { request.setAttribute(QNetworkRequest::User, QVariant("lock")); // We don't bother setting a timeout, apparently the system defaults // to 5 minutes anyway. request.setAttribute(QNetworkRequest::Attribute(QNetworkRequest::User+ ATTDATA) ,QVariant(mRequestNumber)); request.setAttribute(QNetworkRequest::Attribute(QNetworkRequest::User+ ATTLOCKTYPE) ,QVariant(extra)); reply = sendCustomRequest(request,verb,data); } else if ( type == DAVUNLOCK) { QString token = "<"+extra+">"; request.setAttribute(QNetworkRequest::User, QVariant("unlock")); request.setRawHeader(QByteArray("Lock-Token"), QByteArray(token.toLatin1())); reply = sendCustomRequest(request,verb,0); } else { syncDebug() << "Error! DAV Request of type " << type << " is not known!"; reply = 0; } // Connect the finished() signal! connectReplyFinished(reply); return reply; }
static FPATH_RETVAL fpathRoute(MOVE_CONTROL *psMove, unsigned id, int startX, int startY, int tX, int tY, PROPULSION_TYPE propulsionType, DROID_TYPE droidType, FPATH_MOVETYPE moveType, int owner, bool acceptNearest, StructureBounds const &dstStructure) { objTrace(id, "called(*,id=%d,sx=%d,sy=%d,ex=%d,ey=%d,prop=%d,type=%d,move=%d,owner=%d)", id, startX, startY, tX, tY, (int)propulsionType, (int)droidType, (int)moveType, owner); if (!worldOnMap(startX, startY) || !worldOnMap(tX, tY)) { debug(LOG_ERROR, "Droid trying to find path to/from invalid location (%d %d) -> (%d %d).", startX, startY, tX, tY); objTrace(id, "Invalid start/end."); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // don't have to do anything if already there if (startX == tX && startY == tY) { // return failed to stop them moving anywhere objTrace(id, "Tried to move nowhere"); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // Check if waiting for a result while (psMove->Status == MOVEWAITROUTE) { objTrace(id, "Checking if we have a path yet"); auto const &I = pathResults.find(id); ASSERT(I != pathResults.end(), "Missing path result promise"); PATHRESULT result = I->second.get(); ASSERT(result.retval != FPR_OK || result.sMove.asPath, "Ok result but no path in list"); // Copy over select fields - preserve others psMove->destination = result.sMove.destination; psMove->numPoints = result.sMove.numPoints; bool correctDestination = tX == result.originalDest.x && tY == result.originalDest.y; psMove->pathIndex = 0; psMove->Status = MOVENAVIGATE; free(psMove->asPath); psMove->asPath = result.sMove.asPath; FPATH_RETVAL retval = result.retval; ASSERT(retval != FPR_OK || psMove->asPath, "Ok result but no path after copy"); ASSERT(retval != FPR_OK || psMove->numPoints > 0, "Ok result but path empty after copy"); // Remove it from the result list pathResults.erase(id); objTrace(id, "Got a path to (%d, %d)! Length=%d Retval=%d", psMove->destination.x, psMove->destination.y, psMove->numPoints, (int)retval); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = %d, path[%d] = %08X->(%d, %d)", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner, retval, psMove->numPoints, ~crcSumVector2i(0, psMove->asPath, psMove->numPoints), psMove->destination.x, psMove->destination.y); if (!correctDestination) { goto queuePathfinding; // Seems we got the result of an old pathfinding job for this droid, so need to pathfind again. } return retval; } queuePathfinding: // We were not waiting for a result, and found no trivial path, so create new job and start waiting PATHJOB job; job.origX = startX; job.origY = startY; job.droidID = id; job.destX = tX; job.destY = tY; job.dstStructure = dstStructure; job.droidType = droidType; job.propulsion = propulsionType; job.moveType = moveType; job.owner = owner; job.acceptNearest = acceptNearest; job.deleted = false; fpathSetBlockingMap(&job); debug(LOG_NEVER, "starting new job for droid %d 0x%x", id, id); // Clear any results or jobs waiting already. It is a vital assumption that there is only one // job or result for each droid in the system at any time. fpathRemoveDroidData(id); packagedPathJob task([job]() { return fpathExecute(job); }); pathResults[id] = task.get_future(); // Add to end of list wzMutexLock(fpathMutex); bool isFirstJob = pathJobs.empty(); pathJobs.push_back(std::move(task)); wzMutexUnlock(fpathMutex); if (isFirstJob) { wzSemaphorePost(fpathSemaphore); // Wake up processing thread. } objTrace(id, "Queued up a path-finding request to (%d, %d), at least %d items earlier in queue", tX, tY, isFirstJob); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_WAIT", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_WAIT; // wait while polling result queue }
static void gameStateUpdate() { // Can't dump isHumanPlayer, since it causes spurious desynch dumps when players leave. // TODO isHumanPlayer should probably be synchronised, since the game state seems to depend on it, so there might also be a risk of real desynchs when players leave. //syncDebug("map = \"%s\", humanPlayers = %d %d %d %d %d %d %d %d", game.map, isHumanPlayer(0), isHumanPlayer(1), isHumanPlayer(2), isHumanPlayer(3), isHumanPlayer(4), isHumanPlayer(5), isHumanPlayer(6), isHumanPlayer(7)); syncDebug("map = \"%s\"", game.map); // Actually send pending droid orders. sendQueuedDroidInfo(); sendPlayerGameTime(); gameSRand(gameTime); // Brute force way of synchronising the random number generator, which can't go out of synch. if (!paused && !scriptPaused() && !editPaused()) { /* Update the event system */ if (!bInTutorial) { eventProcessTriggers(gameTime/SCR_TICKRATE); } else { eventProcessTriggers(realTime/SCR_TICKRATE); } updateScripts(); } // Update abandoned structures handleAbandonedStructures(); // Update the visibility change stuff visUpdateLevel(); // Put all droids/structures/features into the grid. gridReset(); // Check which objects are visible. processVisibility(); // Update the map. mapUpdate(); //update the findpath system fpathUpdate(); // update the cluster system clusterUpdate(); // update the command droids cmdDroidUpdate(); if(getDrivingStatus()) { driveUpdate(); } fireWaitingCallbacks(); //Now is the good time to fire waiting callbacks (since interpreter is off now) for (unsigned i = 0; i < MAX_PLAYERS; i++) { //update the current power available for a player updatePlayerPower(i); //set the flag for each player setHQExists(false, i); setSatUplinkExists(false, i); numCommandDroids[i] = 0; numConstructorDroids[i] = 0; numDroids[i]=0; numTransporterDroids[i]=0; DROID *psNext; for (DROID *psCurr = apsDroidLists[i]; psCurr != NULL; psCurr = psNext) { // Copy the next pointer - not 100% sure if the droid could get destroyed but this covers us anyway psNext = psCurr->psNext; droidUpdate(psCurr); // update the droid counts numDroids[i]++; switch (psCurr->droidType) { case DROID_COMMAND: numCommandDroids[i] += 1; break; case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: numConstructorDroids[i] += 1; break; case DROID_TRANSPORTER: if( (psCurr->psGroup != NULL) ) { DROID *psDroid = NULL; numTransporterDroids[i] += psCurr->psGroup->refCount-1; // and count the units inside it... for (psDroid = psCurr->psGroup->psList; psDroid != NULL && psDroid != psCurr; psDroid = psDroid->psGrpNext) { if (psDroid->droidType == DROID_CYBORG_CONSTRUCT || psDroid->droidType == DROID_CONSTRUCT) { numConstructorDroids[i] += 1; } if (psDroid->droidType == DROID_COMMAND) { numCommandDroids[i] += 1; } } } break; default: break; } } numMissionDroids[i]=0; for (DROID *psCurr = mission.apsDroidLists[i]; psCurr != NULL; psCurr = psNext) { /* Copy the next pointer - not 100% sure if the droid could get destroyed but this covers us anyway */ psNext = psCurr->psNext; missionDroidUpdate(psCurr); numMissionDroids[i]++; switch (psCurr->droidType) { case DROID_COMMAND: numCommandDroids[i] += 1; break; case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: numConstructorDroids[i] += 1; break; case DROID_TRANSPORTER: if( (psCurr->psGroup != NULL) ) { numTransporterDroids[i] += psCurr->psGroup->refCount-1; } break; default: break; } } for (DROID *psCurr = apsLimboDroids[i]; psCurr != NULL; psCurr = psNext) { /* Copy the next pointer - not 100% sure if the droid could get destroyed but this covers us anyway */ psNext = psCurr->psNext; // count the type of units switch (psCurr->droidType) { case DROID_COMMAND: numCommandDroids[i] += 1; break; case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: numConstructorDroids[i] += 1; break; default: break; } } // FIXME: These for-loops are code duplicationo /*set this up AFTER droidUpdate so that if trying to building a new one, we know whether one exists already*/ setLasSatExists(false, i); STRUCTURE *psNBuilding; for (STRUCTURE *psCBuilding = apsStructLists[i]; psCBuilding != NULL; psCBuilding = psNBuilding) { /* Copy the next pointer - not 100% sure if the structure could get destroyed but this covers us anyway */ psNBuilding = psCBuilding->psNext; structureUpdate(psCBuilding, false); //set animation flag if (psCBuilding->pStructureType->type == REF_HQ && psCBuilding->status == SS_BUILT) { setHQExists(true, i); } if (psCBuilding->pStructureType->type == REF_SAT_UPLINK && psCBuilding->status == SS_BUILT) { setSatUplinkExists(true, i); } //don't wait for the Las Sat to be built - can't build another if one is partially built if (asWeaponStats[psCBuilding->asWeaps[0].nStat]. weaponSubClass == WSC_LAS_SAT) { setLasSatExists(true, i); } } for (STRUCTURE *psCBuilding = mission.apsStructLists[i]; psCBuilding != NULL; psCBuilding = psNBuilding) { /* Copy the next pointer - not 100% sure if the structure could get destroyed but this covers us anyway. It shouldn't do since its not even on the map!*/ psNBuilding = psCBuilding->psNext; structureUpdate(psCBuilding, true); // update for mission if (psCBuilding->pStructureType->type == REF_HQ && psCBuilding->status == SS_BUILT) { setHQExists(true, i); } if (psCBuilding->pStructureType->type == REF_SAT_UPLINK && psCBuilding->status == SS_BUILT) { setSatUplinkExists(true, i); } //don't wait for the Las Sat to be built - can't build another if one is partially built if (asWeaponStats[psCBuilding->asWeaps[0].nStat]. weaponSubClass == WSC_LAS_SAT) { setLasSatExists(true, i); } } } missionTimerUpdate(); proj_UpdateAll(); FEATURE *psNFeat; for (FEATURE *psCFeat = apsFeatureLists[0]; psCFeat; psCFeat = psNFeat) { psNFeat = psCFeat->psNext; featureUpdate(psCFeat); } objmemUpdate(); // Do completely useless stuff. if (!isInSync()) { sendCheck(); // send some pointless checking info if we're doomed anyway } // Must end update, since we may or may not have ticked, and some message queue processing code may vary depending on whether it's in an update. gameTimeUpdateEnd(); }
// Carry out the various counting operations we perform each loop void countUpdate(bool synch) { for (unsigned i = 0; i < MAX_PLAYERS; i++) { //set the flag for each player setSatUplinkExists(false, i); numCommandDroids[i] = 0; numConstructorDroids[i] = 0; numDroids[i] = 0; numMissionDroids[i] = 0; numTransporterDroids[i] = 0; for (DROID *psCurr = apsDroidLists[i]; psCurr != nullptr; psCurr = psCurr->psNext) { numDroids[i]++; switch (psCurr->droidType) { case DROID_COMMAND: numCommandDroids[i] += 1; break; case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: numConstructorDroids[i] += 1; break; case DROID_TRANSPORTER: case DROID_SUPERTRANSPORTER: if ((psCurr->psGroup != nullptr)) { DROID *psDroid = nullptr; numTransporterDroids[i] += psCurr->psGroup->refCount - 1; // and count the units inside it... for (psDroid = psCurr->psGroup->psList; psDroid != nullptr && psDroid != psCurr; psDroid = psDroid->psGrpNext) { if (psDroid->droidType == DROID_CYBORG_CONSTRUCT || psDroid->droidType == DROID_CONSTRUCT) { numConstructorDroids[i] += 1; } if (psDroid->droidType == DROID_COMMAND) { numCommandDroids[i] += 1; } } } break; default: break; } } for (DROID *psCurr = mission.apsDroidLists[i]; psCurr != nullptr; psCurr = psCurr->psNext) { numMissionDroids[i]++; switch (psCurr->droidType) { case DROID_COMMAND: numCommandDroids[i] += 1; break; case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: numConstructorDroids[i] += 1; break; case DROID_TRANSPORTER: case DROID_SUPERTRANSPORTER: if ((psCurr->psGroup != nullptr)) { numTransporterDroids[i] += psCurr->psGroup->refCount - 1; } break; default: break; } } for (DROID *psCurr = apsLimboDroids[i]; psCurr != nullptr; psCurr = psCurr->psNext) { // count the type of units switch (psCurr->droidType) { case DROID_COMMAND: numCommandDroids[i] += 1; break; case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: numConstructorDroids[i] += 1; break; default: break; } } // FIXME: These for-loops are code duplicationo setLasSatExists(false, i); for (STRUCTURE *psCBuilding = apsStructLists[i]; psCBuilding != nullptr; psCBuilding = psCBuilding->psNext) { if (psCBuilding->pStructureType->type == REF_SAT_UPLINK && psCBuilding->status == SS_BUILT) { setSatUplinkExists(true, i); } //don't wait for the Las Sat to be built - can't build another if one is partially built if (asWeaponStats[psCBuilding->asWeaps[0].nStat].weaponSubClass == WSC_LAS_SAT) { setLasSatExists(true, i); } } for (STRUCTURE *psCBuilding = mission.apsStructLists[i]; psCBuilding != nullptr; psCBuilding = psCBuilding->psNext) { if (psCBuilding->pStructureType->type == REF_SAT_UPLINK && psCBuilding->status == SS_BUILT) { setSatUplinkExists(true, i); } //don't wait for the Las Sat to be built - can't build another if one is partially built if (asWeaponStats[psCBuilding->asWeaps[0].nStat].weaponSubClass == WSC_LAS_SAT) { setLasSatExists(true, i); } } if (synch) { syncDebug("counts[%d] = {droid: %d, command: %d, constructor: %d, mission: %d, transporter: %d}", i, numDroids[i], numCommandDroids[i], numConstructorDroids[i], numMissionDroids[i], numTransporterDroids[i]); } } }
// //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // Recv Messages. Get a message and dispatch to relevant function. bool recvMessage(void) { NETQUEUE queue; uint8_t type; while (NETrecvNet(&queue, &type) || NETrecvGame(&queue, &type)) // for all incoming messages. { bool processedMessage1 = false; bool processedMessage2 = false; if (queue.queueType == QUEUE_GAME) { syncDebug("Processing player %d, message %s", queue.index, messageTypeToString(type)); } // messages only in game. if(!ingame.localJoiningInProgress) { processedMessage1 = true; switch(type) { case GAME_DROIDINFO: //droid update info recvDroidInfo(queue); break; case NET_TEXTMSG: // simple text message recvTextMessage(queue); break; case NET_DATA_CHECK: recvDataCheck(queue); break; case NET_AITEXTMSG: //multiplayer AI text message recvTextMessageAI(queue); break; case NET_BEACONMSG: //beacon (blip) message recvBeacon(queue); break; case GAME_DROIDDISEMBARK: recvDroidDisEmbark(queue); //droid has disembarked from a Transporter break; case GAME_GIFT: // an alliance gift from one player to another. recvGift(queue); break; case GAME_LASSAT: recvLasSat(queue); break; case GAME_DEBUG_MODE: recvProcessDebugMappings(queue); break; case GAME_DEBUG_ADD_DROID: recvDroid(queue); break; case GAME_DEBUG_ADD_STRUCTURE: recvBuildFinished(queue); break; case GAME_DEBUG_ADD_FEATURE: recvMultiPlayerFeature(queue); break; case GAME_DEBUG_REMOVE_DROID: recvDestroyDroid(queue); break; case GAME_DEBUG_REMOVE_STRUCTURE: recvDestroyStructure(queue); break; case GAME_DEBUG_REMOVE_FEATURE: recvDestroyFeature(queue); break; case GAME_DEBUG_FINISH_RESEARCH: recvResearch(queue); break; default: processedMessage1 = false; break; } } // messages usable all the time processedMessage2 = true; switch(type) { case GAME_TEMPLATE: // new template recvTemplate(queue); break; case GAME_TEMPLATEDEST: // template destroy recvDestroyTemplate(queue); break; case NET_PING: // diagnostic ping msg. recvPing(queue); break; case NET_OPTIONS: recvOptions(queue); break; case NET_PLAYER_DROPPED: // remote player got disconnected { uint32_t player_id; NETbeginDecode(queue, NET_PLAYER_DROPPED); { NETuint32_t(&player_id); } NETend(); if (whosResponsible(player_id) != queue.index && queue.index != NET_HOST_ONLY) { HandleBadParam("NET_PLAYER_DROPPED given incorrect params.", player_id, queue.index); break; } debug(LOG_INFO,"** player %u has dropped!", player_id); if (NetPlay.players[player_id].allocated) { MultiPlayerLeave(player_id); // get rid of their stuff NET_InitPlayer(player_id, false); } NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, player_id); break; } case NET_PLAYERRESPONDING: // remote player is now playing { uint32_t player_id; resetReadyStatus(false); NETbeginDecode(queue, NET_PLAYERRESPONDING); // the player that has just responded NETuint32_t(&player_id); NETend(); if (player_id >= MAX_PLAYERS) { debug(LOG_ERROR, "Bad NET_PLAYERRESPONDING received, ID is %d", (int)player_id); break; } // This player is now with us! ingame.JoiningInProgress[player_id] = false; break; } // FIXME: the next 4 cases might not belong here --check (we got two loops for this) case NET_COLOURREQUEST: recvColourRequest(queue); break; case NET_POSITIONREQUEST: recvPositionRequest(queue); break; case NET_TEAMREQUEST: recvTeamRequest(queue); break; case NET_READY_REQUEST: recvReadyRequest(queue); // if hosting try to start the game if everyone is ready if(NetPlay.isHost && multiplayPlayersReady(false)) { startMultiplayerGame(); } break; case GAME_ALLIANCE: recvAlliance(queue, true); break; case NET_KICK: // in-game kick message { uint32_t player_id; char reason[MAX_KICK_REASON]; LOBBY_ERROR_TYPES KICK_TYPE = ERROR_NOERROR; NETbeginDecode(queue, NET_KICK); NETuint32_t(&player_id); NETstring(reason, MAX_KICK_REASON); NETenum(&KICK_TYPE); NETend(); if (player_id == NET_HOST_ONLY) { char buf[250]= {'\0'}; ssprintf(buf, "Player %d (%s : %s) tried to kick %u", (int) queue.index, NetPlay.players[queue.index].name, NetPlay.players[queue.index].IPtextAddress, player_id); NETlogEntry(buf, SYNC_FLAG, 0); debug(LOG_ERROR, "%s", buf); if (NetPlay.isHost) { NETplayerKicked((unsigned int) queue.index); } break; } else if (selectedPlayer == player_id) // we've been told to leave. { debug(LOG_ERROR, "You were kicked because %s", reason); setPlayerHasLost(true); } else { debug(LOG_NET, "Player %d was kicked: %s", player_id, reason); NETplayerKicked(player_id); } break; } case GAME_RESEARCHSTATUS: recvResearchStatus(queue); break; case GAME_STRUCTUREINFO: recvStructureInfo(queue); break; case NET_PLAYER_STATS: recvMultiStats(queue); break; case GAME_PLAYER_LEFT: recvPlayerLeft(queue); break; default: processedMessage2 = false; break; } if (processedMessage1 && processedMessage2) { debug(LOG_ERROR, "Processed %s message twice!", messageTypeToString(type)); } if (!processedMessage1 && !processedMessage2) { debug(LOG_ERROR, "Didn't handle %s message!", messageTypeToString(type)); } NETpop(queue); } return true; }
// //////////////////////////////////////////////////////////////////////////// // receive droid information form other players. bool recvDroidInfo(NETQUEUE queue) { NETbeginDecode(queue, GAME_DROIDINFO); { QueuedDroidInfo info; memset(&info, 0x00, sizeof(info)); NETQueuedDroidInfo(&info); STRUCTURE_STATS *psStats = NULL; if (info.subType == LocOrder && (info.order == DORDER_BUILD || info.order == DORDER_LINEBUILD)) { // Find structure target for (unsigned typeIndex = 0; typeIndex < numStructureStats; typeIndex++) { if (asStructureStats[typeIndex].ref == info.structRef) { psStats = asStructureStats + typeIndex; break; } } } switch (info.subType) { case ObjOrder: syncDebug("Order=%s,%d(%d)", getDroidOrderName(info.order), info.destId, info.destType); break; case LocOrder: syncDebug("Order=%s,(%d,%d)", getDroidOrderName(info.order), info.pos.x, info.pos.y); break; case SecondaryOrder: syncDebug("SecondaryOrder=%d,%08X", (int)info.secOrder, (int)info.secState); break; } DROID_ORDER_DATA sOrder = infoToOrderData(info, psStats); uint32_t num = 0; NETuint32_t(&num); for (unsigned n = 0; n < num; ++n) { // Get the next droid ID which is being given this order. uint32_t deltaDroidId = 0; NETuint32_t(&deltaDroidId); info.droidId += deltaDroidId; DROID *psDroid = IdToDroid(info.droidId, info.player); if (!psDroid) { debug(LOG_NEVER, "Packet from %d refers to non-existent droid %u, [%s : p%d]", queue.index, info.droidId, isHumanPlayer(info.player) ? "Human" : "AI", info.player); syncDebug("Droid %d missing", info.droidId); continue; // Can't find the droid, so skip this droid. } CHECK_DROID(psDroid); syncDebugDroid(psDroid, '<'); switch (info.subType) { case ObjOrder: case LocOrder: /* * If the current order not is a command order and we are not a * commander yet are in the commander group remove us from it. */ if (hasCommander(psDroid)) { psDroid->psGroup->remove(psDroid); } if (sOrder.psObj != TargetMissing) // Only do order if the target didn't die. { if (!info.add) { orderDroidListEraseRange(psDroid, 0, psDroid->listSize + 1); // Clear all non-pending orders, plus the first pending order (which is probably the order we just received). orderDroidBase(psDroid, &sOrder); // Execute the order immediately (even if in the middle of another order. } else { orderDroidAdd(psDroid, &sOrder); // Add the order to the (non-pending) list. Will probably overwrite the corresponding pending order, assuming all pending orders were written to the list. } } break; case SecondaryOrder: // Set the droids secondary order turnOffMultiMsg(true); secondarySetState(psDroid, info.secOrder, info.secState); turnOffMultiMsg(false); break; } syncDebugDroid(psDroid, '>'); CHECK_DROID(psDroid); } } NETend(); return true; }
static FPATH_RETVAL fpathRoute(MOVE_CONTROL *psMove, int id, int startX, int startY, int tX, int tY, PROPULSION_TYPE propulsionType, DROID_TYPE droidType, FPATH_MOVETYPE moveType, int owner, bool acceptNearest) { objTrace(id, "called(*,id=%d,sx=%d,sy=%d,ex=%d,ey=%d,prop=%d,type=%d,move=%d,owner=%d)", id, startX, startY, tX, tY, (int)propulsionType, (int)droidType, (int)moveType, owner); // don't have to do anything if already there if (startX == tX && startY == tY) { // return failed to stop them moving anywhere objTrace(id, "Tried to move nowhere"); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // Check if waiting for a result while (psMove->Status == MOVEWAITROUTE) { objTrace(id, "Checking if we have a path yet"); wzMutexLock(fpathMutex); // psNext should be _declared_ here, after the mutex lock! Used to be a race condition, thanks to -Wdeclaration-after-statement style pseudocompiler compatibility. for (std::list<PATHRESULT>::iterator psResult = pathResults.begin(); psResult != pathResults.end(); ++psResult) { if (psResult->droidID != id) { continue; // Wrong result, try next one. } ASSERT(psResult->retval != FPR_OK || psResult->sMove.asPath, "Ok result but no path in list"); // Copy over select fields - preserve others psMove->destination = psResult->sMove.destination; psMove->numPoints = psResult->sMove.numPoints; psMove->Position = 0; psMove->Status = MOVENAVIGATE; free(psMove->asPath); psMove->asPath = psResult->sMove.asPath; FPATH_RETVAL retval = psResult->retval; ASSERT(retval != FPR_OK || psMove->asPath, "Ok result but no path after copy"); ASSERT(retval != FPR_OK || psMove->numPoints > 0, "Ok result but path empty after copy"); // Remove it from the result list pathResults.erase(psResult); wzMutexUnlock(fpathMutex); objTrace(id, "Got a path to (%d, %d)! Length=%d Retval=%d", psMove->destination.x, psMove->destination.y, psMove->numPoints, (int)retval); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = %d, path[%d] = %08X->(%d, %d)", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner, retval, psMove->numPoints, ~crcSumVector2i(0, psMove->asPath, psMove->numPoints), psMove->destination.x, psMove->destination.y); return retval; } objTrace(id, "No path yet. Waiting."); waitingForResult = true; waitingForResultId = id; wzMutexUnlock(fpathMutex); wzSemaphoreWait(waitingForResultSemaphore); // keep waiting } // We were not waiting for a result, and found no trivial path, so create new job and start waiting PATHJOB job; job.origX = startX; job.origY = startY; job.droidID = id; job.destX = tX; job.destY = tY; job.droidType = droidType; job.propulsion = propulsionType; job.moveType = moveType; job.owner = owner; job.acceptNearest = acceptNearest; job.deleted = false; fpathSetBlockingMap(&job); // Clear any results or jobs waiting already. It is a vital assumption that there is only one // job or result for each droid in the system at any time. fpathRemoveDroidData(id); wzMutexLock(fpathMutex); // Add to end of list bool isFirstJob = pathJobs.empty(); pathJobs.push_back(job); if (isFirstJob) { wzSemaphorePost(fpathSemaphore); // Wake up processing thread. } wzMutexUnlock(fpathMutex); objTrace(id, "Queued up a path-finding request to (%d, %d), at least %d items earlier in queue", tX, tY, isFirstJob); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_WAIT", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_WAIT; // wait while polling result queue }
static void syncDebugEconomy(unsigned player, char ch) { syncDebug("%c economy%u = %"PRId64"", ch, player, asPower[player].currentPower); }
/*called when a Template is deleted in the Design screen*/ void deleteTemplateFromProduction(DROID_TEMPLATE *psTemplate, unsigned player, QUEUE_MODE mode) { STRUCTURE *psStruct; STRUCTURE *psList; //see if any factory is currently using the template for (unsigned i = 0; i < 2; ++i) { psList = NULL; switch (i) { case 0: psList = apsStructLists[player]; break; case 1: psList = mission.apsStructLists[player]; break; } for (psStruct = psList; psStruct != NULL; psStruct = psStruct->psNext) { if (StructIsFactory(psStruct)) { FACTORY *psFactory = &psStruct->pFunctionality->factory; if (psFactory->psAssemblyPoint->factoryInc < asProductionRun[psFactory->psAssemblyPoint->factoryType].size()) { ProductionRun &productionRun = asProductionRun[psFactory->psAssemblyPoint->factoryType][psFactory->psAssemblyPoint->factoryInc]; for (unsigned inc = 0; inc < productionRun.size(); ++inc) { if (productionRun[inc].psTemplate->multiPlayerID == psTemplate->multiPlayerID && mode == ModeQueue) { //just need to erase this production run entry productionRun.erase(productionRun.begin() + inc); --inc; } } } if (psFactory->psSubject == NULL) { continue; } // check not being built in the factory for the template player if (psTemplate->multiPlayerID == psFactory->psSubject->multiPlayerID && mode == ModeImmediate) { syncDebugStructure(psStruct, '<'); syncDebug("Clearing production"); // Clear the factory's subject. psFactory->psSubject = NULL; if (player == productionPlayer) { //check to see if anything left to produce DROID_TEMPLATE *psNextTemplate = factoryProdUpdate(psStruct, NULL); //power is returned by factoryProdAdjust() if (psNextTemplate) { structSetManufacture(psStruct, psNextTemplate, ModeQueue); // ModeQueue because production lists aren't synchronised. } } //tell the interface intManufactureFinished(psStruct); syncDebugStructure(psStruct, '>'); } } } } }
void recvStructureInfo(NETQUEUE queue) { uint8_t player = 0; uint32_t structId = 0; uint32_t templateId = 0; uint8_t structureInfo; STRUCTURE *psStruct; DROID_TEMPLATE *psTempl = NULL; NETbeginDecode(queue, GAME_STRUCTUREINFO); NETuint8_t(&player); NETuint32_t(&structId); NETuint8_t(&structureInfo); if (structureInfo == STRUCTUREINFO_MANUFACTURE) { NETuint32_t(&templateId); if (templateId != 0) { // For autogames, where we want the AI to take us over, our templates are not setup... so let's use any AI's templates. if (!NetPlay.players[player].autoGame) { psTempl = IdToTemplate(templateId, player); } else { psTempl = IdToTemplate(templateId, ANYPLAYER); } if (psTempl == NULL) { debug(LOG_SYNC, "Synch error, don't have tempate id %u, so can't change production of factory %u!", templateId, structId); } } } NETend(); psStruct = IdToStruct(structId, player); syncDebug("player%d,structId%u%c,structureInfo%u,templateId%u%c", player, structId, psStruct == NULL ? '^' : '*', structureInfo, templateId, psTempl == NULL ? '^' : '*'); if (psStruct == NULL) { debug(LOG_SYNC, "Couldn't find structure %u to change production.", structId); return; } if (!canGiveOrdersFor(queue.index, psStruct->player)) { syncDebug("Wrong player."); return; } CHECK_STRUCTURE(psStruct); if (StructIsFactory(psStruct)) { popStatusPending(psStruct->pFunctionality->factory); } else if (psStruct->pStructureType->type == REF_RESEARCH) { popStatusPending(psStruct->pFunctionality->researchFacility); } syncDebugStructure(psStruct, '<'); switch (structureInfo) { case STRUCTUREINFO_MANUFACTURE: structSetManufacture(psStruct, psTempl, ModeImmediate); break; case STRUCTUREINFO_CANCELPRODUCTION: cancelProduction(psStruct, ModeImmediate, false); break; case STRUCTUREINFO_HOLDPRODUCTION: holdProduction(psStruct, ModeImmediate); break; case STRUCTUREINFO_RELEASEPRODUCTION: releaseProduction(psStruct, ModeImmediate); break; case STRUCTUREINFO_HOLDRESEARCH: holdResearch(psStruct, ModeImmediate); break; case STRUCTUREINFO_RELEASERESEARCH: releaseResearch(psStruct, ModeImmediate); break; default: debug(LOG_ERROR, "Invalid structureInfo %d", structureInfo); } syncDebugStructure(psStruct, '>'); CHECK_STRUCTURE(psStruct); }
/* Deals damage to an object * \param psObj object to deal damage to * \param damage amount of damage to deal * \param weaponClass the class of the weapon that deals the damage * \param weaponSubClass the subclass of the weapon that deals the damage * \return < 0 when the dealt damage destroys the object, > 0 when the object survives */ int32_t objDamage(BASE_OBJECT *psObj, unsigned damage, unsigned originalhp, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, bool isDamagePerSecond) { int actualDamage, armour, level = 1, lastHit = psObj->timeLastHit; // If the previous hit was by an EMP cannon and this one is not: // don't reset the weapon class and hit time // (Giel: I guess we need this to determine when the EMP-"shock" is over) if (psObj->lastHitWeapon != WSC_EMP || weaponSubClass == WSC_EMP) { psObj->timeLastHit = gameTime; psObj->lastHitWeapon = weaponSubClass; } // EMP cannons do no damage, if we are one return now if (weaponSubClass == WSC_EMP) { return 0; } // apply game difficulty setting damage = modifyForDifficultyLevel(damage, psObj->player != selectedPlayer); armour = psObj->armour[weaponClass]; if (psObj->type == OBJ_STRUCTURE || psObj->type == OBJ_DROID) { if (psObj->type == OBJ_STRUCTURE && ((STRUCTURE *)psObj)->status == SS_BEING_BUILT) { armour = 0; } // Force sending messages, even if messages were turned off, since a non-synchronised script will execute here. (Aaargh!) bool bMultiMessagesBackup = bMultiMessages; bMultiMessages = bMultiPlayer; clustObjectAttacked((BASE_OBJECT *)psObj); triggerEventAttacked(psObj, g_pProjLastAttacker, lastHit); bMultiMessages = bMultiMessagesBackup; } debug(LOG_ATTACK, "objDamage(%d): body %d armour %d damage: %d", psObj->id, psObj->body, armour, damage); if (psObj->type == OBJ_DROID) { DROID *psDroid = (DROID *)psObj; // Retrieve highest, applicable, experience level level = getDroidEffectiveLevel(psDroid); } // Reduce damage taken by EXP_REDUCE_DAMAGE % for each experience level actualDamage = (damage * (100 - EXP_REDUCE_DAMAGE * level)) / 100; // You always do at least a third of the experience modified damage actualDamage = MAX(actualDamage - armour, actualDamage / 3); // And at least MIN_WEAPON_DAMAGE points actualDamage = MAX(actualDamage, MIN_WEAPON_DAMAGE); if (isDamagePerSecond) { int deltaDamageRate = actualDamage - psObj->burnDamage; if (deltaDamageRate <= 0) { return 0; // Did this much damage already, this tick, so don't do more. } actualDamage = gameTimeAdjustedAverage(deltaDamageRate); psObj->burnDamage += deltaDamageRate; } objTrace(psObj->id, "objDamage: Penetrated %d", actualDamage); syncDebug("damage%u dam%u,o%u,wc%d.%d,ar%d,lev%d,aDam%d,isDps%d", psObj->id, damage, originalhp, weaponClass, weaponSubClass, armour, level, actualDamage, isDamagePerSecond); // for some odd reason, we have 0 hitpoints. if (!originalhp) { ASSERT(originalhp, "original hitpoints are 0 ?"); return -65536; // it is dead } // If the shell did sufficient damage to destroy the object, deal with it and return if (actualDamage >= psObj->body) { return -(int64_t)65536 * psObj->body / originalhp; } // Subtract the dealt damage from the droid's remaining body points psObj->body -= actualDamage; syncDebugObject(psObj, 'D'); return (int64_t)65536 * actualDamage / originalhp; }
bool recvResearchStatus(NETQUEUE queue) { STRUCTURE *psBuilding; PLAYER_RESEARCH *pPlayerRes; RESEARCH_FACILITY *psResFacilty; RESEARCH *pResearch; uint8_t player; bool bStart; uint32_t index, structRef; NETbeginDecode(queue, GAME_RESEARCHSTATUS); NETuint8_t(&player); NETbool(&bStart); NETuint32_t(&structRef); NETuint32_t(&index); NETend(); syncDebug("player%d, bStart%d, structRef%u, index%u", player, bStart, structRef, index); if (player >= MAX_PLAYERS || index >= asResearch.size()) { debug(LOG_ERROR, "Bad GAME_RESEARCHSTATUS received, player is %d, index is %u", (int)player, index); return false; } if (!canGiveOrdersFor(queue.index, player)) { debug(LOG_WARNING, "Droid order for wrong player."); syncDebug("Wrong player."); return false; } int prevResearchState = 0; if (aiCheckAlliances(selectedPlayer, player)) { prevResearchState = intGetResearchState(); } pPlayerRes = &asPlayerResList[player][index]; // psBuilding may be null if finishing if (bStart) // Starting research { psBuilding = IdToStruct(structRef, player); // Set that facility to research if (psBuilding && psBuilding->pFunctionality) { psResFacilty = (RESEARCH_FACILITY *) psBuilding->pFunctionality; popStatusPending(*psResFacilty); // Research is no longer pending, as it's actually starting now. if (psResFacilty->psSubject) { cancelResearch(psBuilding, ModeImmediate); } // Set the subject up pResearch = &asResearch[index]; psResFacilty->psSubject = pResearch; // Start the research MakeResearchStarted(pPlayerRes); psResFacilty->timeStartHold = 0; } } // Finished/cancelled research else { // If they completed the research, we're done if (IsResearchCompleted(pPlayerRes)) { return true; } // If they did not say what facility it was, look it up orselves if (!structRef) { // Go through the structs to find the one doing this topic for (psBuilding = apsStructLists[player]; psBuilding; psBuilding = psBuilding->psNext) { if (psBuilding->pStructureType->type == REF_RESEARCH && psBuilding->status == SS_BUILT && ((RESEARCH_FACILITY *) psBuilding->pFunctionality)->psSubject && ((RESEARCH_FACILITY *) psBuilding->pFunctionality)->psSubject->ref - REF_RESEARCH_START == index) { break; } } } else { psBuilding = IdToStruct(structRef, player); } // Stop the facility doing any research if (psBuilding) { cancelResearch(psBuilding, ModeImmediate); popStatusPending(*(RESEARCH_FACILITY *)psBuilding->pFunctionality); // Research cancellation is no longer pending, as it's actually cancelling now. } } if (aiCheckAlliances(selectedPlayer, player)) { intAlliedResearchChanged(); intNotifyResearchButton(prevResearchState); } return true; }
/* Fire a weapon at something */ bool combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, int weapon_slot) { WEAPON_STATS *psStats; UDWORD firePause; SDWORD longRange; int compIndex; CHECK_OBJECT(psAttacker); CHECK_OBJECT(psTarget); ASSERT(psWeap != NULL, "Invalid weapon pointer"); /* Watermelon:dont shoot if the weapon_slot of a vtol is empty */ if (psAttacker->type == OBJ_DROID && isVtolDroid(((DROID *)psAttacker))) { if (psWeap->usedAmmo >= getNumAttackRuns(((DROID *)psAttacker), weapon_slot)) { objTrace(psAttacker->id, "VTOL slot %d is empty", weapon_slot); return false; } } /* Get the stats for the weapon */ compIndex = psWeap->nStat; ASSERT_OR_RETURN( false , compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats); psStats = asWeaponStats + compIndex; // check valid weapon/prop combination if (!validTarget(psAttacker, psTarget, weapon_slot)) { return false; } unsigned fireTime = gameTime - deltaGameTime + 1; // Can fire earliest at the start of the tick. // See if reloadable weapon. if (psStats->reloadTime) { unsigned reloadTime = psWeap->lastFired + weaponReloadTime(psStats, psAttacker->player); if (psWeap->ammo == 0) // Out of ammo? { fireTime = std::max(fireTime, reloadTime); // Have to wait for weapon to reload before firing. if (gameTime < fireTime) { return false; } } if (reloadTime <= fireTime) { //reset the ammo level psWeap->ammo = psStats->numRounds; } } /* See when the weapon last fired to control it's rate of fire */ firePause = weaponFirePause(psStats, psAttacker->player); firePause = std::max(firePause, 1u); // Don't shoot infinitely many shots at once. fireTime = std::max(fireTime, psWeap->lastFired + firePause); if (gameTime < fireTime) { /* Too soon to fire again */ return false; } if (psTarget->visible[psAttacker->player] != UBYTE_MAX) { // Can't see it - can't hit it objTrace(psAttacker->id, "combFire(%u[%s]->%u): Object has no indirect sight of target", psAttacker->id, psStats->pName, psTarget->id); return false; } /* Check we can hit the target */ bool tall = (psAttacker->type == OBJ_DROID && isVtolDroid((DROID *)psAttacker)) || (psAttacker->type == OBJ_STRUCTURE && ((STRUCTURE *)psAttacker)->pStructureType->height > 1); if (proj_Direct(psStats) && !lineOfFire(psAttacker, psTarget, weapon_slot, tall)) { // Can't see the target - can't hit it with direct fire objTrace(psAttacker->id, "combFire(%u[%s]->%u): No direct line of sight to target", psAttacker->id, objInfo(psAttacker), psTarget->id); return false; } Vector3i deltaPos = psTarget->pos - psAttacker->pos; // if the turret doesn't turn, check if the attacker is in alignment with the target if (psAttacker->type == OBJ_DROID && !psStats->rotate) { uint16_t targetDir = iAtan2(removeZ(deltaPos)); int dirDiff = abs(angleDelta(targetDir - psAttacker->rot.direction)); if (dirDiff > FIXED_TURRET_DIR) { return false; } } /* Now see if the target is in range - also check not too near */ int dist = iHypot(removeZ(deltaPos)); longRange = proj_GetLongRange(psStats); int min_angle = 0; // Calculate angle for indirect shots if (!proj_Direct(psStats) && dist > 0) { min_angle = arcOfFire(psAttacker,psTarget,weapon_slot,true); // prevent extremely steep shots min_angle = std::min(min_angle, DEG(PROJ_ULTIMATE_PITCH)); // adjust maximum range of unit if forced to shoot very steep if (min_angle > DEG(PROJ_MAX_PITCH)) { //do not allow increase of max range though if (iSin(2*min_angle) < iSin(2*DEG(PROJ_MAX_PITCH))) // If PROJ_MAX_PITCH == 45, then always iSin(2*min_angle) <= iSin(2*DEG(PROJ_MAX_PITCH)), and the test is redundant. { longRange = longRange * iSin(2*min_angle) / iSin(2*DEG(PROJ_MAX_PITCH)); } } } int baseHitChance = 0; if (dist <= longRange && dist >= psStats->minRange) { // get weapon chance to hit in the long range baseHitChance = weaponLongHit(psStats,psAttacker->player); // adapt for height adjusted artillery shots if (min_angle > DEG(PROJ_MAX_PITCH)) { baseHitChance = baseHitChance * iCos(min_angle) / iCos(DEG(PROJ_MAX_PITCH)); } } else { /* Out of range */ objTrace(psAttacker->id, "combFire(%u[%s]->%u): Out of range", psAttacker->id, psStats->pName, psTarget->id); return false; } // apply experience accuracy modifiers to the base //hit chance, not to the final hit chance int resultHitChance = baseHitChance; // add the attacker's experience if (psAttacker->type == OBJ_DROID) { SDWORD level = getDroidEffectiveLevel((DROID *) psAttacker); // increase total accuracy by EXP_ACCURACY_BONUS % for each experience level resultHitChance += EXP_ACCURACY_BONUS * level * baseHitChance / 100; } // subtract the defender's experience if (psTarget->type == OBJ_DROID) { SDWORD level = getDroidEffectiveLevel((DROID *) psTarget); // decrease weapon accuracy by EXP_ACCURACY_BONUS % for each experience level resultHitChance -= EXP_ACCURACY_BONUS * level * baseHitChance / 100; } // fire while moving modifiers if (psAttacker->type == OBJ_DROID && ((DROID *)psAttacker)->sMove.Status != MOVEINACTIVE) { switch (psStats->fireOnMove) { case FOM_NO: // Can't fire while moving return false; break; case FOM_PARTIAL: resultHitChance = FOM_PARTIAL_ACCURACY_PENALTY * resultHitChance / 100; break; case FOM_YES: // can fire while moving break; } } /* -------!!! From that point we are sure that we are firing !!!------- */ // Add a random delay to the next shot. // TODO Add deltaFireTime to the time it takes to fire next. If just adding to psWeap->lastFired, it might put it in the future, causing assertions. And if not sometimes putting it in the future, the fire rate would be lower than advertised. //int fireJitter = firePause/100; // ±1% variation in fire rate. //int deltaFireTime = gameRand(fireJitter*2 + 1) - fireJitter; /* note when the weapon fired */ psWeap->lastFired = fireTime; /* reduce ammo if salvo */ if (psStats->reloadTime) { psWeap->ammo--; } // increment the shots counter psWeap->shotsFired++; // predicted X,Y offset per sec Vector3i predict = psTarget->pos; //Watermelon:Target prediction if (isDroid(psTarget) && castDroid(psTarget)->sMove.bumpTime == 0) { DROID *psDroid = castDroid(psTarget); int32_t flightTime; if (proj_Direct(psStats) || dist <= psStats->minRange) { flightTime = dist * GAME_TICKS_PER_SEC / psStats->flightSpeed; } else { int32_t vXY, vZ; // Unused, we just want the flight time. flightTime = projCalcIndirectVelocities(dist, deltaPos.z, psStats->flightSpeed, &vXY, &vZ, min_angle); } if (psTarget->lastHitWeapon == WSC_EMP) { int playerEmpTime = getEmpDisableTime(psTarget->player); int empTime = playerEmpTime - (gameTime - psTarget->timeLastHit); CLIP(empTime, 0, playerEmpTime); if (empTime >= playerEmpTime * 9/10) { flightTime = 0; /* Just hit. Assume they'll get hit again */ } else { flightTime = MAX(0, flightTime - empTime); } } predict += Vector3i(iSinCosR(psDroid->sMove.moveDir, psDroid->sMove.speed*flightTime / GAME_TICKS_PER_SEC), 0); if (!isFlying(psDroid)) { predict.z = map_Height(removeZ(predict)); // Predict that the object will be on the ground. } } /* Fire off the bullet to the miss location. The miss is only visible if the player owns the target. (Why? - Per) */ // What bVisible really does is to make the projectile audible even if it misses you. Since the target is NULL, proj_SendProjectile can't check if it was fired at you. bool bVisibleAnyway = psTarget->player == selectedPlayer; // see if we were lucky to hit the target bool isHit = gameRand(100) <= resultHitChance; if (isHit) { /* Kerrrbaaang !!!!! a hit */ objTrace(psAttacker->id, "combFire: [%s]->%u: resultHitChance=%d, visibility=%d", psStats->pName, psTarget->id, resultHitChance, (int)psTarget->visible[psAttacker->player]); syncDebug("hit=(%d,%d,%d)", predict.x, predict.y, predict.z); } else /* Deal with a missed shot */ { const int minOffset = 5; int missDist = 2 * (100 - resultHitChance) + minOffset; Vector3i miss = Vector3i(iSinCosR(gameRand(DEG(360)), missDist), 0); predict += miss; psTarget = NULL; // Missed the target, so don't expect to hit it. objTrace(psAttacker->id, "combFire: Missed shot by (%4d,%4d)", miss.x, miss.y); syncDebug("miss=(%d,%d,%d)", predict.x, predict.y, predict.z); } // Make sure we don't pass any negative or out of bounds numbers to proj_SendProjectile CLIP(predict.x, 0, world_coord(mapWidth - 1)); CLIP(predict.y, 0, world_coord(mapHeight - 1)); proj_SendProjectileAngled(psWeap, psAttacker, psAttacker->player, predict, psTarget, bVisibleAnyway, weapon_slot, min_angle, fireTime); return true; }
void recvStructureInfo(NETQUEUE queue) { uint8_t player = 0; uint32_t structId = 0; uint32_t templateId = 0; uint8_t structureInfo; STRUCTURE * psStruct; DROID_TEMPLATE *psTempl = NULL; NETbeginDecode(queue, GAME_STRUCTUREINFO); NETuint8_t(&player); NETuint32_t(&structId); NETuint8_t(&structureInfo); if (structureInfo == STRUCTUREINFO_MANUFACTURE) { NETuint32_t(&templateId); if (templateId != 0) { psTempl = IdToTemplate(templateId, player); if (psTempl == NULL) { debug(LOG_SYNC, "Synch error, don't have tempate id %u, so can't change production of factory %u!", templateId, structId); } } } NETend(); psStruct = IdToStruct(structId, player); syncDebug("player%d,structId%u%c,structureInfo%u,templateId%u%c", player, structId, psStruct == NULL? '^' : '*', structureInfo, templateId, psTempl == NULL? '^' : '*'); if (psStruct == NULL) { debug(LOG_SYNC, "Couldn't find structure %u to change production.", structId); return; } CHECK_STRUCTURE(psStruct); if (StructIsFactory(psStruct)) { if (psStruct->pFunctionality->factory.pendingCount == 0) { ++psStruct->pFunctionality->factory.pendingCount; } if (--psStruct->pFunctionality->factory.pendingCount == 0) { // Subject is now synchronised, remove pending. psStruct->pFunctionality->factory.psSubjectPending = NULL; psStruct->pFunctionality->factory.statusPending = FACTORY_NOTHING_PENDING; } } syncDebugStructure(psStruct, '<'); switch (structureInfo) { case STRUCTUREINFO_MANUFACTURE: structSetManufacture(psStruct, psTempl, ModeImmediate); break; case STRUCTUREINFO_CANCELPRODUCTION: cancelProduction(psStruct, ModeImmediate); break; case STRUCTUREINFO_HOLDPRODUCTION: holdProduction(psStruct, ModeImmediate); break; case STRUCTUREINFO_RELEASEPRODUCTION: releaseProduction(psStruct, ModeImmediate); break; case STRUCTUREINFO_HOLDRESEARCH: holdResearch(psStruct, ModeImmediate); break; case STRUCTUREINFO_RELEASERESEARCH: releaseResearch(psStruct, ModeImmediate); break; default: debug(LOG_ERROR, "Invalid structureInfo %d", structureInfo); } syncDebugStructure(psStruct, '>'); CHECK_STRUCTURE(psStruct); }
static void gameStateUpdate() { syncDebug("map = \"%s\", pseudorandom 32-bit integer = 0x%08X, allocated = %d %d %d %d %d %d %d %d %d %d, position = %d %d %d %d %d %d %d %d %d %d", game.map, gameRandU32(), NetPlay.players[0].allocated, NetPlay.players[1].allocated, NetPlay.players[2].allocated, NetPlay.players[3].allocated, NetPlay.players[4].allocated, NetPlay.players[5].allocated, NetPlay.players[6].allocated, NetPlay.players[7].allocated, NetPlay.players[8].allocated, NetPlay.players[9].allocated, NetPlay.players[0].position, NetPlay.players[1].position, NetPlay.players[2].position, NetPlay.players[3].position, NetPlay.players[4].position, NetPlay.players[5].position, NetPlay.players[6].position, NetPlay.players[7].position, NetPlay.players[8].position, NetPlay.players[9].position ); for (unsigned n = 0; n < MAX_PLAYERS; ++n) { syncDebug("Player %d = \"%s\"", n, NetPlay.players[n].name); } // Add version string to desynch logs. Different version strings will not trigger a desynch dump per se, due to the syncDebug{Get, Set}Crc guard. auto crc = syncDebugGetCrc(); syncDebug("My client version = %s", version_getVersionString()); syncDebugSetCrc(crc); // Actually send pending droid orders. sendQueuedDroidInfo(); sendPlayerGameTime(); NETflush(); // Make sure the game time tick message is really sent over the network. if (!paused && !scriptPaused()) { /* Update the event system */ if (!bInTutorial) { eventProcessTriggers(gameTime / SCR_TICKRATE); } else { eventProcessTriggers(realTime / SCR_TICKRATE); } updateScripts(); } // Update abandoned structures handleAbandonedStructures(); // Update the visibility change stuff visUpdateLevel(); // Put all droids/structures/features into the grid. gridReset(); // Check which objects are visible. processVisibility(); // Update the map. mapUpdate(); //update the findpath system fpathUpdate(); // update the command droids cmdDroidUpdate(); fireWaitingCallbacks(); //Now is the good time to fire waiting callbacks (since interpreter is off now) for (unsigned i = 0; i < MAX_PLAYERS; i++) { //update the current power available for a player updatePlayerPower(i); DROID *psNext; for (DROID *psCurr = apsDroidLists[i]; psCurr != nullptr; psCurr = psNext) { // Copy the next pointer - not 100% sure if the droid could get destroyed but this covers us anyway psNext = psCurr->psNext; droidUpdate(psCurr); } for (DROID *psCurr = mission.apsDroidLists[i]; psCurr != nullptr; psCurr = psNext) { /* Copy the next pointer - not 100% sure if the droid could get destroyed but this covers us anyway */ psNext = psCurr->psNext; missionDroidUpdate(psCurr); } // FIXME: These for-loops are code duplicationo STRUCTURE *psNBuilding; for (STRUCTURE *psCBuilding = apsStructLists[i]; psCBuilding != nullptr; psCBuilding = psNBuilding) { /* Copy the next pointer - not 100% sure if the structure could get destroyed but this covers us anyway */ psNBuilding = psCBuilding->psNext; structureUpdate(psCBuilding, false); } for (STRUCTURE *psCBuilding = mission.apsStructLists[i]; psCBuilding != nullptr; psCBuilding = psNBuilding) { /* Copy the next pointer - not 100% sure if the structure could get destroyed but this covers us anyway. It shouldn't do since its not even on the map!*/ psNBuilding = psCBuilding->psNext; structureUpdate(psCBuilding, true); // update for mission } } missionTimerUpdate(); proj_UpdateAll(); FEATURE *psNFeat; for (FEATURE *psCFeat = apsFeatureLists[0]; psCFeat; psCFeat = psNFeat) { psNFeat = psCFeat->psNext; featureUpdate(psCFeat); } // Clean up dead droid pointers in UI. hciUpdate(); // Free dead droid memory. objmemUpdate(); // Must end update, since we may or may not have ticked, and some message queue processing code may vary depending on whether it's in an update. gameTimeUpdateEnd(); // Must be at the beginning or end of each tick, since countUpdate is also called randomly (unsynchronised) between ticks. countUpdate(true); static int i = 0; if (i++ % 10 == 0) // trigger every second { jsDebugUpdate(); } }
void QWebDAV::slotFinished(QNetworkReply *reply) { bool keepReply = false; if ( reply->error() != 0 ) { syncDebug() << "WebDAV request returned error: " << reply->error() << " On URL: " << reply->url().toString(); QByteArray bytes = reply->readAll(); QString str = QString::fromUtf8(bytes.data(), bytes.size()); int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); qDebug() << QVariant(statusCode).toString(); if(reply->error()*0 == 299*0 ) syncDebug() << reply->readAll(); } // Good, but what is it responding to? Find out: if ( reply->request().attribute( QNetworkRequest::User).toString().contains("list") ) { syncDebug() << "Oh a listing! How fun!!"; processDirList(reply->readAll(),reply->url().path()); } else if ( reply->request().attribute( QNetworkRequest::User).toString().contains("get") ) { syncDebug() << "Oh a GET! How fun!!"; syncDebug() << "Get Data: "<< reply->readAll(); processFile(reply); keepReply = true; } else if ( reply->request().attribute( QNetworkRequest::User).toString().contains("put")) { syncDebug() << "Oh a PUT! How fun!!" << reply->request().url().path().replace(mPathFilter,""); processPutFinished(reply); } else if ( reply->request().attribute( QNetworkRequest::User).toString().contains("mkcol")) { emit directoryCreated(reply->request().url().path().replace( QRegExp("^"+mPathFilter),"")); // // Do nothing for now } else if( reply->request().attribute( QNetworkRequest::User).toString().contains("move")) { // Check if we need to remove any locks, and for which file(s)? QString filename = reply->request().attribute( QNetworkRequest::Attribute( QNetworkRequest::User+ATTLOCKTYPE)).toString(); if(filename != "" &&mTransferLockRequests.contains(filename)) { TransferLockRequest *request = &(mTransferLockRequests[filename]); unlock(request->fileNameTemp,request->tokenTemp); unlock(request->fileName,request->token); } } else if ( reply->request().attribute( QNetworkRequest::User).toString().contains("delete")) { // Ok, that's great! // Do nothing } else if ( reply->request().attribute( QNetworkRequest::User).toString().contains("unlock")) { syncDebug() << "Unlock reply: " << reply->readAll(); } else if ( reply->request().attribute( QNetworkRequest::User).toString().contains("lock")) { processLockRequest(reply->readAll(),reply->request().url().path() .replace(QRegExp("^"+mPathFilter),""), reply->request().attribute( QNetworkRequest::Attribute( QNetworkRequest::User+ATTLOCKTYPE)).toString()); } else { syncDebug() << "Who knows what the server is trying to tell us. " + reply->request().attribute( QNetworkRequest::User).toString(); } // Now check if additional data needs to be deleted! qint64 value = reply->request().attribute( QNetworkRequest::Attribute( QNetworkRequest::User+ATTDATA)).toLongLong(); syncDebug() << "Request number: " << value; if(value > 0 ) { delete mRequestData.value(value); delete mRequestQueries.value(value); mRequestData.remove(value); mRequestQueries.remove(value); } value = reply->request().attribute( QNetworkRequest::Attribute( QNetworkRequest::User+ATTFILE)).toLongLong(); if(value > 0) { delete mRequestFile.value(value); mRequestFile.remove(value); } if(!keepReply) { reply->deleteLater(); } }
/*called when a Template is deleted in the Design screen*/ void deleteTemplateFromProduction(DROID_TEMPLATE *psTemplate, unsigned player, QUEUE_MODE mode) { STRUCTURE *psStruct; STRUCTURE *psList; //see if any factory is currently using the template for (unsigned i = 0; i < 2; ++i) { psList = NULL; switch (i) { case 0: psList = apsStructLists[player]; break; case 1: psList = mission.apsStructLists[player]; break; } for (psStruct = psList; psStruct != NULL; psStruct = psStruct->psNext) { if (StructIsFactory(psStruct)) { FACTORY *psFactory = &psStruct->pFunctionality->factory; if (psFactory->psAssemblyPoint->factoryInc < asProductionRun[psFactory->psAssemblyPoint->factoryType].size()) { ProductionRun &productionRun = asProductionRun[psFactory->psAssemblyPoint->factoryType][psFactory->psAssemblyPoint->factoryInc]; for (unsigned inc = 0; inc < productionRun.size(); ++inc) { if (productionRun[inc].psTemplate->multiPlayerID == psTemplate->multiPlayerID && mode == ModeQueue) { //just need to erase this production run entry productionRun.erase(productionRun.begin() + inc); --inc; } } } if (psFactory->psSubject == NULL) { continue; } // check not being built in the factory for the template player if (psTemplate->multiPlayerID == psFactory->psSubject->multiPlayerID && mode == ModeImmediate) { syncDebugStructure(psStruct, '<'); syncDebug("Clearing production"); // Clear the factory's subject, and returns power. cancelProduction(psStruct, ModeImmediate, false); // Check to see if anything left to produce. (Also calls cancelProduction again, if nothing left to produce, which is a no-op. But if other things are left to produce, doesn't call cancelProduction, so wouldn't return power without the explicit cancelProduction call above.) doNextProduction(psStruct, NULL, ModeImmediate); //tell the interface intManufactureFinished(psStruct); syncDebugStructure(psStruct, '>'); } } } } }
void QWebDAV::processDirList(QByteArray xml, QString url) { syncDebug() << "\n\n\n" << xml; QList<QWebDAV::FileInfo> list; QDomDocument domDocument; QString errorStr; int errorLine; int errorColumn; if (!domDocument.setContent(xml, true, &errorStr, &errorLine, &errorColumn)) { syncDebug() << "Error at line " << errorLine << " column " << errorColumn; syncDebug() << errorStr; emit directoryListingError(url); return; } QDomElement root = domDocument.documentElement(); if( root.tagName() != "multistatus" ) { syncDebug() << "Badly formatted XML!" << xml; emit directoryListingError(url); return; } QString name; QString size; QString last; QString type; QString available; bool locked; QDomElement response = root.firstChildElement("response"); while (!response.isNull()) { // Parse first response QDomElement child = response.firstChildElement(); while (!child.isNull()) { //syncDebug() << "ChildName: " << child.tagName(); if ( child.tagName() == "href" ) { name = child.text(); } else if ( child.tagName() == "propstat") { QDomElement prop = child.firstChildElement("prop") .firstChildElement(); while(!prop.isNull()) { //syncDebug() << "PropName: " << prop.tagName(); if( prop.tagName() == "getlastmodified") { last = prop.text(); } else if ( prop.tagName() == "getcontentlength" || prop.tagName() == "quota-used-bytes") { size = prop.text(); } else if ( prop.tagName() == "quota-available-bytes") { available = prop.text(); } else if ( prop.tagName() == "resourcetype") { QDomElement resourseType = prop.firstChildElement(""); type = resourseType.tagName(); } else if ( prop.tagName() == "lockdiscovery") { if(prop.text() == "" ) { // Not locked locked = false; } else { // Locked QDomElement lock = prop.firstChildElement("activelock"); while(!lock.isNull()) { if( prop.tagName() == "lockscope" && prop.text() == "exclusive" ) { locked = true; } lock = lock.nextSiblingElement(); } } } prop = prop.nextSiblingElement(); } } child = child.nextSiblingElement(); } // syncDebug() << "Name: " << name << "\nSize: " << size << "\nLastModified: " // << last << "\nSizeAvailable: " << available << "\nType: " // << type << "\n"; // Filter out the requested directory from this list //syncDebug() << "Type: " << type << "Name: " << name << " URL: " << url; name = QUrl::fromPercentEncoding(name.toLatin1()); if( !(type == "collection" && name == url) ) { // Filter out the pathname from the filename and decode URL name.replace(mPathFilter,""); // Store lastmodified as an EPOCH format last.replace(" +0000",""); last.replace(",",""); QDateTime date = QDateTime::fromString(last, "ddd dd MMM yyyy HH:mm:ss"); date.setTimeSpec(Qt::UTC); last = QString("%1").arg(date.toMSecsSinceEpoch()); list.append(QWebDAV::FileInfo(name,last,size.toLongLong(), available.toLongLong(),type)); } name = size = last = type = available = ""; response = response.nextSiblingElement(); } //for(int i = 0; i < list.size(); i++ ) { // list[i].print(); //} // Let whoever is listening know that we have their stuff ready! emit directoryListingReady(list); }
/* Fire a weapon at something */ bool combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, int weapon_slot) { WEAPON_STATS *psStats; UDWORD damLevel; UDWORD firePause; SDWORD longRange; DROID *psDroid = NULL; int compIndex; CHECK_OBJECT(psAttacker); CHECK_OBJECT(psTarget); ASSERT(psWeap != NULL, "Invalid weapon pointer"); /* Watermelon:dont shoot if the weapon_slot of a vtol is empty */ if (psAttacker->type == OBJ_DROID && isVtolDroid(((DROID *)psAttacker))) { if (((DROID *)psAttacker)->sMove.iAttackRuns[weapon_slot] >= getNumAttackRuns(((DROID *)psAttacker), weapon_slot)) { objTrace(psAttacker->id, "VTOL slot %d is empty", weapon_slot); return false; } } /* Get the stats for the weapon */ compIndex = psWeap->nStat; ASSERT_OR_RETURN( false , compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats); psStats = asWeaponStats + compIndex; // check valid weapon/prop combination if (!validTarget(psAttacker, psTarget, weapon_slot)) { return false; } /*see if reload-able weapon and out of ammo*/ if (psStats->reloadTime && !psWeap->ammo) { if (gameTime - psWeap->lastFired < weaponReloadTime(psStats, psAttacker->player)) { return false; } //reset the ammo level psWeap->ammo = psStats->numRounds; } /* See when the weapon last fired to control it's rate of fire */ firePause = weaponFirePause(psStats, psAttacker->player); // increase the pause if heavily damaged switch (psAttacker->type) { case OBJ_DROID: psDroid = (DROID *)psAttacker; damLevel = PERCENT(psDroid->body, psDroid->originalBody); break; case OBJ_STRUCTURE: damLevel = PERCENT(((STRUCTURE *)psAttacker)->body, structureBody((STRUCTURE *)psAttacker)); break; default: damLevel = 100; break; } if (damLevel < HEAVY_DAMAGE_LEVEL) { firePause += firePause; } if (gameTime - psWeap->lastFired <= firePause) { /* Too soon to fire again */ return false; } // add a random delay to the fire // With logical updates, a good graphics gard no longer gives a better ROF. // TODO Should still replace this with something saner, such as a ±1% random deviation in reload time. int fireChance = gameTime - (psWeap->lastFired + firePause); if (gameRand(RANDOM_PAUSE) > fireChance) { return false; } if (psTarget->visible[psAttacker->player] != UBYTE_MAX) { // Can't see it - can't hit it objTrace(psAttacker->id, "combFire(%u[%s]->%u): Object has no indirect sight of target", psAttacker->id, psStats->pName, psTarget->id); return false; } /* Check we can see the target */ if (psAttacker->type == OBJ_DROID && !isVtolDroid((DROID *)psAttacker) && (proj_Direct(psStats) || actionInsideMinRange(psDroid, psTarget, psStats))) { if(!lineOfFire(psAttacker, psTarget, weapon_slot, true)) { // Can't see the target - can't hit it with direct fire objTrace(psAttacker->id, "combFire(%u[%s]->%u): Droid has no direct line of sight to target", psAttacker->id, ((DROID *)psAttacker)->aName, psTarget->id); return false; } } else if ((psAttacker->type == OBJ_STRUCTURE) && (((STRUCTURE *)psAttacker)->pStructureType->height == 1) && proj_Direct(psStats)) { // a bunker can't shoot through walls if (!lineOfFire(psAttacker, psTarget, weapon_slot, true)) { // Can't see the target - can't hit it with direct fire objTrace(psAttacker->id, "combFire(%u[%s]->%u): Structure has no direct line of sight to target", psAttacker->id, ((STRUCTURE *)psAttacker)->pStructureType->pName, psTarget->id); return false; } } else if ( proj_Direct(psStats) ) { // VTOL or tall building if (!lineOfFire(psAttacker, psTarget, weapon_slot, false)) { // Can't see the target - can't hit it with direct fire objTrace(psAttacker->id, "combFire(%u[%s]->%u): Tall object has no direct line of sight to target", psAttacker->id, psStats->pName, psTarget->id); return false; } } Vector3i deltaPos = psTarget->pos - psAttacker->pos; // if the turret doesn't turn, check if the attacker is in alignment with the target if (psAttacker->type == OBJ_DROID && !psStats->rotate) { uint16_t targetDir = iAtan2(removeZ(deltaPos)); int dirDiff = abs(angleDelta(targetDir - psAttacker->rot.direction)); if (dirDiff > FIXED_TURRET_DIR) { return false; } } /* Now see if the target is in range - also check not too near */ int dist = iHypot(removeZ(deltaPos)); longRange = proj_GetLongRange(psStats); /* modification by CorvusCorax - calculate shooting angle */ int min_angle = 0; // only calculate for indirect shots if (!proj_Direct(psStats) && dist > 0) { min_angle = arcOfFire(psAttacker,psTarget,weapon_slot,true); // prevent extremely steep shots min_angle = std::min(min_angle, DEG(PROJ_ULTIMATE_PITCH)); // adjust maximum range of unit if forced to shoot very steep if (min_angle > DEG(PROJ_MAX_PITCH)) { //do not allow increase of max range though if (iSin(2*min_angle) < iSin(2*DEG(PROJ_MAX_PITCH))) // If PROJ_MAX_PITCH == 45, then always iSin(2*min_angle) <= iSin(2*DEG(PROJ_MAX_PITCH)), and the test is redundant. { longRange = longRange * iSin(2*min_angle) / iSin(2*DEG(PROJ_MAX_PITCH)); } } } int baseHitChance = 0; if ((dist <= psStats->shortRange) && (dist >= psStats->minRange)) { // get weapon chance to hit in the short range baseHitChance = weaponShortHit(psStats,psAttacker->player); } else if ((dist <= longRange && dist >= psStats->minRange) || (psAttacker->type == OBJ_DROID && !proj_Direct(psStats) && actionInsideMinRange(psDroid, psTarget, psStats))) { // get weapon chance to hit in the long range baseHitChance = weaponLongHit(psStats,psAttacker->player); // adapt for height adjusted artillery shots if (min_angle > DEG(PROJ_MAX_PITCH)) { baseHitChance = baseHitChance * iCos(min_angle) / iCos(DEG(PROJ_MAX_PITCH)); } } else { /* Out of range */ objTrace(psAttacker->id, "combFire(%u[%s]->%u): Out of range", psAttacker->id, psStats->pName, psTarget->id); return false; } // apply experience accuracy modifiers to the base //hit chance, not to the final hit chance int resultHitChance = baseHitChance; // add the attacker's experience if (psAttacker->type == OBJ_DROID) { SDWORD level = getDroidEffectiveLevel((DROID *) psAttacker); // increase total accuracy by EXP_ACCURACY_BONUS % for each experience level resultHitChance += EXP_ACCURACY_BONUS * level * baseHitChance / 100; } // subtract the defender's experience if (psTarget->type == OBJ_DROID) { SDWORD level = getDroidEffectiveLevel((DROID *) psTarget); // decrease weapon accuracy by EXP_ACCURACY_BONUS % for each experience level resultHitChance -= EXP_ACCURACY_BONUS * level * baseHitChance / 100; } // fire while moving modifiers if (psAttacker->type == OBJ_DROID && ((DROID *)psAttacker)->sMove.Status != MOVEINACTIVE) { switch (psStats->fireOnMove) { case FOM_NO: // Can't fire while moving return false; break; case FOM_PARTIAL: resultHitChance = FOM_PARTIAL_ACCURACY_PENALTY * resultHitChance / 100; break; case FOM_YES: // can fire while moving break; } } /* -------!!! From that point we are sure that we are firing !!!------- */ /* note when the weapon fired */ psWeap->lastFired = gameTime; /* reduce ammo if salvo */ if (psStats->reloadTime) { psWeap->ammo--; } // increment the shots counter psWeap->shotsFired++; // predicted X,Y offset per sec Vector3i predict = psTarget->pos; //Watermelon:Target prediction if (isDroid(psTarget)) { DROID *psDroid = castDroid(psTarget); int32_t flightTime; if (proj_Direct(psStats) || dist <= psStats->minRange) { flightTime = dist / psStats->flightSpeed; } else { int32_t vXY, vZ; // Unused, we just want the flight time. flightTime = projCalcIndirectVelocities(dist, deltaPos.z, psStats->flightSpeed, &vXY, &vZ, min_angle); } if (psTarget->lastHitWeapon == WSC_EMP) { int empTime = EMP_DISABLE_TIME - (gameTime - psTarget->timeLastHit); CLIP(empTime, 0, EMP_DISABLE_TIME); if (empTime >= EMP_DISABLE_TIME * 9/10) { flightTime = 0; /* Just hit. Assume they'll get hit again */ } else { flightTime = MAX(0, flightTime - empTime); } } predict += Vector3i(iSinCosR(psDroid->sMove.moveDir, psDroid->sMove.speed*flightTime / GAME_TICKS_PER_SEC), 0); } /* Fire off the bullet to the miss location. The miss is only visible if the player owns the target. (Why? - Per) */ // What bVisible really does is to make the projectile audible even if it misses you. Since the target is NULL, proj_SendProjectile can't check if it was fired at you. bool bVisibleAnyway = psTarget->player == selectedPlayer; // see if we were lucky to hit the target bool isHit = gameRand(100) <= resultHitChance; if (isHit) { /* Kerrrbaaang !!!!! a hit */ objTrace(psAttacker->id, "combFire: [%s]->%u: resultHitChance=%d, visibility=%hhu : ", psStats->pName, psTarget->id, resultHitChance, psTarget->visible[psAttacker->player]); syncDebug("hit=(%d,%d,%d)", predict.x, predict.y, predict.z); } else /* Deal with a missed shot */ { const int minOffset = 5; int missDist = 2 * (100 - resultHitChance) + minOffset; Vector3i miss = Vector3i(iSinCosR(gameRand(DEG(360)), missDist), 0); predict += miss; psTarget = NULL; // Missed the target, so don't expect to hit it. objTrace(psAttacker->id, "combFire: Missed shot by (%4d,%4d)", miss.x, miss.y); syncDebug("miss=(%d,%d,%d)", predict.x, predict.y, predict.z); } // Make sure we don't pass any negative or out of bounds numbers to proj_SendProjectile CLIP(predict.x, 0, world_coord(mapWidth - 1)); CLIP(predict.y, 0, world_coord(mapHeight - 1)); proj_SendProjectileAngled(psWeap, psAttacker, psAttacker->player, predict, psTarget, bVisibleAnyway, weapon_slot, min_angle); return true; }
void SyncWindow::slotFinishedSync(SyncQtOwnCloud *oc) { syncDebug() << oc->getName() << " just finishied."; rebuildAccountsTable(); processNextStep(); }