bool fpathCheck(Position orig, Position dest, PROPULSION_TYPE propulsion) { // We have to be careful with this check because it is called on // load when playing campaign on droids that are on the other // map during missions, and those maps are usually larger. if (!worldOnMap(orig.xy) || !worldOnMap(dest.xy)) { return false; } MAPTILE *origTile = worldTile(findNonblockingPosition(orig, propulsion).xy); MAPTILE *destTile = worldTile(findNonblockingPosition(dest, propulsion).xy); ASSERT_OR_RETURN(false, propulsion != PROPULSION_TYPE_NUM, "Bad propulsion type"); ASSERT_OR_RETURN(false, origTile != NULL && destTile != NULL, "Bad tile parameter"); switch (propulsion) { case PROPULSION_TYPE_PROPELLOR: case PROPULSION_TYPE_WHEELED: case PROPULSION_TYPE_TRACKED: case PROPULSION_TYPE_LEGGED: case PROPULSION_TYPE_HALF_TRACKED: return origTile->limitedContinent == destTile->limitedContinent; case PROPULSION_TYPE_HOVER: return origTile->hoverContinent == destTile->hoverContinent; case PROPULSION_TYPE_LIFT: return true; // assume no map uses skyscrapers to isolate areas default: break; } ASSERT(false, "Should never get here, unknown propulsion !"); return false; // should never get here }
bool fpathCheck(Position orig, Position dest, PROPULSION_TYPE propulsion) { MAPTILE *origTile; MAPTILE *destTile; // We have to be careful with this check because it is called on // load when playing campaign on droids that are on the other // map during missions, and those maps are usually larger. if (!worldOnMap(orig.x, orig.y) || !worldOnMap(dest.x, dest.y)) { return false; } origTile = worldTile(orig.x, orig.y); destTile = worldTile(dest.x, dest.y); ASSERT(propulsion != PROPULSION_TYPE_NUM, "Bad propulsion type"); ASSERT(origTile != NULL && destTile != NULL, "Bad tile parameter"); switch (propulsion) { case PROPULSION_TYPE_PROPELLOR: case PROPULSION_TYPE_WHEELED: case PROPULSION_TYPE_TRACKED: case PROPULSION_TYPE_LEGGED: case PROPULSION_TYPE_SKI: // ?! case PROPULSION_TYPE_HALF_TRACKED: return origTile->limitedContinent == destTile->limitedContinent; case PROPULSION_TYPE_HOVER: return origTile->hoverContinent == destTile->hoverContinent; case PROPULSION_TYPE_JUMP: case PROPULSION_TYPE_LIFT: return true; // FIXME: This is not entirely correct for all possible maps. - Per case PROPULSION_TYPE_NUM: break; } return true; // should never get here }
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(); }
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 }
void rayCast(Vector2i src, Vector2i dst, RAY_CALLBACK callback, void *data) { if (!callback(src, 0, data) || src == dst) // Start at src. { return; // Callback gave up after the first point, or there are no other points. } Vector2i srcM = map_coord(src); Vector2i dstM = map_coord(dst); Vector2i step, tile, cur, end; initSteps(srcM.x, dstM.x, tile.x, step.x, cur.x, end.x); initSteps(srcM.y, dstM.y, tile.y, step.y, cur.y, end.y); Vector2i prev(0, 0); // Dummy initialisation. bool first = true; Vector2i nextX(0, 0), nextY(0, 0); // Dummy initialisations. bool canX = tryStep(tile.x, step.x, cur.x, end.x, nextX.x, nextX.y, src.x, src.y, dst.x, dst.y); bool canY = tryStep(tile.y, step.y, cur.y, end.y, nextY.y, nextY.x, src.y, src.x, dst.y, dst.x); while (canX || canY) { int32_t xDist = abs(nextX.x - src.x) + abs(nextX.y - src.y); int32_t yDist = abs(nextY.x - src.x) + abs(nextY.y - src.y); Vector2i sel; Vector2i selTile; if (canX && (!canY || xDist < yDist)) // The line crosses a vertical grid line next. { sel = nextX; selTile = tile; canX = tryStep(tile.x, step.x, cur.x, end.x, nextX.x, nextX.y, src.x, src.y, dst.x, dst.y); } else // The line crosses a horizontal grid line next. { assert(canY); sel = nextY; selTile = tile; canY = tryStep(tile.y, step.y, cur.y, end.y, nextY.y, nextY.x, src.y, src.x, dst.y, dst.x); } if (!first) { // Find midpoint. Vector2i avg = (prev + sel) / 2; // But make sure it's on the right tile, since it could be off-by-one if the line passes exactly through a grid intersection. avg.x = std::min(std::max(avg.x, world_coord(selTile.x)), world_coord(selTile.x + 1) - 1); avg.y = std::min(std::max(avg.y, world_coord(selTile.y)), world_coord(selTile.y + 1) - 1); if (!worldOnMap(avg) || !callback(avg, iHypot(avg), data)) { return; // Callback doesn't want any more points, or we reached the edge of the map, so return. } } prev = sel; first = false; } // Include the endpoint. if (!worldOnMap(dst)) { return; // Stop, since reached the edge of the map. } callback(dst, iHypot(dst), data); }