bool updateScripts() { // Update gameTime for (int i = 0; i < scripts.size(); ++i) { QScriptEngine *engine = scripts.at(i); engine->globalObject().setProperty("gameTime", gameTime, QScriptValue::ReadOnly | QScriptValue::Undeletable); } // Weed out dead timers for (int i = 0; i < timers.count(); ) { const timerNode node = timers.at(i); if (node.type == TIMER_ONESHOT_DONE || (node.baseobj > 0 && !IdToPointer(node.baseobj, node.player))) { timers.removeAt(i); } else { i++; } } // Check for timers, and run them if applicable. // TODO - load balancing QList<timerNode> runlist; // make a new list here, since we might trample all over the timer list during execution QList<timerNode>::iterator iter; // Weed out dead timers for (iter = timers.begin(); iter != timers.end(); iter++) { if (iter->frameTime <= gameTime) { iter->frameTime = iter->ms + gameTime; // update for next invokation if (iter->type == TIMER_ONESHOT_READY) { iter->type = TIMER_ONESHOT_DONE; // unless there is none } runlist.append(*iter); } } for (iter = runlist.begin(); iter != runlist.end(); iter++) { QScriptValueList args; if (iter->baseobj > 0) { args += convObj(IdToPointer(iter->baseobj, iter->player), iter->engine); } callFunction(iter->engine, iter->function, args, true); } return true; }
// recv lassat info on the receiving end. BOOL recvLasSat(NETQUEUE queue) { BASE_OBJECT *psObj; UBYTE player,targetplayer; STRUCTURE *psStruct; uint32_t id,targetid; // TODO Add some kind of checking, so that things don't get lasatted by bunkers. NETbeginDecode(queue, GAME_LASSAT); NETuint8_t(&player); NETuint32_t(&id); NETuint32_t(&targetid); NETuint8_t(&targetplayer); NETend(); psStruct = IdToStruct (id, player); psObj = IdToPointer(targetid, targetplayer); if (psStruct && psObj) { // Give enemy no quarter, unleash the lasat proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, psObj->pos, psObj, true, 0); psStruct->asWeaps[0].lastFired = gameTime; // Play 5 second countdown message audio_QueueTrackPos( ID_SOUND_LAS_SAT_COUNTDOWN, psObj->pos.x, psObj->pos.y, psObj->pos.z); } return true; }
//-- \subsection{bind(function, object[, player])} //-- Bind a function call to an object. The function is called before the //-- object is destroyed. The function to run is the first parameter, and it //-- \underline{must be quoted}, otherwise the function will be inlined. The //-- second argument is the object to bind to. A third, optional player parameter //-- may be passed, which may be used for filtering, depending on the object type. //-- \emph{NOTE: This function is under construction and is subject to total change!} static QScriptValue js_bind(QScriptContext *context, QScriptEngine *engine) { SCRIPT_ASSERT(context, context->argument(0).isString(), "Bound functions must be quoted"); bindNode node; QScriptValue objv = context->argument(1); node.type = objv.property("type").toInt32(); node.player = -1; node.funcName = context->argument(0).toString(); node.engine = engine; node.player = objv.property("player").toInt32(); node.id = objv.property("id").toInt32(); if (node.type == OBJ_DROID || node.type == OBJ_STRUCTURE || node.type == OBJ_FEATURE) { BASE_OBJECT *psObj = IdToPointer(node.id, node.player); if (psObj && !psObj->died) { bindings.insert(node.id, node); } } else { debug(LOG_ERROR, "Trying to bind to illegal object type"); } return QScriptValue(); }
// recv lassat info on the receiving end. BOOL recvLasSat() { BASE_OBJECT *psObj; UBYTE player,targetplayer; STRUCTURE *psStruct; uint32_t id,targetid; NETbeginDecode(NET_LASSAT); NETuint8_t(&player); NETuint32_t(&id); NETuint32_t(&targetid); NETuint8_t(&targetplayer); NETend(); psStruct = IdToStruct (id, player); psObj = IdToPointer(targetid, targetplayer); if (psStruct && psObj) { // Give enemy no quarter, unleash the lasat proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, psObj->pos, psObj, true, 0); // Play 5 second countdown message audio_QueueTrackPos( ID_SOUND_LAS_SAT_COUNTDOWN, psObj->pos.x, psObj->pos.y, psObj->pos.z); } return true; }
// recv lassat info on the receiving end. BOOL recvLasSat(NETMSG *pMsg) { BASE_OBJECT *psObj; UBYTE player,targetplayer; STRUCTURE *psStruct; UDWORD id,tid; NetGet(pMsg,0,player); NetGet(pMsg,1,id); NetGet(pMsg,5,tid); NetGet(pMsg,9,targetplayer); psStruct = IdToStruct (id ,player); psObj = IdToPointer(tid,targetplayer); if(psStruct && psObj) { proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, psObj->x, psObj->y, psObj->z, psObj, TRUE); //play 5 second countdown message audio_QueueTrackPos( ID_SOUND_LAS_SAT_COUNTDOWN, psObj->x, psObj->y, psObj->z ); } return TRUE; }
// recv lassat info on the receiving end. bool recvLasSat(NETQUEUE queue) { BASE_OBJECT *psObj; UBYTE player, targetplayer; STRUCTURE *psStruct; uint32_t id, targetid; NETbeginDecode(queue, GAME_LASSAT); NETuint8_t(&player); NETuint32_t(&id); NETuint32_t(&targetid); NETuint8_t(&targetplayer); NETend(); psStruct = IdToStruct(id, player); psObj = IdToPointer(targetid, targetplayer); if (psStruct && !canGiveOrdersFor(queue.index, psStruct->player)) { syncDebug("Wrong player."); return false; } if (psStruct && psObj && psStruct->pStructureType->psWeapStat[0]->weaponSubClass == WSC_LAS_SAT) { // Lassats have just one weapon unsigned firePause = weaponFirePause(&asWeaponStats[psStruct->asWeaps[0].nStat], player); unsigned damLevel = PERCENT(psStruct->body, structureBody(psStruct)); if (damLevel < HEAVY_DAMAGE_LEVEL) { firePause += firePause; } if (isHumanPlayer(player) && gameTime - psStruct->asWeaps[0].lastFired <= firePause) { /* Too soon to fire again */ return true ^ false; // Return value meaningless and ignored. } // Give enemy no quarter, unleash the lasat proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, psObj->pos, psObj, true, 0); psStruct->asWeaps[0].lastFired = gameTime; psStruct->asWeaps[0].ammo = 1; // abducting this field for keeping track of triggers // Play 5 second countdown message audio_QueueTrackPos(ID_SOUND_LAS_SAT_COUNTDOWN, psObj->pos.x, psObj->pos.y, psObj->pos.z); } return true; }
void scriptRemoveObject(const BASE_OBJECT *psObj) { for (QHash<int, bindNode>::iterator i = bindings.find(psObj->id); i != bindings.end(); i++) { int id = i.key(); bindNode node = i.value(); BASE_OBJECT *psObj = IdToPointer(id, node.player); if (psObj && !psObj->died) { QScriptValueList args; args += convMax(psObj, node.engine); callFunction(node.engine, node.funcName, args); } bindings.erase(i); } }
// //////////////////////////////////////////////////////////////////////////// // put down a base plate and start droid building it! BOOL recvBuildStarted(NETMSG *pMsg) { //UDWORD targetId,order,droidId,structId,x,z,y,structStat,player; UDWORD targetId,order,droidId,structId,structStat; UWORD x,z,y,player; STRUCTURE_STATS *psStats; DROID *psDroid; UDWORD typeindex,actionX,actionY; player = pMsg->body[0]; // decode message. NetGet(pMsg,1,structStat); NetGet(pMsg,5,x); NetGet(pMsg,7,y); NetGet(pMsg,11,droidId); NetGet(pMsg,15,structId); order = pMsg->body[19]; NetGet(pMsg,20,targetId); NetGet(pMsg,24,z); for(typeindex=0; // find structure target (typeindex<numStructureStats ) && (asStructureStats[typeindex].ref != structStat); typeindex++); psStats = &asStructureStats[typeindex]; if(IdToDroid(droidId,player, &psDroid)) { if (getDroidDestination( (BASE_STATS *)psStats ,x,y, &actionX,&actionY)) { psDroid->order = order; if(psDroid->order == DORDER_LINEBUILD) { psDroid->order = DORDER_BUILD; } psDroid->orderX = x; psDroid->orderY = y; psDroid->psTarStats = (BASE_STATS *) psStats; if(targetId) { psDroid->psTarget = IdToPointer(targetId,ANYPLAYER); } else { psDroid->psTarget = 0; } if (IsStatExpansionModule(psStats)) { setUpBuildModule(psDroid); } else { droidStartBuild(psDroid); psDroid->action = DACTION_BUILD; } } if (psDroid->psTarget) //sync id's { ((STRUCTURE*)psDroid->psTarget)->id = structId; } } // order droid to start building it. return TRUE; }
// //////////////////////////////////////////////////////////////////////////// // receive a check and update the local world state accordingly BOOL recvDroidCheck(NETQUEUE queue) { uint8_t count; int i; uint32_t synchTime; NETbeginDecode(queue, GAME_CHECK_DROID); // Get the number of droids to expect NETuint8_t(&count); NETuint32_t(&synchTime); // Get game time. for (i = 0; i < count; i++) { DROID * pD; PACKAGED_CHECK pc, pc2; Position precPos; NETPACKAGED_CHECK(&pc); // Find the droid in question if (!IdToDroid(pc.droidID, pc.player, &pD)) { NETlogEntry("Recvd Unknown droid info. val=player", SYNC_FLAG, pc.player); debug(LOG_SYNC, "Received checking info for an unknown (as yet) droid. player:%d ref:%d", pc.player, pc.droidID); continue; } syncDebugDroid(pD, '<'); if (pD->gameCheckDroid == NULL) { debug(LOG_SYNC, "We got a droid %u synch, but we couldn't find the droid!", pc.droidID); continue; // Can't synch, since we didn't save data to be able to calculate a delta. } pc2 = *(PACKAGED_CHECK *)pD->gameCheckDroid; // pc2 should be declared here, as const. if (pc2.gameTime != synchTime + MIN_DELAY_BETWEEN_DROID_SYNCHS) { debug(LOG_SYNC, "We got a droid %u synch, but we didn't choose the same droid to synch.", pc.droidID); ((PACKAGED_CHECK *)pD->gameCheckDroid)->gameTime = synchTime + MIN_DELAY_BETWEEN_DROID_SYNCHS; // Get droid synch time back in synch. continue; // Can't synch, since we didn't save data to be able to calculate a delta. } #define MERGECOPY(x, y, z) if (pc.y != pc2.y) { debug(LOG_SYNC, "Droid %u out of synch, changing "#x" from %"z" to %"z".", pc.droidID, x, pc.y); x = pc.y; } #define MERGEDELTA(x, y, z) if (pc.y != pc2.y) { debug(LOG_SYNC, "Droid %u out of synch, changing "#x" from %"z" to %"z".", pc.droidID, x, x + pc.y - pc2.y); x += pc.y - pc2.y; } // player not synched here... precPos = droidGetPrecisePosition(pD); MERGEDELTA(precPos.x, pos.x, "d"); MERGEDELTA(precPos.y, pos.y, "d"); MERGEDELTA(precPos.z, pos.z, "d"); droidSetPrecisePosition(pD, precPos); MERGEDELTA(pD->rot.direction, rot.direction, "d"); MERGEDELTA(pD->rot.pitch, rot.pitch, "d"); MERGEDELTA(pD->rot.roll, rot.roll, "d"); MERGEDELTA(pD->body, body, "u"); if (pD->body > pD->originalBody) { pD->body = pD->originalBody; debug(LOG_SYNC, "Droid %u body was too high after synch, reducing to %u.", pc.droidID, pD->body); } MERGEDELTA(pD->experience, experience, "u"); if (pc.pos.x != pc2.pos.x || pc.pos.y != pc2.pos.y) { // snap droid(if on ground) to terrain level at x,y. if ((asPropulsionStats + pD->asBits[COMP_PROPULSION].nStat)->propulsionType != PROPULSION_TYPE_LIFT) // if not airborne. { pD->pos.z = map_Height(pD->pos.x, pD->pos.y); } } // Doesn't cover all cases, but at least doesn't actively break stuff randomly. switch (pc.order) { case DORDER_MOVE: if (pc.order != pc2.order || pc.orderX != pc2.orderX || pc.orderY != pc2.orderY) { debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s(%d, %d).", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.orderX, pc.orderY); // reroute the droid. orderDroidLoc(pD, pc.order, pc.orderX, pc.orderY, ModeImmediate); } break; case DORDER_ATTACK: if (pc.order != pc2.order || pc.targetID != pc2.targetID) { BASE_OBJECT *obj = IdToPointer(pc.targetID, ANYPLAYER); if (obj != NULL) { debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s(%u).", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.targetID); // remote droid is attacking, not here tho! orderDroidObj(pD, pc.order, IdToPointer(pc.targetID, ANYPLAYER), ModeImmediate); } else { debug(LOG_SYNC, "Droid %u out of synch, would change order from %s to %s(%u), but can't find target.", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.targetID); } } break; case DORDER_NONE: case DORDER_GUARD: if (pc.order != pc2.order) { DROID_ORDER_DATA sOrder; memset(&sOrder, 0, sizeof(DROID_ORDER_DATA)); sOrder.order = pc.order; debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s.", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order)); turnOffMultiMsg(true); moveStopDroid(pD); orderDroidBase(pD, &sOrder); turnOffMultiMsg(false); } break; default: break; // Don't know what to do, but at least won't be actively breaking anything. } MERGECOPY(pD->secondaryOrder, secondaryOrder, "u"); // The old code set this after changing orders, so doing that in case. #undef MERGECOPY #undef MERGEDELTA syncDebugDroid(pD, '>'); // ...and repeat! } NETend(); return true; }
// //////////////////////////////////////////////////////////////////////////// // put down a base plate and start droid building it! BOOL recvBuildStarted() { STRUCTURE_STATS *psStats; DROID *psDroid; UDWORD actionX,actionY; unsigned int typeIndex; uint8_t player; uint16_t x, y, z; int32_t order; uint32_t structRef, structId, targetId,droidID; NETbeginDecode(NET_BUILD); NETuint8_t(&player); NETuint32_t(&structRef); NETuint16_t(&x); NETuint16_t(&y); NETuint32_t(&droidID); NETuint32_t(&structId); NETint32_t(&order); NETuint32_t(&targetId); NETuint16_t(&z); NETend(); // Find structure target for (typeIndex = 0; typeIndex < numStructureStats && asStructureStats[typeIndex].ref != structRef; typeIndex++); psStats = &asStructureStats[typeIndex]; if (IdToDroid(droidID, player, &psDroid)) { // Tell the droid to go to where it needs to in order to build the struct if (getDroidDestination((BASE_STATS *) psStats, x, y, &actionX, &actionY)) { psDroid->order = order; if (psDroid->order == DORDER_LINEBUILD) { psDroid->order = DORDER_BUILD; } psDroid->orderX = x; psDroid->orderY = y; psDroid->psTarStats = (BASE_STATS *) psStats; if (targetId) { setDroidTarget(psDroid, IdToPointer(targetId, ANYPLAYER)); } else { setDroidTarget(psDroid, NULL); } if (IsStatExpansionModule(psStats)) { setUpBuildModule(psDroid); } else { droidStartBuild(psDroid); psDroid->action = DACTION_BUILD; } } // Sync IDs if (psDroid->psTarget) { ((STRUCTURE *) psDroid->psTarget)->id = structId; } } return true; }