static bool AIHasClearLine( Vec2i from, Vec2i to, IsBlockedFunc isBlockedFunc) { // Find all tiles that overlap with the line (from, to) // Uses Bresenham that crosses interiors of tiles HasClearLineData data; data.IsBlocked = isBlockedFunc; data.data = &gMap; return HasClearLineXiaolinWu(from, to, &data); }
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); }
// Perform LOS by casting rays from the centre to the edges, terminating // whenever an obstruction or out-of-range is reached. void DrawBufferLOS(DrawBuffer *buffer, Vec2i center) { int sightRange = gConfig.Game.SightRange; // Note: can be zero LOSData data; data.b = buffer; data.center.x = center.x / TILE_WIDTH - buffer->xStart; data.center.y = center.y / TILE_HEIGHT - buffer->yStart; data.sightRange2 = sightRange * sightRange; // First mark center tile and all adjacent tiles as visible // +-+-+-+ // |V|V|V| // +-+-+-+ // |V|C|V| // +-+-+-+ // |V|V|V| (C=center, V=visible) // +-+-+-+ Vec2i end; for (end.x = data.center.x - 1; end.x < data.center.x + 2; end.x++) { for (end.y = data.center.y - 1; end.y < data.center.y + 2; end.y++) { Tile *tile = GetTile(buffer, end); if (tile) { tile->flags |= MAPTILE_IS_VISIBLE; } } } // Work out the perimeter of the LOS casts Vec2i origin = Vec2iZero(); if (sightRange > 0) { // Limit the perimeter to the sight range origin.x = MAX(origin.x, data.center.x - sightRange); origin.y = MAX(origin.y, data.center.y - sightRange); } Vec2i perimSize = Vec2iScale(Vec2iMinus(data.center, origin), 2); // Start from the top-left cell, and proceed clockwise around end = origin; HasClearLineData lineData; lineData.IsBlocked = IsNextTileBlockedAndSetVisibility; lineData.data = &data; // Top edge for (; end.x < origin.x + perimSize.x; end.x++) { HasClearLineXiaolinWu(data.center, end, &lineData); } // right edge for (; end.y < origin.y + perimSize.y; end.y++) { HasClearLineXiaolinWu(data.center, end, &lineData); } // bottom edge for (; end.x > origin.x; end.x--) { HasClearLineXiaolinWu(data.center, end, &lineData); } // left edge for (; end.y > origin.y; end.y--) { HasClearLineXiaolinWu(data.center, end, &lineData); } // Second pass: make any non-visible obstructions that are adjacent to // visible non-obstructions visible too // This is to ensure runs of walls stay visible for (end.y = origin.y; end.y < origin.y + perimSize.y; end.y++) { for (end.x = origin.x; end.x < origin.x + perimSize.x; end.x++) { Tile *tile = GetTile(buffer, end); if (!tile || !(tile->flags & MAPTILE_NO_SEE)) { continue; } // Check sight range if (data.sightRange2 > 0 && DistanceSquared(data.center, end) >= data.sightRange2) { continue; } SetObstructionVisible(buffer, end, tile); } } }