visibility determination
[vrlugburz] / src / player.c
1 #include "player.h"
2
3 static const int step[][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
4
5 void init_player(struct player *p)
6 {
7         memset(p, 0, sizeof *p);
8         cgm_qcons(&p->vrot, 0, 0, 0, 1);
9
10         p->height = 1.75;
11         p->hp = p->hp_max = 10;
12         p->mp = p->mp_max = 10;
13 }
14
15 #define TWO_PI          ((float)M_PI * 2.0f)
16 #define HALF_PI         ((float)M_PI / 2.0f)
17
18 void update_player_dir(struct player *p)
19 {
20         int prev_dir = p->dir;
21         float angle;
22
23         /* TODO: take vrot into account */
24         angle = fmod(p->theta, TWO_PI);
25         if(angle < 0) angle += TWO_PI;
26
27         p->theta = angle;       /* renormalize theta */
28         p->dir = (int)(4.0f * angle / TWO_PI + 0.5) & 3;
29
30         if(p->dir != prev_dir) {
31                 p->vis = 0;     /* invalidate visibility list */
32         }
33 }
34
35 void move_player(struct player *p, int right, int fwd)
36 {
37         int fdir, rdir;
38
39         update_player_dir(p);
40
41         fdir = p->dir & 3;
42         rdir = (fdir + 1) & 3;
43         p->cx += step[fdir][0] * fwd + step[rdir][0] * right;
44         p->cy += step[fdir][1] * fwd + step[rdir][1] * right;
45
46         p->vis = 0;     /* invalidate visibility list */
47 }
48
49 void turn_player(struct player *p, int turn)
50 {
51         if(!turn) return;
52
53         p->theta += turn > 0 ? HALF_PI : -HALF_PI;
54
55         update_player_dir(p);
56         p->theta = (float)p->dir * HALF_PI;     /* snap theta */
57
58         p->vis = 0;     /* invalidate visibility list */
59 }
60
61 void upd_player_xform(struct player *p)
62 {
63         cgm_vec3 pos;
64         float celld = p->lvl ? p->lvl->cell_size : DEF_CELL_SIZE;
65
66         cgm_vcons(&pos, p->cx * celld, p->height, -p->cy * celld);
67         cgm_vadd(&pos, &p->cpos);
68
69         cgm_midentity(p->view_xform);
70         cgm_mprerotate_x(p->view_xform, -p->phi);
71         cgm_mprerotate_y(p->view_xform, p->theta);
72         cgm_mrotate_quat(p->view_xform, &p->vrot);
73         cgm_mpretranslate(p->view_xform, -pos.x, -pos.y, -pos.z);
74 }
75
76 static void vis_visit(struct player *p, int cx, int cy, int *cvis)
77 {
78         int i, j, nx, ny, dx, dy;
79         struct level *lvl = p->lvl;
80         struct cell *cell;
81
82         if(cx < 0 || cx >= lvl->width || cy < 0 || cy >= lvl->height) {
83                 return;
84         }
85         cell = lvl->cells + cy * lvl->width + cx;
86
87         /* stop when we encounter a solid cell */
88         if(cell->type == CELL_SOLID) {
89                 return;
90         }
91
92         dx = cx - p->cx;
93         dy = cy - p->cy;
94         /* stop beyond the maximum visibility distance (manhattan) */
95         if(abs(dx) > lvl->visdist || abs(dy) > lvl->visdist) {
96                 return;
97         }
98
99         /* dot product */
100         if(step[p->dir][0] * dx + step[p->dir][1] * dy < 0) {
101                 return; /* cell is behind the player */
102         }
103
104         cvis[cy * lvl->width + cx] = 1;         /* mark as visited before recursing */
105
106         /* visit neighboring nodes before adding current cell */
107         for(i=0; i<3; i++) {
108                 ny = cy - 1 + i;
109                 if(ny < 0) continue;
110                 if(ny >= lvl->height) break;
111
112                 for(j=0; j<3; j++) {
113                         if(i == 1 && j == 1) continue;
114                         nx = cx - 1 + j;
115                         if(nx < 0) continue;
116                         if(nx >= lvl->width) break;
117
118                         if(!cvis[ny * lvl->width + nx]) {
119                                 vis_visit(p, nx, ny, cvis);
120                         }
121                 }
122         }
123
124         /* then add this cell to the visible list */
125         cell->next = p->vis;
126         p->vis = cell;
127 }
128
129 void upd_player_vis(struct player *p)
130 {
131         int *cvis;
132         struct level *lvl = p->lvl;
133
134         cvis = alloca(lvl->width * lvl->height * sizeof *cvis);
135         memset(cvis, 0, lvl->width * lvl->height * sizeof *cvis);
136
137         p->vis = 0;
138         vis_visit(p, p->cx, p->cy, cvis);
139 }