Skip to main content
Became Hot Network Question
Changed the title to reflect the actual question being asked here
Link
Philipp
  • 123.2k
  • 28
  • 264
  • 345

A range attack Line-of-sight algorithm for turntile-based tactics

added 2 characters in body
Source Link
Clyeh
  • 13
  • 4

SupercoverSupercover

Supercover

Supercover

Source Link
Clyeh
  • 13
  • 4

A range attack algorithm for turn-based tactics

I've tried Bresenham line and Raycasting, but they don't quite give the results I need (examples in the screenshot).

'&' - character

'+' - cells that can be attacked by the character

'#' - walls

Supercover:

Supercover

Supercover

Bresenham:

Bresenham

Bresenham

As can be seen in the screenshot, a character can attack a cell, but if moved to it, it cannot attack from there to its original position.

Is there a fair algorithm so that a character who can attack an enemy can be attacked by the enemy in return.

Perhaps the problem is in my code?

void FieldOfSight(Field &field, const Vector2i &player, float distance,
                  int type)
{
    Vector2i endPosition;
    float    radians = 0;
    float    step    = M_PI / (distance * 10);

    while (radians < 2 * M_PI) {
        endPosition.x = player.x + round(cos(radians) * (distance));
        endPosition.y = player.y + round(sin(radians) * (distance));
        if (endPosition.y >= field.GetHeight())
            endPosition.y = field.GetHeight() - 1;
        else if (endPosition.y <= 0)
            endPosition.y = 0;
        if (endPosition.x >= field.GetWidth())
            endPosition.x = field.GetWidth() - 1;
        else if (endPosition.x <= 0)
            endPosition.x = 0;
        radians += step;

        switch (type) {
        case (0):
            SupercoverLine(field, player, endPosition);
            break;
        case (1):
            WalkLine(field, player, endPosition);
            break;
        case (2):
            BresenhamLine(field, player, endPosition);
            break;
        }
    }
}

void SupercoverLine(Field &field, int x0, int y0, int x1, int y1)
{
    int dx = x1 - x0, dy = y1 - y0;
    int nx = abs(dx), ny = abs(dy);
    int sign_x = dx > 0 ? 1 : -1, sign_y = dy > 0 ? 1 : -1;

    int px = x0, py = y0;

    field.SetFG(px, py, 8);

    for (int ix = 0, iy = 0; ix < nx || iy < ny;) {
        int decision = (1 + 2 * ix) * ny - (1 + 2 * iy) * nx;
        if (decision == 0) {
            px += sign_x;
            py += sign_y;
            if (field[py - sign_y][px] == 1 && field[py][px - sign_x] == 1)
                break;
            ++ix;
            ++iy;
        } else if (decision < 0) {
            px += sign_x;
            ++ix;
        } else {
            py += sign_y;
            ++iy;
        }
        if (sqrt((x0 - px) * (x0 - px) + (y0 - py) * (y0 - py)) > 5 ||
            field[py][px] == 1)
            break;
        field.SetFG(px, py, 8);
    }
}

void WalkLine(Field &field, int x0, int y0, int x1, int y1)
{
    int dx = x1 - x0, dy = y1 - y0;
    int nx = abs(dx), ny = abs(dy);
    int sign_x = dx > 0 ? 1 : -1, sign_y = dy > 0 ? 1 : -1;

    int px = x0, py = y0;
    for (int ix = 0, iy = 0; ix < nx || iy < ny;) {
        if ((0.5 + ix) / nx < (0.5 + iy) / ny) {
            px += sign_x;
            ix++;
        } else {
            py += sign_y;
            iy++;
        }
        if (sqrt((x0 - px) * (x0 - px) + (y0 - py) * (y0 - py)) > 5 ||
            field[py][px] == 1)
            break;
        field.SetFG(px, py, 8);
    }
}

void BresenhamLine(Field &field, int x0, int y0, int x1, int y1)
{
    int dx    = abs(x1 - x0);
    int sx    = x0 < x1 ? 1 : -1;
    int dy    = -abs(y1 - y0);
    int sy    = y0 < y1 ? 1 : -1;
    int error = dx + dy;
    int x = x0, y = y0;

    field.SetFG(x0, y0, 8);

    while (true) {
        if (x0 == x1 && y0 == y1)
            break;

        int e2 = 2 * error;

        if (e2 >= dy) {
            if (x == x1)
                break;
            error += dy;
            x += sx;
        }
        if (e2 <= dx) {
            if (y == y1)
                break;
            error += dx;
            y += sy;
        }

        if ((field[y - sy][x] == 1 && field[y][x - sx] == 1) ||
            sqrt((x0 - x) * (x0 - x) + (y0 - y) * (y0 - y)) > 5 ||
            field[y][x] == 1)
            break;
        field.SetFG(x, y, 8);
    }
}

Sorry for the code without comments