// Check that we are still close to the start of the A* path, // and the end of the path is close to our goal static int AStarCloseToPath( AIGotoContext *c, Vec2i currentTile, Vec2i goalTile) { Vec2i *pathTile; Vec2i *pathEnd; if (!c || c->PathIndex >= (int)ASPathGetCount(c->Path) - 1) // at end of path { return 0; } // Check if we're too far from the current start of the path pathTile = ASPathGetNode(c->Path, c->PathIndex); if (CHEBYSHEV_DISTANCE( currentTile.x, currentTile.y, pathTile->x, pathTile->y) > 2) { return 0; } // Check if we're too far from the end of the path pathEnd = ASPathGetNode(c->Path, ASPathGetCount(c->Path) - 1); if (CHEBYSHEV_DISTANCE( goalTile.x, goalTile.y, pathEnd->x, pathEnd->y) > 0) { return 0; } return 1; }
static TActor *AIGetClosestActor(Vec2i from, int (*compFunc)(TActor *)) { // Search all the actors and find the closest one that // satisfies the condition TActor *closest = NULL; int minDistance = -1; for (int i = 0; i < (int)gActors.size; i++) { TActor *a = CArrayGet(&gActors, i); if (!a->isInUse || a->dead) { continue; } // Never target invulnerables or civilians if (a->flags & (FLAGS_INVULNERABLE | FLAGS_PENALTY)) { continue; } if (compFunc(a)) { int distance = CHEBYSHEV_DISTANCE(from.x, from.y, a->Pos.x, a->Pos.y); if (!closest || distance < minDistance) { minDistance = distance; closest = a; } } } return closest; }
static bool CanSeeAPlayer(const TActor *a) { const Vec2i realPos = Vec2iFull2Real(a->Pos); CA_FOREACH(const PlayerData, p, gPlayerDatas) if (!IsPlayerAlive(p)) { continue; } const TActor *player = ActorGetByUID(p->ActorUID); const Vec2i playerRealPos = Vec2iFull2Real(player->Pos); // Can see player if: // - Clear line of sight, and // - If they are close, or if facing and they are not too far if (!AIHasClearShot(realPos, playerRealPos)) { continue; } const int distance = CHEBYSHEV_DISTANCE( realPos.x, realPos.y, playerRealPos.x, playerRealPos.y); const bool isClose = distance < 16 * 4; const bool isNotTooFar = distance < 16 * 30; if (isClose || (isNotTooFar && AIIsFacing(a, player->Pos, a->direction))) { return true; } CA_FOREACH_END() return false; }
static bool IsCloseToPlayer(const Vec2i fullPos, const int fullDistance) { TActor *closestPlayer = AIGetClosestPlayer(fullPos); return closestPlayer && CHEBYSHEV_DISTANCE( fullPos.x, fullPos.y, closestPlayer->Pos.x, closestPlayer->Pos.y) < fullDistance; }
static bool CanSeeAPlayer(const TActor *a) { const Vec2i realPos = Vec2iFull2Real(a->Pos); for (int i = 0; i < MAX_PLAYERS; i++) { if (!IsPlayerAlive(i)) { continue; } const TActor *player = CArrayGet(&gActors, gPlayerIds[i]); const Vec2i playerRealPos = Vec2iFull2Real(player->Pos); // Can see player if: // - Clear line of sight, and // - If they are close, or if facing and they are not too far if (!AIHasClearShot(realPos, playerRealPos)) { continue; } const int distance = CHEBYSHEV_DISTANCE( realPos.x, realPos.y, playerRealPos.x, playerRealPos.y); const bool isClose = distance < 16 * 4; const bool isNotTooFar = distance < 16 * 30; if (isClose || (isNotTooFar && IsFacing(realPos, playerRealPos, a->direction))) { return true; } } return false; }
void SoundPlayAtPlusDistance( SoundDevice *device, Mix_Chunk *data, const Vec2i pos, const int plusDistance) { int distance, bearing; Vec2i closestLeftEar, closestRightEar; Vec2i origin; // Find closest set of ears to the sound if (CHEBYSHEV_DISTANCE( pos.x, pos.y, device->earLeft1.x, device->earLeft1.y) < CHEBYSHEV_DISTANCE( pos.x, pos.y, device->earLeft2.x, device->earLeft2.y)) { closestLeftEar = device->earLeft1; } else { closestLeftEar = device->earLeft2; } if (CHEBYSHEV_DISTANCE( pos.x, pos.y, device->earRight1.x, device->earRight1.y) < CHEBYSHEV_DISTANCE( pos.x, pos.y, device->earRight2.x, device->earRight2.y)) { closestRightEar = device->earRight1; } else { closestRightEar = device->earRight2; } origin = CalcClosestPointOnLineSegmentToPoint( closestLeftEar, closestRightEar, pos); CalcChebyshevDistanceAndBearing(origin, pos, &distance, &bearing); HasClearLineData lineData; lineData.IsBlocked = IsPosNoSee; lineData.data = &gMap; bool isMuffled = false; if (!HasClearLineXiaolinWu(pos, origin, &lineData)) { isMuffled = true; } SoundPlayAtPosition( &gSoundDevice, data, distance + plusDistance, bearing, isMuffled); }
static bool TryPlacePickup(HealthPickups *h) { Vec2i size = Vec2iNew(HEALTH_W, HEALTH_H); // Attempt to place one in unexplored area for (int i = 0; i < 100; i++) { const Vec2i v = MapGenerateFreePosition(h->map, size); if (!Vec2iIsZero(v) && !MapGetTile(h->map, Vec2iToTile(v))->isVisited) { MapPlaceHealth(v); return true; } } // Attempt to place one in out-of-sight area for (int i = 0; i < 100; i++) { const Vec2i v = MapGenerateFreePosition(h->map, size); const Vec2i fullpos = Vec2iReal2Full(v); const TActor *closestPlayer = AIGetClosestPlayer(fullpos); if (!Vec2iIsZero(v) && (!closestPlayer || CHEBYSHEV_DISTANCE( fullpos.x, fullpos.y, closestPlayer->Pos.x, closestPlayer->Pos.y) >= 256 * 150)) { MapPlaceHealth(v); return true; } } // Attempt to place one anyway for (int i = 0; i < 100; i++) { const Vec2i v = MapGenerateFreePosition(h->map, size); if (!Vec2iIsZero(v)) { MapPlaceHealth(v); return true; } } return false; }
void CalcChebyshevDistanceAndBearing( Vector2i origin, Vector2i target, int *distance, int *bearing) { // short circuit if origin and target same if (origin.x == target.x && origin.y == target.y) { *distance = 0; *bearing = 0; } else { double angle; *distance = CHEBYSHEV_DISTANCE(origin.x, origin.y, target.x, target.y); angle = ToDegrees(atan2(target.y - origin.y, target.x - origin.x)); // convert angle to bearings // first rotate so 0 angle = 0 bearing angle -= 90.0; // then reflect about Y axis angle = 360 - angle; *bearing = (int)floor(angle + 0.5); } }
TActor *AIGetClosestPlayer(Vec2i fullpos) { int i; int minDistance = -1; TActor *closestPlayer = NULL; for (i = 0; i < gOptions.numPlayers; i++) { if (IsPlayerAlive(i)) { TActor *p = CArrayGet(&gActors, gPlayerIds[i]); Vec2i pPos = Vec2iFull2Real(p->Pos); int distance = CHEBYSHEV_DISTANCE( fullpos.x, fullpos.y, pPos.x, pPos.y); if (!closestPlayer || distance < minDistance) { closestPlayer = p; minDistance = distance; } } } return closestPlayer; }
static void PlaceBaddie(TActor *actor) { int hasPlaced = 0; int i; for (i = 0; i < 100; i++) // Don't try forever trying to place baddie { // Try spawning out of players' sights actor->Pos.x = (rand() % (gMap.Size.x * TILE_WIDTH)) << 8; actor->Pos.y = (rand() % (gMap.Size.y * TILE_HEIGHT)) << 8; TActor *closestPlayer = AIGetClosestPlayer(actor->Pos); if (closestPlayer && CHEBYSHEV_DISTANCE( actor->Pos.x, actor->Pos.y, closestPlayer->Pos.x, closestPlayer->Pos.y) >= 256 * 150 && IsActorPositionValid(actor)) { hasPlaced = 1; break; } } // Keep trying, but this time try spawning anywhere, even close to player while (!hasPlaced) { actor->Pos.x = (rand() % (gMap.Size.x * TILE_WIDTH)) << 8; actor->Pos.y = (rand() % (gMap.Size.y * TILE_HEIGHT)) << 8; if (IsActorPositionValid(actor)) { hasPlaced = 1; break; } } ActorInit(actor); if (!(actor->flags & FLAGS_SLEEPALWAYS) && rand() % 100 < gBaddieCount) { actor->flags &= ~FLAGS_SLEEPING; } }
NetMsgVec2i PlaceBaddie(Map *map) { // Don't try forever trying to place baddie for (int i = 0; i < 100; i++) { // Try spawning out of players' sights const Vec2i pos = Vec2iReal2Full(Vec2iNew( rand() % (map->Size.x * TILE_WIDTH), rand() % (map->Size.y * TILE_HEIGHT))); const TActor *closestPlayer = AIGetClosestPlayer(pos); if (closestPlayer && CHEBYSHEV_DISTANCE( pos.x, pos.y, closestPlayer->Pos.x, closestPlayer->Pos.y) >= 256 * 150 && MapIsTileAreaClear(map, pos, Vec2iNew(ACTOR_W, ACTOR_H))) { NetMsgVec2i posNet; posNet.x = pos.x; posNet.y = pos.y; return posNet; } } // Keep trying, but this time try spawning anywhere, // even close to player for (;;) { const Vec2i pos = Vec2iReal2Full(Vec2iNew( rand() % (map->Size.x * TILE_WIDTH), rand() % (map->Size.y * TILE_HEIGHT))); if (MapIsTileAreaClear(map, pos, Vec2iNew(ACTOR_W, ACTOR_H))) { NetMsgVec2i posNet; posNet.x = pos.x; posNet.y = pos.y; return posNet; } } }