/* Give a group of droids an order */ void orderGroupLoc(DROID_GROUP *psGroup, DROID_ORDER order, UDWORD x, UDWORD y) { DROID *psCurr; ASSERT((PTRVALID(psGroup, sizeof(DROID_GROUP)), "orderGroupLoc: invalid droid group")); if(bMultiPlayer) { SendGroupOrderGroup(psGroup,order,x,y,NULL); bMultiPlayer = FALSE; for(psCurr=psGroup->psList; psCurr; psCurr = psCurr->psGrpNext) { orderDroidLoc(psCurr, order, x,y); } bMultiPlayer = TRUE; } else { for(psCurr=psGroup->psList; psCurr; psCurr = psCurr->psGrpNext) { orderDroidLoc(psCurr, order, x,y); } } }
static void driveMoveFollower(DROID *psDroid) { if(driveBumpTime < gameTime) { // Update the driven droid's followers. if(!driveInDriverRange(psDroid)) { //psDroid->secondaryOrder&=~DSS_MOVEHOLD_SET; // Remove secondary order ... this stops the droid from jumping back to GUARD mode ... see order.c #111 - tjc secondarySetState(psDroid, DSO_HALTTYPE, DSS_HALT_GUARD); // if the droid is currently guarding we need to change the order to a move if (psDroid->order==DORDER_GUARD) { orderDroidLoc(psDroid, DORDER_MOVE, psDrivenDroid->pos.x, psDrivenDroid->pos.y, ModeQueue); } else { // otherwise we just adjust its move-to location moveDroidTo(psDroid,psDrivenDroid->pos.x,psDrivenDroid->pos.y); } } } // Stop it when it gets within range of the driver. if(DoFollowRangeCheck) { if(driveInDriverRange(psDroid)) { psDroid->sMove.Status = MOVEINACTIVE; } else { AllInRange = false; } } }
static QScriptValue js_orderDroidLoc(QScriptContext *context, QScriptEngine *) { QScriptValue droidVal = context->argument(0); int id = droidVal.property("id").toInt32(); int player = droidVal.property("player").toInt32(); QScriptValue orderVal = context->argument(1); int x = context->argument(2).toInt32(); int y = context->argument(3).toInt32(); DROID_ORDER order = (DROID_ORDER)orderVal.toInt32(); DROID *psDroid = IdToDroid(id, player); SCRIPT_ASSERT(context, psDroid, "Droid id %d not found belonging to player %d", id, player); SCRIPT_ASSERT(context, worldOnMap(x, y), "Outside map bounds (%d, %d)", x, y); orderDroidLoc(psDroid, order, x, y, ModeQueue); return QScriptValue(); }
// Give a Droid an order to a location bool scrOrderDroidLoc(void) { DROID *psDroid; DROID_ORDER order; SDWORD x, y; if (!stackPopParams(4, ST_DROID, &psDroid, VAL_INT, &order, VAL_INT, &x, VAL_INT, &y)) { return false; } ASSERT(psDroid != NULL, "scrOrderUnitLoc: Invalid unit pointer"); if (psDroid == NULL) { return false; } if (order != DORDER_MOVE && order != DORDER_SCOUT) { ASSERT(false, "scrOrderUnitLoc: Invalid order"); return false; } if (x < 0 || x > world_coord(mapWidth) || y < 0 || y > world_coord(mapHeight)) { ASSERT(false, "scrOrderUnitLoc: Invalid location"); return false; } orderDroidLoc(psDroid, order, x, y, ModeQueue); return true; }
// Give a Droid an order to a location BOOL scrOrderDroidLoc(void) { DROID *psDroid; SDWORD order, x,y; if (!stackPopParams(4, ST_DROID, &psDroid, VAL_INT, &order, VAL_INT, &x, VAL_INT, &y)) { return FALSE; } ASSERT((PTRVALID(psDroid, sizeof(DROID)), "scrOrderUnitLoc: Invalid unit pointer")); if (psDroid == NULL) { return FALSE; } if (order != DORDER_MOVE && order != DORDER_SCOUT) { ASSERT((FALSE, "scrOrderUnitLoc: Invalid order")); return FALSE; } if (x < 0 || x > (SDWORD)(mapWidth << TILE_SHIFT) || y < 0 || y > (SDWORD)(mapHeight << TILE_SHIFT)) { ASSERT((FALSE, "scrOrderUnitLoc: Invalid location")); return FALSE; } orderDroidLoc(psDroid, order, (UDWORD)x,(UDWORD)y); return TRUE; }
// //////////////////////////////////////////////////////////////////////////// // receive droid creation information from other players BOOL recvDroid(NETQUEUE queue) { DROID_TEMPLATE* pT; DROID* psDroid; uint8_t player; uint32_t id; Position pos; uint32_t templateID; BOOL haveInitialOrders; INITIAL_DROID_ORDERS initialOrders; NETbeginDecode(queue, GAME_DROID); { NETuint8_t(&player); NETuint32_t(&id); NETPosition(&pos); NETuint32_t(&templateID); NETbool(&haveInitialOrders); if (haveInitialOrders) { NETuint32_t(&initialOrders.secondaryOrder); NETint32_t(&initialOrders.moveToX); NETint32_t(&initialOrders.moveToY); NETuint32_t(&initialOrders.factoryId); // For making scripts happy. } pT = IdToTemplate(templateID, player); } NETend(); ASSERT( player < MAX_PLAYERS, "invalid player %u", player); debug(LOG_LIFE, "<=== getting Droid from %u id of %u ",player,id); if ((pos.x == 0 && pos.y == 0) || pos.x > world_coord(mapWidth) || pos.y > world_coord(mapHeight)) { debug(LOG_ERROR, "Received bad droid position (%d, %d) from %d about p%d (%s)", (int)pos.x, (int)pos.y, queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); return false; } // If we can not find the template ask for the entire droid instead if (!pT) { debug(LOG_ERROR, "Packet from %d refers to non-existent template %u, [%s : p%d]", queue.index, templateID, isHumanPlayer(player) ? "Human" : "AI", player); return false; } // Create that droid on this machine. psDroid = reallyBuildDroid(pT, pos.x, pos.y, player, false); // If we were able to build the droid set it up if (psDroid) { psDroid->id = id; addDroid(psDroid, apsDroidLists); if (haveInitialOrders) { psDroid->secondaryOrder = initialOrders.secondaryOrder; orderDroidLoc(psDroid, DORDER_MOVE, initialOrders.moveToX, initialOrders.moveToY, ModeImmediate); cbNewDroid(IdToStruct(initialOrders.factoryId, ANYPLAYER), psDroid); } } else { debug(LOG_ERROR, "Packet from %d cannot create droid for p%d (%s)!", queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); #ifdef DEBUG CONPRINTF(ConsoleString, (ConsoleString, "MULTIPLAYER: Couldn't build a remote droid, relying on checking to resync")); #endif return false; } 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; }
/* Attempts to find a new location for the tracking camera to go to, or a new object (target) for it to track. */ static void findSomethingInteresting(void) { enum { SEEK_DROID, SEEK_TARGET, SEEK_LAST, SEEK_OVERRIDE, } type; UDWORD player,otherPlayer; DROID *psDroid; UDWORD numWith; BOOL bSeekOnlyLocations; UDWORD i; BOOL bHaveHuman = false; PROPULSION_STATS *psPropStats; //--- //---- /* There may be droids, so don't rule it out */ bSeekOnlyLocations = false; /* Check all the droid lists, looking for empty ones */ for(i = 0,numWith = 0; i<MAX_PLAYERS; i++) { /* Keep a count of how many are empty */ if(apsDroidLists[i]) { /* We got one */ numWith++; if(i<MAX_PLAYERS-2) { bHaveHuman = true; } } } /* If they were all empty, then record this fact and only seek locations */ /* We need two sides for this to work! */ if(numWith<2 || !bHaveHuman) { bSeekOnlyLocations = true; } /* Keep going until we get one */ // while(!gotNewTarget) // { /* Are we only to seek locations? */ if(bSeekOnlyLocations) { /* Then force the switch outcome - hacky I know, but same as if else in code */ type = SEEK_OVERRIDE; } else { /* We're off hunting droids */ type = rand()%2 == 0? SEEK_DROID : SEEK_TARGET; } /* Check which */ switch (type) { /* Go after a droid, or a droid location */ case SEEK_DROID: case SEEK_TARGET: /* Choose a player at random */ player = rand()%MAX_PLAYERS; /* Have they got any droids? */ while(apsDroidLists[player]==NULL) { /* Nope, so choose another one until we get one with droids */ player = rand()%MAX_PLAYERS; } /* Choose a player at random */ otherPlayer = rand()%MAX_PLAYERS; /* Have they got any structures? Make sure it's not their own we're checking! */ while(apsStructLists[otherPlayer]==NULL || otherPlayer==player) { /* Nope, so choose another one until we get one with droids */ otherPlayer = rand()%MAX_PLAYERS; } /* If there was a droid last time, deselect it */ if(psLastDroid && !psLastDroid->died) { psLastDroid->selected = false; } /* Jump to droid and track */ psDroid = getDroidForDemo(player); /* Only do if we've got a droid and an enemy building to attack */ if(psDroid && apsStructLists[otherPlayer]) { psDroid->selected = true; selectedPlayer = player; realSelectedPlayer = selectedPlayer; psLastDroid = psDroid; // if(orderState(psDroid,DORDER_ATTACK) == false) // { orderDroidLoc(psDroid,DORDER_MOVE, apsStructLists[otherPlayer]->pos.x, apsStructLists[otherPlayer]->pos.y, ModeQueue); // } if(!getWarCamStatus()) { /* Start the tracking */ camToggleStatus(); } else { camToggleStatus(); processWarCam(); camToggleStatus(); } psPropStats = asPropulsionStats + psDroid->asBits[COMP_PROPULSION].nStat; if ( psPropStats->propulsionType == PROPULSION_TYPE_LIFT ) { /* Track vtols for longer */ demoCamInterval = 3*DEFAULT_DEMO_INTERVAL; } else { demoCamInterval = DEFAULT_DEMO_INTERVAL; } } break; /* Go to a new location cos there's no droids left in the world....ahhhhhhh*/ case SEEK_OVERRIDE: requestRadarTrack((16 + rand()%(mapWidth-31))*TILE_UNITS, (16 + rand()%(mapHeight-31)) * TILE_UNITS ); break; default: break; } // } }
/* Updates the camera's point of interest if it's time to */ void processDemoCam( void ) { UDWORD firstPlayer,otherPlayer; DROID *psDroid; BOOL bSkipOrder = false; UDWORD i,numWith; /* Is the demo camera actually active? */ if(presentStatus == DC_INACTIVE) { /* Nope, so get out */ return; } /* Is it time for a new target? */ if( gameTime > (lastCameraMove + demoCamInterval) ) { lastCameraMove = gameTime; /* The bones */ findSomethingInteresting(); // player.r.x = DEG(-90); } /* Otherwise, just send a droid off to war */ else if(gameTime > (lastDroidSending + GAME_TICKS_PER_SEC) ) { /* Check all the droid lists, looking for empty ones */ for(i = 0,numWith = 0; i<MAX_PLAYERS; i++) { /* Keep a count of how many are empty */ if(apsDroidLists[i]) { /* We got one */ numWith++; } } /* If they were all empty, then record this fact and only seek locations */ /* We need two sides for this to work! */ if(numWith<2) { bSkipOrder = true; } if(!bSkipOrder) { lastDroidSending = gameTime; /* Choose a player at random */ firstPlayer = rand()%MAX_PLAYERS; /* Have they got any droids? */ while(apsDroidLists[firstPlayer]==NULL) { /* Nope, so choose another one until we get one with droids */ firstPlayer = rand()%MAX_PLAYERS; } /* Choose a player at random */ otherPlayer = rand()%MAX_PLAYERS; /* Have they got any structures? Make sure it's not their own we're checking! */ while(apsStructLists[otherPlayer]==NULL || otherPlayer==firstPlayer) { /* Nope, so choose another one until we get one with droids */ otherPlayer = rand()%MAX_PLAYERS; } psDroid = getDroidForDemo(firstPlayer); /* Only do this if we've got a droid and an enemy building to attack! */ if(psDroid && apsStructLists[otherPlayer]) { if( (orderState(psDroid,DORDER_NONE) == true) || ((orderState(psDroid,DORDER_GUARD) == true) && (psDroid->action == DACTION_NONE))) { /* Make the droid attack the building - it'll indirectly route there too */ orderDroidLoc(psDroid,DORDER_SCOUT, apsStructLists[otherPlayer]->pos.x, apsStructLists[otherPlayer]->pos.y, ModeQueue); } } } } }
// //////////////////////////////////////////////////////////////////////////// // receive droid creation information from other players bool recvDroid(NETQUEUE queue) { DROID_TEMPLATE t, *pT = &t; DROID *psDroid; uint8_t player; uint32_t id; Position pos; bool haveInitialOrders; INITIAL_DROID_ORDERS initialOrders; NETbeginDecode(queue, GAME_DEBUG_ADD_DROID); { int32_t droidType; NETuint8_t(&player); NETuint32_t(&id); NETPosition(&pos); NETqstring(pT->name); pT->id = pT->name; NETint32_t(&droidType); NETuint8_t(&pT->asParts[COMP_BODY]); NETuint8_t(&pT->asParts[COMP_BRAIN]); NETuint8_t(&pT->asParts[COMP_PROPULSION]); NETuint8_t(&pT->asParts[COMP_REPAIRUNIT]); NETuint8_t(&pT->asParts[COMP_ECM]); NETuint8_t(&pT->asParts[COMP_SENSOR]); NETuint8_t(&pT->asParts[COMP_CONSTRUCT]); NETint8_t(&pT->numWeaps); for (int i = 0; i < pT->numWeaps; i++) { NETuint8_t(&pT->asWeaps[i]); } NETbool(&haveInitialOrders); if (haveInitialOrders) { NETuint32_t(&initialOrders.secondaryOrder); NETint32_t(&initialOrders.moveToX); NETint32_t(&initialOrders.moveToY); NETuint32_t(&initialOrders.factoryId); // For making scripts happy. } pT->droidType = (DROID_TYPE)droidType; } NETend(); if (!getDebugMappingStatus() && bMultiPlayer) { debug(LOG_WARNING, "Failed to add droid for player %u.", NetPlay.players[queue.index].position); return false; } ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "invalid player %u", player); debug(LOG_LIFE, "<=== getting Droid from %u id of %u ", player, id); if ((pos.x == 0 && pos.y == 0) || pos.x > world_coord(mapWidth) || pos.y > world_coord(mapHeight)) { debug(LOG_ERROR, "Received bad droid position (%d, %d) from %d about p%d (%s)", (int)pos.x, (int)pos.y, queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); return false; } // Create that droid on this machine. psDroid = reallyBuildDroid(pT, pos, player, false); // If we were able to build the droid set it up if (psDroid) { psDroid->id = id; addDroid(psDroid, apsDroidLists); if (haveInitialOrders) { psDroid->secondaryOrder = initialOrders.secondaryOrder; psDroid->secondaryOrderPending = psDroid->secondaryOrder; orderDroidLoc(psDroid, DORDER_MOVE, initialOrders.moveToX, initialOrders.moveToY, ModeImmediate); cbNewDroid(IdToStruct(initialOrders.factoryId, ANYPLAYER), psDroid); } syncDebugDroid(psDroid, '+'); } else { debug(LOG_ERROR, "Packet from %d cannot create droid for p%d (%s)!", queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); #ifdef DEBUG CONPRINTF(ConsoleString, (ConsoleString, "MULTIPLAYER: Couldn't build a remote droid, relying on checking to resync")); #endif return false; } return true; }