summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/losglobal.cc
blob: 7619f01b1305ba8e508fb1dcf92facfbc7fb005c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "AppHdr.h"

#include "losglobal.h"

#include "coord.h"
#include "coordit.h"
#include "libutil.h"
#include "los_def.h"

#define LOS_KNOWN 4

typedef uint8_t losfield_t;
typedef losfield_t halflos_t[LOS_MAX_RANGE+1][2*LOS_MAX_RANGE+1];
static const int o_half_x = 0;
static const int o_half_y = LOS_MAX_RANGE;
typedef halflos_t globallos_t[GXM][GYM];

static globallos_t globallos;

static losfield_t* _lookup_globallos(const coord_def& p, const coord_def& q)
{
    COMPILE_CHECK(LOS_KNOWN * 2 <= sizeof(losfield_t) * 8);

    if (!map_bounds(p) || !map_bounds(q))
        return NULL;
    coord_def diff = q - p;
    if (diff.abs() > LOS_RADIUS_SQ)
        return NULL;
    // p < q iff p.x < q.x || p.x == q.x && p.y < q.y
    if (diff < coord_def(0, 0))
        return &globallos[q.x][q.y][-diff.x + o_half_x][-diff.y + o_half_y];
    else
        return &globallos[p.x][p.y][ diff.x + o_half_x][ diff.y + o_half_y];
}

static void _save_los(los_def* los, los_type l)
{
    const coord_def o = los->get_center();
    int y1 = o.y - LOS_MAX_RANGE;
    int y2 = o.y + LOS_MAX_RANGE;
    int x1 = o.x - LOS_MAX_RANGE;
    int x2 = o.x + LOS_MAX_RANGE;
    for (int y = y1; y <= y2; y++)
        for (int x = x1; x <= x2; x++)
        {
            if (sqr(o.x - x) + sqr(o.y - y) > sqr(LOS_MAX_RANGE) + 1)
                continue;

            coord_def ri(x, y);
            losfield_t* flags = _lookup_globallos(o, ri);
            if (!flags)
                continue;
            *flags |= l << LOS_KNOWN;
            if (los->see_cell(ri))
                *flags |= l;
            else
                *flags &= ~l;
        }
}

// Opacity at p has changed.
void invalidate_los_around(const coord_def& p)
{
    int x1 = max(p.x - LOS_MAX_RANGE, 0);
    int y1 = max(p.y - LOS_MAX_RANGE, 0);
    int x2 = min(p.x, GXM - 1);
    int y2 = min(p.y + LOS_MAX_RANGE, GYM - 1);
    for (int y = y1; y <= y2; y++)
        for (int x = x1; x <= x2; x++)
            if (sqr(p.x - x) + sqr(p.y - y) <= sqr(LOS_MAX_RANGE) + 1)
                memset(globallos[x][y], 0, sizeof(halflos_t));
}

void invalidate_los()
{
    for (rectangle_iterator ri(0); ri; ++ri)
        memset(globallos[ri->x][ri->y], 0, sizeof(halflos_t));
}

static void _update_globallos_at(const coord_def& p, los_type l)
{
    switch (l)
    {
    case LOS_DEFAULT:
        {
            los_def los(p, opc_default);
            los.update();
            _save_los(&los, l);
            break;
        }
    case LOS_NO_TRANS:
        {
            los_def los(p, opc_no_trans);
            los.update();
            _save_los(&los, l);
            break;
        }
    case LOS_SOLID:
        {
            los_def los(p, opc_solid);
            los.update();
            _save_los(&los, l);
            break;
        }
    case LOS_SOLID_SEE:
        {
            los_def los(p, opc_solid_see);
            los.update();
            _save_los(&los, l);
            break;
        }
    default:
        die("invalid opacity");
    }
}

bool cell_see_cell(const coord_def& p, const coord_def& q, los_type l)
{
    if (l == LOS_NONE)
        return true;

    losfield_t* flags = _lookup_globallos(p, q);

    if (!flags)
        return false; // outside range

    if (!(*flags & (l << LOS_KNOWN)))
        _update_globallos_at(p, l);

    ASSERT(*flags & (l << LOS_KNOWN));

    return *flags & l;
}