// Check if the map tile at a location blocks a droid bool fpathBaseBlockingTile(SDWORD x, SDWORD y, PROPULSION_TYPE propulsion, int mapIndex, FPATH_MOVETYPE moveType) { /* All tiles outside of the map and on map border are blocking. */ if (x < 1 || y < 1 || x > mapWidth - 1 || y > mapHeight - 1) { return true; } /* Check scroll limits (used in campaign to partition the map. */ if (propulsion != PROPULSION_TYPE_LIFT && (x < scrollMinX + 1 || y < scrollMinY + 1 || x >= scrollMaxX - 1 || y >= scrollMaxY - 1)) { // coords off map - auto blocking tile return true; } unsigned aux = auxTile(x, y, mapIndex); int auxMask = 0; switch (moveType) { case FMT_MOVE: auxMask = AUXBITS_NONPASSABLE; break; // do not wish to shoot our way through enemy buildings, but want to go through friendly gates (without shooting them) case FMT_ATTACK: auxMask = AUXBITS_OUR_BUILDING; break; // move blocked by friendly building, assuming we do not want to shoot it up en route case FMT_BLOCK: auxMask = AUXBITS_BLOCKING; break; // Do not wish to tunnel through closed gates or buildings. } unsigned unitbits = prop2bits(propulsion); // TODO - cache prop2bits to psDroid, and pass in instead of propulsion type if ((unitbits & FEATURE_BLOCKED) != 0 && (aux & auxMask) != 0) { return true; // move blocked by building, and we cannot or do not want to shoot our way through anything } // the MAX hack below is because blockTile() range does not include player-specific versions... return (blockTile(x, y, MAX(0, mapIndex - MAX_PLAYERS)) & unitbits) != 0; // finally check if move is blocked by propulsion related factors }
// Check if the map tile at a location blocks a droid BOOL fpathBaseBlockingTile(SDWORD x, SDWORD y, PROPULSION_TYPE propulsion, int mapIndex, FPATH_MOVETYPE moveType) { uint8_t aux, unitbits = prop2bits(propulsion); // TODO - cache prop2bits to psDroid, and pass in instead of propulsion type /* All tiles outside of the map and on map border are blocking. */ if (x < 1 || y < 1 || x > mapWidth - 1 || y > mapHeight - 1) { return true; } /* Check scroll limits (used in campaign to partition the map. */ if (propulsion != PROPULSION_TYPE_LIFT && (x < scrollMinX + 1 || y < scrollMinY + 1 || x >= scrollMaxX - 1 || y >= scrollMaxY - 1)) { // coords off map - auto blocking tile return true; } aux = auxTile(x, y, mapIndex); if ((unitbits & FEATURE_BLOCKED) && ((moveType == FMT_MOVE && (aux & AUXBITS_ANY_BUILDING)) // do not wish to shoot our way through enemy buildings || (aux & AUXBITS_OUR_BUILDING))) // move blocked by friendly building, assuming we do not want to shoot it up en route { return true; // move blocked by building, and we cannot or do not want to shoot our way through anything } // the MAX hack below is because blockTile() range does not include player-specific versions... return (blockTile(x, y, MAX(0, mapIndex - MAX_PLAYERS)) & unitbits); // finally check if move is blocked by propulsion related factors }
void fpathSetBlockingMap(PATHJOB *psJob) { if (fpathCurrentGameTime != gameTime) { // New tick, remove maps which are no longer needed. fpathCurrentGameTime = gameTime; fpathPrevBlockingMaps.swap(fpathBlockingMaps); fpathBlockingMaps.clear(); } // Figure out which map we are looking for. PathBlockingType type; type.gameTime = gameTime; type.propulsion = psJob->propulsion; type.owner = psJob->owner; type.moveType = psJob->moveType; // Find the map. std::list<PathBlockingMap>::iterator i = std::find(fpathBlockingMaps.begin(), fpathBlockingMaps.end(), type); if (i == fpathBlockingMaps.end()) { // Didn't find the map, so i does not point to a map. fpathBlockingMaps.push_back(PathBlockingMap()); --i; // i now points to an empty map with no data. Fill the map. i->type = type; std::vector<bool> &map = i->map; map.resize(mapWidth*mapHeight); uint32_t checksumMap = 0, checksumDangerMap = 0, factor = 0; for (int y = 0; y < mapHeight; ++y) for (int x = 0; x < mapWidth; ++x) { map[x + y*mapWidth] = fpathBaseBlockingTile(x, y, type.propulsion, type.owner, type.moveType); checksumMap ^= map[x + y*mapWidth]*(factor = 3*factor + 1); } if (!isHumanPlayer(type.owner) && type.moveType == FMT_MOVE) { std::vector<bool> &dangerMap = i->dangerMap; dangerMap.resize(mapWidth*mapHeight); for (int y = 0; y < mapHeight; ++y) for (int x = 0; x < mapWidth; ++x) { dangerMap[x + y*mapWidth] = auxTile(x, y, type.owner) & AUXBITS_THREAT; checksumDangerMap ^= map[x + y*mapWidth]*(factor = 3*factor + 1); } } syncDebug("blockingMap(%d,%d,%d,%d) = %08X %08X", gameTime, psJob->propulsion, psJob->owner, psJob->moveType, checksumMap, checksumDangerMap); } else { syncDebug("blockingMap(%d,%d,%d,%d) = cached", gameTime, psJob->propulsion, psJob->owner, psJob->moveType); } // i now points to the correct map. Make psJob->blockingMap point to it. psJob->blockingMap = &*i; }
// not a direct script function but a helper for scrSkDefenseLocation and scrSkDefenseLocationB static bool defenseLocation(bool variantB) { SDWORD *pX, *pY, statIndex, statIndex2; UDWORD x, y, gX, gY, dist, player, nearestSoFar, count; GATEWAY *psGate, *psChosenGate; DROID *psDroid; UDWORD x1, x2, x3, x4, y1, y2, y3, y4; bool noWater; UDWORD minCount; UDWORD offset; if (!stackPopParams(6, VAL_REF | VAL_INT, &pX, VAL_REF | VAL_INT, &pY, ST_STRUCTURESTAT, &statIndex, ST_STRUCTURESTAT, &statIndex2, ST_DROID, &psDroid, VAL_INT, &player)) { debug(LOG_ERROR, "defenseLocation: failed to pop"); return false; } if (player >= MAX_PLAYERS) { ASSERT(false, "defenseLocation:player number is too high"); return false; } ASSERT_OR_RETURN(false, statIndex < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex, numStructureStats); ASSERT_OR_RETURN(false, statIndex2 < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex2, numStructureStats); STRUCTURE_STATS *psWStats = (asStructureStats + statIndex2); // check for wacky coords. if (*pX < 0 || *pX > world_coord(mapWidth) || *pY < 0 || *pY > world_coord(mapHeight) ) { goto failed; } x = map_coord(*pX); // change to tile coords. y = map_coord(*pY); // go down the gateways, find the nearest gateway with >1 empty tiles nearestSoFar = UDWORD_MAX; psChosenGate = NULL; for (psGate = gwGetGateways(); psGate; psGate = psGate->psNext) { if (auxTile(psGate->x1, psGate->y1, player) & AUXBITS_THREAT) { continue; // enemy can shoot there, not safe to build } count = 0; noWater = true; // does it have >1 tile unoccupied. if (psGate->x1 == psGate->x2) { // vert //skip gates that are too short if (variantB && (psGate->y2 - psGate->y1) <= 2) { continue; } gX = psGate->x1; for (gY = psGate->y1; gY <= psGate->y2; gY++) { if (! TileIsOccupied(mapTile(gX, gY))) { count++; } if (terrainType(mapTile(gX, gY)) == TER_WATER) { noWater = false; } } } else { // horiz //skip gates that are too short if (variantB && (psGate->x2 - psGate->x1) <= 2) { continue; } gY = psGate->y1; for (gX = psGate->x1; gX <= psGate->x2; gX++) { if (! TileIsOccupied(mapTile(gX, gY))) { count++; } if (terrainType(mapTile(gX, gY)) == TER_WATER) { noWater = false; } } } if (variantB) { minCount = 2; } else { minCount = 1; } if (count > minCount && noWater) //<NEW> min 2 tiles { // ok it's free. Is it the nearest one yet? /* Get gateway midpoint */ gX = (psGate->x1 + psGate->x2) / 2; gY = (psGate->y1 + psGate->y2) / 2; /* Estimate the distance to it */ dist = iHypot(x - gX, y - gY); /* Is it best we've found? */ if (dist < nearestSoFar && dist < 30) { /* Yes, then keep a record of it */ nearestSoFar = dist; psChosenGate = psGate; } } } if (!psChosenGate) // we have a gateway. { goto failed; } // find an unnocupied tile on that gateway. if (psChosenGate->x1 == psChosenGate->x2) { // vert gX = psChosenGate->x1; for (gY = psChosenGate->y1; gY <= psChosenGate->y2; gY++) { if (! TileIsOccupied(mapTile(gX, gY))) { y = gY; x = gX; break; } } } else { // horiz gY = psChosenGate->y1; for (gX = psChosenGate->x1; gX <= psChosenGate->x2; gX++) { if (! TileIsOccupied(mapTile(gX, gY))) { y = gY; x = gX; break; } } } // back to world coords and store result. *pX = world_coord(x) + (TILE_UNITS / 2); // return centre of tile. *pY = world_coord(y) + (TILE_UNITS / 2); scrFunctionResult.v.bval = true; if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // success { return false; } // order the droid to build two walls, one either side of the gateway. // or one in the case of a 2 size gateway. //find center of the gateway x = (psChosenGate->x1 + psChosenGate->x2) / 2; y = (psChosenGate->y1 + psChosenGate->y2) / 2; //find start pos of the gateway x1 = world_coord(psChosenGate->x1) + (TILE_UNITS / 2); y1 = world_coord(psChosenGate->y1) + (TILE_UNITS / 2); if (variantB) { offset = 2; } else { offset = 1; } if (psChosenGate->x1 == psChosenGate->x2) //vert { x2 = x1; //vert: end x pos of the first section = start x pos y2 = world_coord(y - 1) + TILE_UNITS / 2; //start y loc of the first sec x3 = x1; y3 = world_coord(y + offset) + TILE_UNITS / 2; } else //hor { x2 = world_coord(x - 1) + TILE_UNITS / 2; y2 = y1; x3 = world_coord(x + offset) + TILE_UNITS / 2; y3 = y1; } //end coords of the second section x4 = world_coord(psChosenGate->x2) + TILE_UNITS / 2; y4 = world_coord(psChosenGate->y2) + TILE_UNITS / 2; // first section. if (x1 == x2 && y1 == y2) //first sec is 1 tile only: ((2 tile gate) or (3 tile gate and first sec)) { orderDroidStatsLocDir(psDroid, DORDER_BUILD, psWStats, x1, y1, 0, ModeQueue); } else { orderDroidStatsTwoLocDir(psDroid, DORDER_LINEBUILD, psWStats, x1, y1, x2, y2, 0, ModeQueue); } // second section if (x3 == x4 && y3 == y4) { orderDroidStatsLocDirAdd(psDroid, DORDER_BUILD, psWStats, x3, y3, 0); } else { orderDroidStatsTwoLocDirAdd(psDroid, DORDER_LINEBUILD, psWStats, x3, y3, x4, y4, 0); } return true; failed: scrFunctionResult.v.bval = false; if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // failed! { return false; } return true; }