DEADSOFTWARE

menu: rewrite menu code (software render are broken)
[flatwaifu.git] / src / gl / render.c
1 #include "glob.h"
2 #include "render.h"
3 #include "system.h"
4 #include "files.h"
5 #include "memory.h"
6 #include "misc.h"
7 #include "error.h"
9 #include "menu.h"
10 #include "game.h"
11 #include "dots.h"
12 #include "items.h"
14 #include "sound.h" // snd_vol
15 #include "music.h" // mus_vol
17 #include "fx.h"
18 #include "player.h"
19 #include "monster.h"
20 #include "weapons.h"
21 #include "smoke.h"
22 #include "view.h"
23 #include "switch.h" // sw_secrets
25 #ifdef __APPLE__
26 # include <OpenGL/gl.h>
27 #else
28 # include <GL/gl.h>
29 #endif
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <assert.h>
36 #define VGA_TRANSPARENT_COLOR 0
37 #define DEFAULT_SKY_COLOR 0x97
38 #define MANCOLOR 0xD0
39 #define PLAYER_COLOR_OFFSET 7
40 #define MAXAIR 1091
41 #define ANIT 5
42 #define PL_FLASH 90
44 #pragma pack(1)
45 typedef struct vgaimg {
46 word w, h;
47 short x, y;
48 byte data[];
49 } vgaimg;
51 typedef struct rgb {
52 byte r, g, b;
53 } rgb;
55 typedef struct rgba {
56 byte r, g, b, a;
57 } rgba;
58 #pragma pack()
60 typedef struct node {
61 struct cache *base;
62 struct node *left, *right;
63 struct node *up;
64 int l, t, r, b;
65 int leaf;
66 } node;
68 typedef struct cache {
69 struct cache *next;
70 struct node root;
71 GLuint id;
72 } cache;
74 typedef struct image {
75 node *n;
76 GLint x, y;
77 GLuint w, h;
78 int res;
79 } image;
81 /* Render Specific */
82 int SCRW = 320; // public
83 int SCRH = 200; // public
84 static int gamma;
85 static int fullscreen;
86 static rgb playpal[256];
87 static byte bright[256];
88 static GLuint lastTexture;
89 static cache *root;
91 /* Game */
92 static image scrnh[3]; // TITLEPIC INTERPIC ENDPIC
93 static image ltn[2][2];
95 /* Smoke */
96 static image smk_spr[SMSN];
97 static image smk_fspr[FLSN];
99 /* Effects */
100 static image fx_spr[15];
101 static char fx_sprd[15];
103 /* Weapons */
104 static image wp_spr[49*2];
105 static char wp_sprd[49*2];
107 /* Items */
108 static image item_spr[58];
109 static char item_sprd[58];
111 /* Player */
112 static image plr_spr[27*2];
113 static image plr_msk[27*2];
114 static char plr_sprd[27*2];
115 static image plr_wpn[11][6];
117 /* Monsters */
118 static image pl_spr[2];
119 static image pl_msk[2];
120 static image mn_spr[MN_TN][29*2];
121 static image mn_man_msk[29*2];
122 static char mn_sprd[MN_TN][29*2];
123 static image mn_fspr[8];
124 static image mn_sgun[2];
126 /* Misc */
127 static image sth[22];
128 static image bfh[160 - '!'];
129 static image sfh[160 - '!'];
130 static image stone;
131 static image stone2;
132 static image keys[3];
133 static int prx = 0;
134 static int pry = 0;
136 /* Menu */
137 static int gm_tm;
138 static image msklh[2];
139 static image mbarl;
140 static image mbarm;
141 static image mbarr;
142 static image mbaro;
143 static image mslotl;
144 static image mslotm;
145 static image mslotr;
147 /* Map */
148 static const char *anm[ANIT - 1][5] = {
149 {"WALL22_1", "WALL23_1", "WALL23_2", NULL, NULL},
150 {"WALL58_1", "WALL58_2", "WALL58_3", NULL, NULL},
151 {"W73A_1", "W73A_2", NULL, NULL, NULL},
152 {"RP2_1", "RP2_2", "RP2_3", "RP2_4", NULL}
153 };
154 static int max_wall_width;
155 static int max_wall_height;
156 static int max_textures;
157 static image walp[256];
158 static byte walani[256];
159 static image anip[ANIT][5];
160 static byte anic[ANIT];
161 static image horiz;
163 /* Texture cache */
165 // https://blackpawn.com/texts/lightmaps/
166 static node *R_node_alloc (node *p, int w, int h) {
167 assert(p);
168 assert(w > 0);
169 assert(h > 0);
170 if (p->left) {
171 assert(p->right);
172 node *n = R_node_alloc(p->left, w, h);
173 return n ? n : R_node_alloc(p->right, w, h);
174 } else {
175 int pw = p->r - p->l + 1;
176 int ph = p->b - p->t + 1;
177 if (p->leaf || pw < w || ph < h) {
178 return NULL;
179 } else if (pw == w && ph == h) {
180 p->leaf = 1;
181 return p;
182 } else {
183 p->left = malloc(sizeof(node));
184 p->right = malloc(sizeof(node));
185 if (pw - w > ph - h) {
186 *p->left = (node) {
187 .up = p,
188 .l = p->l,
189 .t = p->t,
190 .r = p->l + w - 1,
191 .b = p->b
192 };
193 *p->right = (node) {
194 .up = p,
195 .l = p->l + w,
196 .t = p->t,
197 .r = p->r,
198 .b = p->b
199 };
200 } else {
201 *p->left = (node) {
202 .up = p,
203 .l = p->l,
204 .t = p->t,
205 .r = p->r,
206 .b = p->t + h - 1
207 };
208 *p->right = (node) {
209 .up = p,
210 .l = p->l,
211 .t = p->t + h,
212 .r = p->r,
213 .b = p->b
214 };
216 return R_node_alloc(p->left, w, h);
221 static int R_node_have_leaf (node *n) {
222 return n && (n->leaf || R_node_have_leaf(n->left) || R_node_have_leaf(n->right));
225 static void R_node_free_recursive (node *n) {
226 if (n) {
227 R_node_free_recursive(n->left);
228 R_node_free_recursive(n->right);
229 free(n);
233 static void R_node_free (node *n) {
234 if (n) {
235 //logo("free node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
236 assert(n->leaf);
237 assert(n->left == NULL);
238 assert(n->right == NULL);
239 n->leaf = 0;
240 n->base = NULL;
241 node *p = n->up;
242 while (p != NULL) {
243 assert(p->leaf == 0);
244 assert(p->left);
245 assert(p->right);
246 if (R_node_have_leaf(p) == 0) {
247 R_node_free_recursive(p->left);
248 p->left = NULL;
249 R_node_free_recursive(p->right);
250 p->right = NULL;
251 p = p->up;
252 } else {
253 p = NULL;
259 static void R_cache_get_max_texture_size (int *w, int *h) {
260 GLint size = 0;
261 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
262 size = min(max(size, 0), 512); // more can be buggy on older hardware
263 *w = size;
264 *h = size;
267 static void R_gl_bind_texture (GLuint id) {
268 if (id != lastTexture) {
269 glBindTexture(GL_TEXTURE_2D, id);
273 static cache *R_cache_new (void) {
274 int w, h;
275 GLuint id;
276 cache *c = NULL;
277 R_cache_get_max_texture_size(&w, &h);
278 if (w && h) {
279 glGenTextures(1, &id);
280 if (id) {
281 R_gl_bind_texture(id);
282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
283 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
284 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
285 c = malloc(sizeof(cache));
286 if (c != NULL) {
287 *c = (cache) {
288 .id = id,
289 .root.r = w - 1,
290 .root.b = h - 1
291 };
292 } else {
293 glDeleteTextures(1, &id);
297 //logo("new cache %p\n", c);
298 return c;
301 static void R_cache_free (cache *root, int freetexture) {
302 cache *next;
303 cache *c = root;
304 while (c != NULL) {
305 next = c->next;
306 R_node_free_recursive(c->root.left);
307 R_node_free_recursive(c->root.right);
308 if (freetexture && c->id != 0) {
309 glDeleteTextures(1, &c->id);
311 free(c);
312 c = next;
316 static node *R_cache_alloc (cache *root, int w, int h) {
317 assert(root);
318 assert(w > 0 && h > 0);
319 node *n = NULL;
320 cache *p = NULL;
321 cache *c = root;
322 int maxw, maxh;
323 R_cache_get_max_texture_size(&maxw, &maxh);
324 if (w <= maxw && h <= maxh) {
325 while (c && !n) {
326 n = R_node_alloc(&c->root, w, h);
327 if (n) {
328 assert(n->leaf);
329 n->base = c;
331 p = c;
332 c = c->next;
334 if (!n) {
335 c = R_cache_new();
336 if (c) {
337 p->next = c;
338 n = R_node_alloc(&c->root, w, h);
339 if (n) {
340 assert(n->leaf);
341 n->base = c;
346 if (n) {
347 //logo("new node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
348 } else {
349 logo("new node failed {%i:%i}\n", w, h);
351 return n;
354 static void R_cache_update (node *n, const void *data, int x, int y, int w, int h) {
355 assert(n);
356 assert(n->leaf);
357 assert(n->base);
358 assert(data);
359 assert(x >= 0);
360 assert(y >= 0);
361 assert(n->l + x + w - 1 <= n->r);
362 assert(n->t + y + h - 1 <= n->b);
363 R_gl_bind_texture(n->base->id);
364 glTexSubImage2D(GL_TEXTURE_2D, 0, n->l + x, n->t + y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
367 /* Generic helpers */
369 static void R_init_playpal (void) {
370 int i;
371 byte *vgapal = M_lock(F_getresid("PLAYPAL"));
372 for (i = 0; i < 256; i++) {
373 playpal[i] = (rgb) {
374 .r = vgapal[i * 3 + 0] * 255 / 63,
375 .g = vgapal[i * 3 + 1] * 255 / 63,
376 .b = vgapal[i * 3 + 2] * 255 / 63,
377 };
378 bright[i] = ((int)vgapal[i * 3 + 0] + vgapal[i * 3 + 1] + vgapal[i * 3 + 2]) * 8 / (63 * 3);
380 M_unlock(vgapal);
383 static vgaimg *R_getvga (int id) {
384 int loaded = M_was_locked(id);
385 vgaimg *v = M_lock(id);
386 if (v != NULL && !loaded) {
387 v->w = short2host(v->w);
388 v->h = short2host(v->h);
389 v->x = short2host(v->x);
390 v->y = short2host(v->y);
392 return v;
395 static rgba *R_extract_flame_spr (vgaimg *v) {
396 static const byte flametab[16] = {
397 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0xD5,
398 0xD6, 0xD7, 0xA1, 0xA0, 0xE3, 0xE2, 0xE1, 0xE0
399 };
400 int i, j;
401 rgba *s = malloc(v->w * v->h * sizeof(rgba));
402 if (s != NULL) {
403 for (j = 0; j < v->h; j++) {
404 for (i = 0; i < v->w; i++) {
405 int k = j * v->w + i;
406 byte c = v->data[k] + bright[DEFAULT_SKY_COLOR];
407 s[k] = (rgba) {
408 .r = playpal[flametab[c]].r,
409 .g = playpal[flametab[c]].g,
410 .b = playpal[flametab[c]].b,
411 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
412 };
416 return s;
419 static rgba *R_extract_smoke_spr (vgaimg *v) {
420 int i, j;
421 rgba *s = malloc(v->w * v->h * sizeof(rgba));
422 if (s != NULL) {
423 for (j = 0; j < v->h; j++) {
424 for (i = 0; i < v->w; i++) {
425 int k = j * v->w + i;
426 byte c = ((v->data[k] + bright[DEFAULT_SKY_COLOR]) + 0x60) ^ 0x0F;
427 byte a = 0xFF - ((int)playpal[c].r + playpal[c].g + playpal[c].b) / 3;
428 s[k] = (rgba) {
429 .r = playpal[c].r,
430 .g = playpal[c].g,
431 .b = playpal[c].b,
432 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : a,
433 };
437 return s;
440 static rgba *R_extract_mask_spr (vgaimg *v) {
441 int i, j;
442 rgba *s = malloc(v->w * v->h * sizeof(rgba));
443 if (s != NULL) {
444 for (j = 0; j < v->h; j++) {
445 for (i = 0; i < v->w; i++) {
446 int k = j * v->w + i;
447 byte c = v->data[k];
448 if (c >= 0x70 && c <= 0x7F) {
449 byte mask = c - 0x70;
450 mask = 0xFF - ((mask << 4) | mask);
451 s[k] = (rgba) {
452 .r = mask,
453 .g = mask,
454 .b = mask,
455 .a = 0xFF,
456 };
457 } else {
458 s[k] = (rgba) {
459 .r = 0,
460 .g = 0,
461 .b = 0,
462 .a = 0,
463 };
468 return s;
471 static rgba *R_extract_rgba_spr (vgaimg *v) {
472 int i, j;
473 rgba *s = malloc(v->w * v->h * sizeof(rgba));
474 if (s != NULL) {
475 for (j = 0; j < v->h; j++) {
476 for (i = 0; i < v->w; i++) {
477 int k = j * v->w + i;
478 byte c = v->data[k];
479 s[k] = (rgba) {
480 .r = playpal[c].r,
481 .g = playpal[c].g,
482 .b = playpal[c].b,
483 .a = c == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
484 };
488 return s;
491 /* OpenGL helpers */
493 static image R_gl_create_image (const rgba *buf, int w, int h) {
494 node *n = R_cache_alloc(root, w, h);
495 if (n) {
496 R_cache_update(n, buf, 0, 0, w, h);
498 return (image) {
499 .n = n,
500 .w = w,
501 .h = h,
502 .res = -1
503 };
506 static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
507 image img;
508 //char name[8];
509 //F_getresname(name, id);
510 //logo("load image: %.8s\n", name);
511 vgaimg *v = R_getvga(id);
512 if (v != NULL) {
513 rgba *buf = (*fn)(v);
514 img = R_gl_create_image(buf, v->w, v->h);
515 img.x = v->x;
516 img.y = v->y;
517 img.res = id;
518 M_unlock(v);
519 free(buf);
520 } else {
521 img = (image) {
522 .res = id
523 };
525 return img;
528 static image R_gl_getimage (int id) {
529 return R_gl_get_special_image(id, &R_extract_rgba_spr);
532 static image R_gl_loadimage (const char name[8]) {
533 return R_gl_getimage(F_getresid(name));
536 static image R_gl_get_special_spr (const char n[4], int s, int d, rgba *(*fn)(vgaimg*)) {
537 return R_gl_get_special_image(F_getsprid(n, s, d), fn);
540 static void R_gl_free_image (image *img) {
541 if (img->n != NULL && img->res >= 0) {
542 R_node_free(img->n);
544 img->n = NULL;
545 img->res = -1;
548 static void R_gl_draw_quad (int x, int y, int w, int h) {
549 glBegin(GL_QUADS);
550 glVertex2i(x + w, y);
551 glVertex2i(x, y);
552 glVertex2i(x, y + h);
553 glVertex2i(x + w, y + h);
554 glEnd();
557 static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip) {
558 if (img->n) {
559 GLfloat nw = img->n->base->root.r + 1;
560 GLfloat nh = img->n->base->root.b + 1;
561 GLfloat ax = (flip ? img->n->l : img->n->r + 1) / nw;
562 GLfloat bx = (flip ? img->n->r + 1 : img->n->l) / nh;
563 GLfloat ay = (img->n->t) / nw;
564 GLfloat by = (img->n->b + 1) / nh;
565 R_gl_bind_texture(img->n->base->id);
566 glEnable(GL_TEXTURE_2D);
567 glBegin(GL_QUADS);
568 glTexCoord2f(ax, ay); glVertex2i(x + w, y);
569 glTexCoord2f(bx, ay); glVertex2i(x, y);
570 glTexCoord2f(bx, by); glVertex2i(x, y + h);
571 glTexCoord2f(ax, by); glVertex2i(x + w, y + h);
572 glEnd();
573 } else {
574 glColor3ub(255, 0, 0);
575 glDisable(GL_BLEND);
576 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
577 glDisable(GL_TEXTURE_2D);
578 R_gl_draw_quad(x, y, w, h);
582 /* fit image into rectangle without applying offset and transparency */
583 static void R_gl_draw_image_ext (image *img, int x, int y, int w, int h) {
584 glDisable(GL_BLEND);
585 glColor3ub(255, 255, 255);
586 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
587 R_gl_draw_textured(img, x, y, w, h, 0);
590 /* draw sprite with offset and coloring */
591 static void R_gl_draw_image_color (image *img, int x, int y, int flip) {
592 int xx = flip ? x - img->w + img->x : x - img->x;
593 glEnable(GL_BLEND);
594 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
595 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
598 /* draw sprite with offset */
599 static void R_gl_draw_image (image *img, int x, int y, int flip) {
600 glColor3ub(255, 255, 255);
601 R_gl_draw_image_color(img, x, y, flip);
604 static void R_gl_set_color (byte c) {
605 glColor3ub(playpal[c].r, playpal[c].g, playpal[c].b);
608 static void R_gl_setclip (int x, int y, int w, int h) {
609 glScissor(x, SCRH - h - y, w, h);
612 static void R_gl_setmatrix (void) {
613 glScissor(0, 0, SCRW, SCRH);
614 glViewport(0, 0, SCRW, SCRH);
615 glMatrixMode(GL_PROJECTION);
616 glLoadIdentity();
617 glOrtho(0, SCRW, SCRH, 0, 0, 1);
618 glMatrixMode(GL_MODELVIEW);
619 glLoadIdentity();
622 /* --- Misc --- */
624 static image Z_getspr (const char n[4], int s, int d, char *dir) {
625 int h = F_getsprid(n, s, d);
626 if (dir != NULL) {
627 *dir = (h & 0x8000) ? 1 : 0;
629 return R_gl_getimage(h);
632 static void Z_putch_generic (image img[], int off, int ch) {
633 image *p = NULL;
634 if (ch > 32 && ch < 160) {
635 p = &img[ch - '!'];
636 if (p->n == NULL) {
637 p = &img[toupper(ch) - '!'];
640 if (p != NULL && p->n != NULL) {
641 R_gl_draw_image(p, prx, pry, 0);
642 prx += p->w - 1;
643 } else {
644 prx += off;
648 static void Z_printf_generic (image img[], int off, const char *fmt, va_list ap) {
649 int i;
650 char buf[80];
651 vsprintf(buf, fmt, ap);
652 for (i = 0; buf[i]; ++i) {
653 switch (buf[i]) {
654 case '\n':
655 pry += off + 1;
656 case '\r':
657 prx = 0;
658 break;
659 default:
660 Z_putch_generic(img, off, (byte)buf[i]);
665 static void Z_gotoxy (int x, int y) {
666 prx = x;
667 pry = y;
670 static void Z_printbf (const char *fmt, ...) {
671 va_list a;
672 va_start(a, fmt);
673 Z_printf_generic(bfh, 12, fmt, a);
674 va_end(a);
677 static void Z_printsf (const char *fmt, ...) {
678 va_list a;
679 va_start(a, fmt);
680 Z_printf_generic(sfh, 7, fmt, a);
681 va_end(a);
684 static void Z_printhf (const char *fmt, ...) {
685 int i, c;
686 char buf[80];
687 va_list a;
688 va_start(a, fmt);
689 vsprintf(buf, fmt, a);
690 va_end(a);
691 for (i = 0; buf[i]; ++i) {
692 switch (buf[i]) {
693 case '0':
694 case '1':
695 case '2':
696 case '3':
697 case '4':
698 case '5':
699 case '6':
700 case '7':
701 case '8':
702 case '9':
703 c = buf[i] - '0';
704 break;
705 case '-':
706 c = 10;
707 break;
708 case '%':
709 c = 11;
710 break;
711 case '\n':
712 pry += 19;
713 case '\r':
714 c = -1;
715 prx = 0;
716 break;
717 default:
718 c = -1;
719 break;
721 if (c >= 0) {
722 R_gl_draw_image(&sth[c], prx, pry, 0);
724 prx += 14;
728 /* --- Menu --- */
730 static image *PL_getspr (int s, int d, int msk) {
731 int i = (s - 'A') * 2 + d;
732 return msk ? &plr_msk[i] : &plr_spr[i];
735 static int count_menu_entries (const new_menu_t *m) {
736 assert(m != NULL);
737 int i = 0;
738 while (m->entries[i].type != 0) {
739 i += 1;
741 return i;
744 static int count_menu_xoff (const new_menu_t *m, int sz) {
745 assert(m != NULL);
746 assert(sz >= 0);
747 int i, len;
748 // TODO change sz with real pixel width
749 int x = strlen(m->title) * (sz + 1);
750 int n = count_menu_entries(m);
751 for (i = 0; i < n; i++) {
752 switch (m->entries[i].type) {
753 case GM_TEXTFIELD:
754 case GM_SCROLLER:
755 len = 24 * 8; // TODO get real maxlen
756 break;
757 default: len = strlen(m->entries[i].caption) * (sz + 1); break;
759 x = max(x, len);
761 return x;
764 static int GM_draw (void) {
765 int i, j, xoff, yoff, n, x, y, cx, cy, cur, curoff, curmod;
766 const new_menu_t *m = GM_get();
767 new_msg_t msg;
768 if (m != NULL) {
769 cx = SCRW / 2;
770 cy = SCRH / 2;
771 n = count_menu_entries(m);
772 y = cy - (n * 16 - 20) / 2;
773 cur = GM_geti();
774 // --- title ---
775 switch (m->type) {
776 case GM_BIG:
777 yoff = 20;
778 x = cx - count_menu_xoff(m, 12) / 2;
779 Z_gotoxy(x, y - 10);
780 Z_printbf("%s", m->title);
781 break;
782 case GM_SMALL:
783 yoff = 8;
784 x = cx - count_menu_xoff(m, 7) / 2;
785 Z_gotoxy(x, y - 10);
786 Z_printsf("%s", m->title);
787 break;
788 default:
789 assert(0);
791 // --- entries ---
792 curoff = yoff;
793 curmod = 0;
794 for (i = 0; i < n; i++) {
795 if (i == cur) {
796 curoff = yoff;
797 curmod = m->entries[i].type == GM_SMALL_BUTTON ? 1 : 0;
799 switch (m->entries[i].type) {
800 case GM_BUTTON:
801 Z_gotoxy(x, y + yoff);
802 Z_printbf("%s", m->entries[i].caption);
803 yoff += 16;
804 break;
805 case GM_SMALL_BUTTON:
806 Z_gotoxy(x, y + yoff);
807 Z_printsf("%s", m->entries[i].caption);
808 yoff += 12;
809 break;
810 case GM_TEXTFIELD:
811 case GM_TEXTFIELD_BUTTON:
812 Z_gotoxy(x, y + yoff);
813 Z_printbf("%s", m->entries[i].caption);
814 xoff = strlen(m->entries[i].caption) * 19;
815 yoff += 9;
816 R_gl_draw_image(&mslotl, x + xoff, y + yoff, 0);
817 for (j = 1; j < 23; j++) {
818 R_gl_draw_image(&mslotm, x + xoff + j * 8, y + yoff, 0);
820 R_gl_draw_image(&mslotr, x + xoff + j * 8, y + yoff, 0);
821 Z_gotoxy(x + xoff + 4, y + yoff - 7);
822 if (input && i == cur) {
823 Z_printsf("%.*s", GM_MAX_INPUT, ibuf);
824 Z_gotoxy(x + xoff + 4 + 8 * icur, y + yoff - 7);
825 Z_printsf("_");
826 } else {
827 msg.type = GM_GETSTR;
828 if (GM_send(m, i, &msg)) {
829 Z_printsf("%.*s", msg.string.maxlen, msg.string.s);
832 yoff += 7;
833 break;
834 case GM_SCROLLER:
835 Z_gotoxy(x, y + yoff);
836 Z_printbf("%s", m->entries[i].caption);
837 xoff = strlen(m->entries[i].caption) * 19;
838 R_gl_draw_image(&mbarl, x + xoff, y + yoff, 0);
839 for (j = 1; j < 9; j++) {
840 R_gl_draw_image(&mbarm, x + xoff + j * 8, y + yoff, 0);
842 R_gl_draw_image(&mbarr, x + xoff + j * 8, y + yoff, 0);
843 msg.type = GM_GETINT;
844 if (GM_send(m, i, &msg)) {
845 int lev = (msg.integer.i - msg.integer.a) * (7 * 8) / msg.integer.b;
846 R_gl_draw_image(&mbaro, x + xoff + lev + 8, y + yoff, 0);
848 yoff += 16;
849 break;
850 default:
851 assert(0);
854 // --- cursor ---
855 if (curmod) {
856 Z_gotoxy(x - 8, y + curoff);
857 Z_printsf(">");
858 } else {
859 R_gl_draw_image(&msklh[(gm_tm / 6) & 1], x - 25, y + curoff - 8, 0);
862 return m != NULL;
865 /*
866 static void GM_draw (void) {
867 enum {MENU, MSG}; // copypasted from menu.c!
868 enum {
869 CANCEL, NEWGAME, LOADGAME, SAVEGAME, OPTIONS, QUITGAME, QUIT, ENDGAME, ENDGM,
870 PLR1, PLR2, COOP, DM, VOLUME, GAMMA, LOAD, SAVE, PLCOLOR, PLCEND, MUSIC, INTERP,
871 SVOLM, SVOLP, MVOLM, MVOLP, GAMMAM, GAMMAP, PL1CM, PL1CP, PL2CM, PL2CP
872 }; // copypasted from menu.c!
873 int i, j, k, x, y, cx, cy;
874 image *img;
875 gm_tm += 1;
876 if (mnu != NULL) {
877 cx = SCRW / 2;
878 cy = SCRH / 2;
879 if (mnu->type == MENU) {
880 y = cy - (mnu->n * 16 - 20) / 2;
881 Z_gotoxy(cx - mnu->x, y - 10); Z_printbf("%s", mnu->ttl);
882 for (i = 0; i < mnu->n; i++) {
883 if (mnu->t[i] == LOAD || mnu->t[i] == SAVE) {
884 j = y + i * 16 + 29;
885 R_gl_draw_image(&mslotl, cx - mnu->x, j, 0);
886 for (k = 8; k < 184; k += 8) {
887 R_gl_draw_image(&mslotm, cx - mnu->x + k, j, 0);
889 R_gl_draw_image(&mslotr, cx - mnu->x + 184, j, 0);
890 Z_gotoxy(cx - mnu->x + 4, j - 8);
891 if (input && i == save_mnu.cur) {
892 Z_printsf("%s_", ibuf);
893 } else {
894 Z_printsf("%s", savname[i]);
896 } else {
897 x = mnu->t[i] >= SVOLM ? (mnu->t[i] >= PL1CM ? 50 : 152) : 0;
898 Z_gotoxy(cx - mnu->x + x, y + i * 16 + 20);
899 Z_printbf("%s", mnu->m[i]);
900 switch (mnu->t[i]) {
901 case MUSIC:
902 Z_printbf(" '%s'", g_music);
903 break;
904 case INTERP:
905 Z_printbf("%s", fullscreen ? "ON" : "OFF");
906 break;
907 case PL1CM:
908 case PL1CP:
909 case PL2CM:
910 case PL2CP:
911 img = PL_getspr(*panimp, 0, 0);
912 R_gl_draw_image(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
913 img = PL_getspr(*panimp, 0, 1);
914 R_gl_set_color(pcolortab[(mnu->t[i] == PL1CM) ? p1color : p2color] + PLAYER_COLOR_OFFSET);
915 R_gl_draw_image_color(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
916 break;
917 case SVOLM:
918 case SVOLP:
919 case MVOLM:
920 case MVOLP:
921 case GAMMAM:
922 case GAMMAP:
923 j = y + i * 16 + 20;
924 R_gl_draw_image(&mbarl, cx - mnu->x, j, 0);
925 for (k = 8; k < 144; k += 8) {
926 R_gl_draw_image(&mbarm, cx - mnu->x + k, j, 0);
928 R_gl_draw_image(&mbarr, cx - mnu->x + 144, j, 0);
929 switch (mnu->t[i]) {
930 case SVOLM:
931 k = snd_vol;
932 break;
933 case MVOLM:
934 k = mus_vol;
935 break;
936 case GAMMAM:
937 k = gamma << 5;
938 break;
939 default:
940 k = 0;
941 break;
943 R_gl_draw_image(&mbaro, cx - mnu->x + 8 + k, j, 0);
944 break;
948 R_gl_draw_image(&msklh[(gm_tm / 6) & 1], cx - mnu->x - 25, y + mnu->cur * 16 + 20 - 8, 0);
949 } else if (mnu->type == MSG) {
950 Z_gotoxy(cx - strlen(mnu->ttl) * 7 / 2, cy - 10); Z_printsf(mnu->ttl);
951 Z_gotoxy(cx - 24, SCRH / 2); Z_printsf("(Y/N)");
952 } else {
953 ERR_fatal("Unknown menu type %i\n", mnu->type);
957 */
959 /* --- View --- */
961 static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int fg) {
962 int i, j;
963 assert(minx >= 0 && minx <= FLDW);
964 assert(miny >= 0 && miny <= FLDH);
965 assert(maxx >= 0 && maxx <= FLDW);
966 assert(maxy >= 0 && maxy <= FLDH);
967 for (j = miny; j < maxy; j++) {
968 for (i = minx; i < maxx; i++) {
969 byte id = fld[j * FLDW + i];
970 if (id != 0) {
971 if (walp[id].res < 0) {
972 if (fg) {
973 switch (R_get_special_id(id)) {
974 case 1:
975 glColor4ub(0, 0, 255, 127);
976 break;
977 case 2:
978 glColor4ub(0, 127, 0, 127);
979 break;
980 case 3:
981 glColor4ub(127, 0, 0, 127);
982 break;
983 default:
984 glColor4ub(0, 0, 0, 127);
985 break;
987 glEnable(GL_BLEND);
988 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
989 glDisable(GL_TEXTURE_2D);
990 R_gl_draw_quad(i * CELW, j * CELW, CELW, CELH);
992 } else {
993 R_gl_draw_image(&walp[id], i * CELW, j * CELH, 0);
1000 static void R_draw_dots (void) {
1001 int i;
1002 glDisable(GL_BLEND);
1003 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1004 glDisable(GL_TEXTURE_2D);
1005 glBegin(GL_POINTS);
1006 for (i = 0; i < MAXDOT; i++) {
1007 if (dot[i].t != 0) {
1008 R_gl_set_color(dot[i].c);
1009 glVertex2i(dot[i].o.x, dot[i].o.y + 1);
1012 glEnd();
1015 static void R_draw_items (void) {
1016 int i, s;
1017 for (i = 0; i < MAXITEM; ++i) {
1018 s = -1;
1019 if (it[i].t && it[i].s >= 0) {
1020 switch (it[i].t & 0x7FFF) {
1021 case I_ARM1:
1022 s = it[i].s / 9 + 18;
1023 break;
1024 case I_ARM2:
1025 s = it[i].s / 9 + 20;
1026 break;
1027 case I_MEGA:
1028 s = it[i].s / 2 + 22;
1029 break;
1030 case I_INVL:
1031 s = it[i].s / 2 + 26;
1032 break;
1033 case I_SUPER:
1034 case I_RTORCH:
1035 case I_GTORCH:
1036 case I_BTORCH:
1037 s = it[i].s / 2 + (it[i].t - I_SUPER) * 4 + 35;
1038 break;
1039 case I_GOR1: case I_FCAN:
1040 s = it[i].s / 2 + (it[i].t - I_GOR1) * 3 + 51;
1041 break;
1042 case I_AQUA:
1043 s = 30;
1044 break;
1045 case I_SUIT:
1046 s = 34;
1047 break;
1048 case I_KEYR:
1049 case I_KEYG:
1050 case I_KEYB:
1051 s = (it[i].t & 0x7FFF) - I_KEYR + 31;
1052 break;
1053 case I_GUN2:
1054 s = 57;
1055 break;
1056 default:
1057 s = (it[i].t & 0x7FFF) - 1;
1060 if (s >= 0) {
1061 R_gl_draw_image(&item_spr[s], it[i].o.x, it[i].o.y, item_sprd[s]);
1066 static int standspr (player_t *p) {
1067 if (p->f & PLF_UP) {
1068 return 'X';
1069 } else if (p->f & PLF_DOWN) {
1070 return 'Z';
1071 } else {
1072 return 'E';
1076 static int wpnspr (player_t *p) {
1077 if (p->f & PLF_UP) {
1078 return 'C';
1079 } else if(p->f & PLF_DOWN) {
1080 return 'E';
1081 } else {
1082 return 'A';
1086 static void R_draw_player (player_t *p) {
1087 enum {STAND, GO, DIE, SLOP, DEAD, MESS, OUT, FALL}; // copypasted from player.c!
1088 static const int wytab[] = {-1, -2, -1, 0};
1089 int s = 'A';
1090 int w = 0;
1091 int wx = 0;
1092 int wy = 0;
1093 switch (p->st) {
1094 case STAND:
1095 if (p->f & PLF_FIRE) {
1096 s = standspr(p) + 1;
1097 w = wpnspr(p) + 1;
1098 } else if (p->pain) {
1099 s = 'G';
1100 w = 'A';
1101 wx = p->d ? 2 : -2;
1102 wy = 1;
1103 } else {
1104 s = standspr(p);
1105 w = wpnspr(p);
1107 break;
1108 case DEAD:
1109 s = 'N';
1110 break;
1111 case MESS:
1112 s = 'W';
1113 break;
1114 case GO:
1115 if (p->pain) {
1116 s = 'G';
1117 w = 'A';
1118 wx = p->d ? 2 : -2;
1119 wy = 1;
1120 } else {
1121 s = plr_goanim[p->s / 8];
1122 w = (p->f & PLF_FIRE) ? 'B' : 'A';
1123 wx = p->d ? 2 : -2;
1124 wy = 1 + wytab[s - 'A'];
1126 break;
1127 case DIE:
1128 s = plr_dieanim[p->s];
1129 break;
1130 case SLOP:
1131 s = plr_slopanim[p->s];
1132 break;
1133 case OUT:
1134 s = 0;
1135 break;
1137 if (p->wpn == 0) {
1138 w = 0;
1140 if (w) {
1141 R_gl_draw_image(&plr_wpn[(int)p->wpn][w -'A'], p->o.x + wx, p->o.y + wy, p->d);
1143 if (s) {
1144 R_gl_draw_image(&plr_spr[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
1145 R_gl_set_color(p->color + PLAYER_COLOR_OFFSET);
1146 R_gl_draw_image_color(&plr_msk[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
1150 static void R_draw_monsters (void) {
1151 enum {SLEEP, GO, RUN, CLIMB, DIE, DEAD, ATTACK, SHOOT, PAIN, WAIT, REVIVE, RUNOUT}; // copypasted from monster.c!
1152 int i;
1153 for (i = 0; i < MAXMN; i++) {
1154 if (mn[i].t != MN_NONE) {
1155 int x = mn[i].o.x;
1156 int y = mn[i].o.y;
1157 if (mn[i].t < MN__LAST) {
1158 if ((mn[i].t != MN_SOUL && mn[i].t != MN_PAIN) || mn[i].st != DEAD) {
1159 int ap = mn[i].ap[mn[i].ac];
1160 int d = (ap - 'A') * 2 + mn[i].d;
1161 int dir = mn_sprd[mn[i].t - 1][d];
1162 if (mn[i].t == MN_MAN && (ap == 'E' || ap == 'F')) {
1163 R_gl_draw_image(&mn_sgun[ap - 'E'], x, y, mn[i].d);
1165 R_gl_draw_image(&mn_spr[mn[i].t - 1][d], x, y, dir);
1166 if (mn[i].t == MN_MAN) {
1167 R_gl_set_color(MANCOLOR + PLAYER_COLOR_OFFSET);
1168 R_gl_draw_image_color(&mn_man_msk[d], x, y, dir);
1171 if (mn[i].t == MN_VILE && mn[i].st == SHOOT) {
1172 R_gl_draw_image(&mn_fspr[mn[i].ac / 3], mn[i].tx, mn[i].ty, 0);
1174 } else if (mn[i].t == MN_PL_DEAD || mn[i].t == MN_PL_MESS) {
1175 int type = mn[i].t - MN_PL_DEAD;
1176 R_gl_draw_image(&pl_spr[type], x, y, 0);
1177 R_gl_set_color(mn[i].d);
1178 R_gl_draw_image_color(&pl_msk[type], x, y, 0);
1184 static void R_draw_weapons (void) {
1185 enum {NONE, ROCKET, PLASMA, APLASMA, BALL1, BALL2, BALL7, BFGBALL, BFGHIT, MANF, REVF, FIRE}; // copypasted from weapons.c!
1186 int i, s, d, x, y;
1187 for (i = 0; i < MAXWPN; ++i) {
1188 s = -1;
1189 d = 0;
1190 switch (wp[i].t) {
1191 case REVF:
1192 case ROCKET:
1193 d = wp[i].s;
1194 if (d < 2) {
1195 d = wp[i].o.xv > 0 ? 1 : 0;
1196 x = abs(wp[i].o.xv);
1197 y = wp[i].o.yv;
1198 s = 0;
1199 if (y < 0) {
1200 if (-y >= x) {
1201 s = 30;
1203 } else if (y > 0) {
1204 if (y >= x / 2) {
1205 s = 31;
1208 } else {
1209 s = (d - 2) / 2 + 1;
1210 d = 0;
1212 break;
1213 case MANF:
1214 s=wp[i].s;
1215 if (s >= 2) {
1216 s /= 2;
1217 break;
1219 case PLASMA:
1220 case APLASMA:
1221 case BALL1:
1222 case BALL7:
1223 case BALL2:
1224 s = wp[i].s;
1225 if (s >= 2) {
1226 s = s / 2 + 1;
1228 switch (wp[i].t) {
1229 case PLASMA:
1230 s += 4;
1231 break;
1232 case APLASMA:
1233 s += 11;
1234 break;
1235 case BALL1:
1236 s += 32;
1237 break;
1238 case BALL2:
1239 s += 42;
1240 break;
1241 case BALL7:
1242 s += 37;
1243 d = wp[i].o.xv >= 0 ? 1 : 0;
1244 break;
1245 case MANF:
1246 s += 47;
1247 d= wp[i].o.xv>=0 ? 1 : 0;
1248 break;
1250 break;
1251 case BFGBALL:
1252 s = wp[i].s;
1253 if (s >= 2) {
1254 s = s / 2 + 1;
1256 s += 18;
1257 break;
1258 case BFGHIT:
1259 s = wp[i].s / 2 + 26;
1260 break;
1262 if (s >= 0) {
1263 R_gl_draw_image(&wp_spr[s * 2 + d], wp[i].o.x, wp[i].o.y, wp_sprd[s * 2 + d]);
1268 static void R_draw_smoke (void) {
1269 int i, s;
1270 for (i = 0; i < MAXSMOK; ++i) {
1271 if (sm[i].t) {
1272 switch (sm[i].s) {
1273 case 0:
1274 s = sm[i].t;
1275 if (s >= (SMSN - 1) * 3) {
1276 s = 0;
1277 } else {
1278 s = SMSN - 1 - s / 3;
1280 R_gl_draw_image(&smk_spr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1281 break;
1282 case 1:
1283 s = sm[i].t;
1284 if (s >= FLSN - 1) {
1285 s = 0;
1286 } else {
1287 s = FLSN - 1 - s;
1289 R_gl_draw_image(&smk_fspr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1290 break;
1296 static void R_draw_effects (void) {
1297 enum {NONE, TFOG, IFOG, BUBL}; // copypasted from fx.c
1298 int i, s;
1299 for (i = 0; i < MAXFX; ++i) {
1300 switch (fx[i].t) {
1301 case TFOG:
1302 s = fx[i].s / 2;
1303 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1304 break;
1305 case IFOG:
1306 s = fx[i].s / 2 + 10;
1307 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1308 break;
1309 case BUBL:
1310 glDisable(GL_BLEND);
1311 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1312 glDisable(GL_TEXTURE_2D);
1313 glBegin(GL_POINTS);
1314 R_gl_set_color(0xC0 + fx[i].s);
1315 glVertex2i(fx[i].x >> 8, (fx[i].y >> 8) + 1);
1316 glEnd();
1317 break;
1322 static int get_pu_st (int t) {
1323 if (t >= PL_FLASH) {
1324 return 1;
1325 } else if((t / 9) & 1) {
1326 return 0;
1327 } else {
1328 return 1;
1332 static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
1333 glPushMatrix();
1334 R_gl_setclip(x, y, w, h);
1335 glTranslatef(x, y, 0);
1336 if (w_horiz && horiz.n != NULL) {
1337 R_gl_draw_image_ext(&horiz, 0, 0, w, h);
1338 if (sky_type == 2 && lt_time < 0) {
1339 image *tanderbolt = &ltn[lt_type][lt_time < -5 ? 0 : 1];
1340 if (!lt_side) {
1341 R_gl_draw_image(tanderbolt, 0, lt_ypos, 0);
1342 } else {
1343 R_gl_draw_image(tanderbolt, w - 1, lt_ypos, 1);
1346 } else {
1347 glDisable(GL_BLEND);
1348 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1349 glDisable(GL_TEXTURE_2D);
1350 R_gl_set_color(DEFAULT_SKY_COLOR);
1351 R_gl_draw_quad(0, 0, w, h);
1353 int maxx = min((camx + w) / CELW + 1, FLDW);
1354 int maxy = min((camy + h) / CELH + 1, FLDH);
1355 int minx = max((camx - max_wall_width) / CELW, 0);
1356 int miny = max((camy - max_wall_height) / CELH, 0);
1357 glTranslatef(-camx, -camy, 0);
1358 R_draw_fld((byte*)fldb, minx, miny, maxx, maxy, 0);
1359 R_draw_dots();
1360 R_draw_items();
1361 R_draw_player(&pl1);
1362 if (_2pl) {
1363 R_draw_player(&pl2);
1365 R_draw_monsters();
1366 R_draw_weapons();
1367 R_draw_smoke();
1368 R_draw_effects();
1369 R_draw_fld((byte*)fldf, minx, miny, maxx, maxy, 1);
1370 glTranslatef(camx, camy, 0);
1371 if (sky_type == 2 && (lt_time == -4 || lt_time == -2)) {
1372 glColor4ub(255, 255, 255, 255);
1373 glEnable(GL_BLEND);
1374 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1375 glDisable(GL_TEXTURE_2D);
1376 R_gl_draw_quad(0, 0, w, h);
1378 glPopMatrix();
1381 static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
1382 p->looky = min(max(p->looky, -SCRH / 4), SCRH / 4); // TODO remove writeback
1383 int st = stone.w;
1384 int cw = w - st;
1385 int cx = min(max(p->o.x, cw / 2), FLDW * CELW - cw / 2);
1386 int cy = min(max(p->o.y - 12 + p->looky, h / 2), FLDH * CELH - h / 2);
1387 int camx = max(cx - cw / 2, 0);
1388 int camy = max(cy - h / 2, 0);
1389 glPushMatrix();
1390 R_draw_view(x, y + 1, cw, h - 2, camx, camy);
1391 glTranslatef(x, y, 0);
1392 if (p->invl) {
1393 if (get_pu_st(p->invl)) {
1394 glEnable(GL_BLEND);
1395 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
1396 glDisable(GL_TEXTURE_2D);
1397 glColor4ub(191, 191, 191, 255);
1398 R_gl_draw_quad(0, 0, cw, h);
1400 } else {
1401 if (p->suit && get_pu_st(p->suit)) {
1402 glEnable(GL_BLEND);
1403 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1404 glDisable(GL_TEXTURE_2D);
1405 glColor4ub(0, 255, 0, 192);
1406 R_gl_draw_quad(0, 0, cw, h);
1408 int f = min(max(p->pain * 3, 0), 255);
1409 if (f > 0) {
1410 glEnable(GL_BLEND);
1411 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1412 glDisable(GL_TEXTURE_2D);
1413 glColor4ub(255, 0, 0, f);
1414 R_gl_draw_quad(0, 0, cw, h);
1417 R_gl_setclip(x, y, w, h);
1418 glTranslatef(-x + cw, 0, 0);
1419 R_gl_draw_image(&stone, 0, 0, 0);
1420 int i = stone.h;
1421 while (i < h) {
1422 R_gl_draw_image(&stone2, 0, i, 0);
1423 i += stone2.h;
1425 if (p->drawst & PL_DRAWAIR) {
1426 if (p->air < PL_AIR) {
1427 int a = min(max(p->air, 0), MAXAIR) * 100 / MAXAIR;
1428 glDisable(GL_BLEND);
1429 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1430 glDisable(GL_TEXTURE_2D);
1431 R_gl_set_color(0xC8);
1432 R_gl_draw_quad(10, 49, a, 2);
1435 if (p->drawst & PL_DRAWLIFE) {
1436 Z_gotoxy(10, 7);
1437 Z_printhf("%3d%%", p->life);
1439 if (p->drawst & PL_DRAWARMOR) {
1440 Z_gotoxy(10, 7 + 19);
1441 Z_printhf("%3d%%", p->armor);
1443 if (p->drawst & PL_DRAWWPN) {
1444 switch(p->wpn) {
1445 case 2:
1446 case 5:
1447 i = p->ammo;
1448 break;
1449 case 3:
1450 case 4:
1451 case 9:
1452 i = p->shel;
1453 break;
1454 case 6:
1455 i = p->rock;
1456 break;
1457 case 7:
1458 case 8:
1459 i = p->cell;
1460 break;
1461 case 10:
1462 i = p->fuel;
1463 break;
1464 default:
1465 i = -1;
1466 break;
1468 // weapon
1469 if (p->wpn >= 0) {
1470 R_gl_draw_image(&sth[12 + p->wpn], st - 88, 58 + 19, 0);
1472 // ammo
1473 if (p->wpn >= 2) {
1474 Z_gotoxy(st - 10 - 5 * 14, 58 + 2);
1475 Z_printhf("%5d", i);
1478 if (p->drawst & PL_DRAWFRAG && g_dm) {
1479 Z_gotoxy(st - 5 - 5 * 14, 77 + 5);
1480 Z_printhf("%5d", p->frag);
1482 if (p->drawst & PL_DRAWKEYS) {
1483 int x, k, n;
1484 for (k = p->keys >> 4, n = 0, x = st - 75; n < 3; n++, k >>= 1, x += 9) {
1485 if (k & 1) {
1486 R_gl_draw_image(&keys[n], x, 91, 0);
1490 if (p->drawst & PL_DRAWLIVES && !_2pl) {
1491 Z_gotoxy(st - 35, 17);
1492 Z_printhf("%d", p->lives);
1494 glPopMatrix();
1497 /* --- Game --- */
1499 static void pl_info (player_t *p, int x, int y) {
1500 dword t = p->kills * 10920 / g_time;
1501 Z_gotoxy(x + 25, y); Z_printbf("KILLS");
1502 Z_gotoxy(x + 25, y + 15); Z_printbf("KPM");
1503 Z_gotoxy(x + 25, y + 30); Z_printbf("SECRETS %u / %u", p->secrets, sw_secrets);
1504 Z_gotoxy(x + 255, y); Z_printbf("%u", p->kills);
1505 Z_gotoxy(x + 255, y + 15); Z_printbf("%u.%u", t / 10, t % 10);
1508 static void R_draw_intermission (void) {
1509 int cx = SCRW / 2;
1510 word hr, mn, sc, h;
1511 Z_gotoxy(cx - 14*12/2, 20);
1512 Z_printbf("LEVEL COMPLETE");
1513 Z_calc_time(g_time, &hr, &mn, &sc);
1514 Z_gotoxy(cx - 12*12/2, 40);
1515 Z_printbf("TIME %u:%02u:%02u", hr, mn, sc);
1516 h = 40 + SCRH / 10;
1517 if (_2pl) {
1518 Z_gotoxy(cx - 10*12/2, h);
1519 Z_printbf("PLAYER ONE");
1520 h += 20;
1522 pl_info(&pl1, cx - 160, h);
1523 if (_2pl) {
1524 h += 30 + SCRH / 10;
1525 Z_gotoxy(cx - 10*12/2, h);
1526 Z_printbf("PLAYER TWO");
1527 h += 20;
1528 pl_info(&pl2, cx - 160, h);
1532 static void W_act (void) {
1533 int i, a;
1534 if (g_time % 3 == 0) {
1535 for (i = 1; i < max_textures; i++) {
1536 a = walani[i];
1537 if (a != 0) {
1538 anic[a]++;
1539 if (anip[a][anic[a]].res == -1) {
1540 anic[a] = 0;
1542 walp[i] = anip[a][anic[a]];
1548 void R_draw (void) {
1549 W_act();
1550 glClearColor(0, 0, 0, 1);
1551 glClear(GL_COLOR_BUFFER_BIT);
1552 glEnable(GL_SCISSOR_TEST);
1553 R_gl_setmatrix();
1554 switch (g_st) {
1555 case GS_ENDANIM:
1556 case GS_END2ANIM:
1557 case GS_DARKEN:
1558 case GS_BVIDEO:
1559 case GS_EVIDEO:
1560 case GS_END3ANIM:
1561 break;
1562 case GS_TITLE:
1563 R_gl_draw_image_ext(&scrnh[0], 0, 0, SCRW, SCRH);
1564 break;
1565 case GS_INTER:
1566 R_gl_draw_image_ext(&scrnh[1], 0, 0, SCRW, SCRH);
1567 R_draw_intermission();
1568 break;
1569 case GS_ENDSCR:
1570 R_gl_draw_image_ext(&scrnh[2], 0, 0, SCRW, SCRH);
1571 break;
1572 case GS_GAME:
1573 if (_2pl) {
1574 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH / 2);
1575 R_draw_player_view(&pl2, 0, SCRH / 2, SCRW, SCRH / 2);
1576 } else {
1577 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH);
1579 R_gl_setclip(0, 0, SCRW, SCRH);
1580 break;
1582 GM_draw();
1583 Y_swap_buffers();
1586 void R_alloc (void) {
1587 char s[10];
1588 int i, j, n;
1589 logo("R_alloc: load graphics\n");
1590 /* Game */
1591 scrnh[0] = R_gl_loadimage("TITLEPIC");
1592 scrnh[1] = R_gl_loadimage("INTERPIC");
1593 scrnh[2] = R_gl_loadimage("ENDPIC");
1594 for (i = 0; i < 2; i++) {
1595 sprintf(s, "LTN%c", '1' + i);
1596 for (j = 0; j < 2; j++) {
1597 ltn[i][j] = Z_getspr(s, j, 0, NULL);
1600 /* Smoke */
1601 for (i = 0; i < SMSN; i++) {
1602 smk_spr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_smoke_spr);
1604 for (i = 0; i < FLSN; i++) {
1605 smk_fspr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_flame_spr);
1607 /* Effects */
1608 for (i = 0; i < 10; i++) {
1609 fx_spr[i] = Z_getspr("TFOG", i, 0, fx_sprd + i);
1611 for (; i < 15; i++) {
1612 fx_spr[i] = Z_getspr("IFOG", i - 10, 0, fx_sprd + i);
1614 /* Weapons */
1615 for (i = 0; i < 4; i++) {
1616 wp_spr[i * 2] = Z_getspr("MISL", i, 1, wp_sprd + i * 2);
1617 wp_spr[i * 2 + 1] = Z_getspr("MISL", i, 2, wp_sprd + i * 2 + 1);
1619 for (; i < 6; i++) {
1620 wp_spr[i * 2] = Z_getspr("PLSS", i - 4, 1, wp_sprd + i * 2);
1621 wp_spr[i * 2 + 1] = Z_getspr("PLSS", i - 4, 2, wp_sprd + i * 2 + 1);
1623 for (; i < 11; i++) {
1624 wp_spr[i * 2] = Z_getspr("PLSE", i - 6, 1, wp_sprd + i * 2);
1625 wp_spr[i * 2 + 1] = Z_getspr("PLSE", i - 6, 2, wp_sprd + i * 2 + 1);
1627 for (; i < 13; i++) {
1628 wp_spr[i * 2] = Z_getspr("APLS", i - 11, 1, wp_sprd + i * 2);
1629 wp_spr[i * 2 + 1] = Z_getspr("APLS", i - 11, 2, wp_sprd + i * 2 + 1);
1631 for (; i < 18; i++) {
1632 wp_spr[i * 2] = Z_getspr("APBX", i - 13, 1, wp_sprd + i * 2);
1633 wp_spr[i * 2 + 1] = Z_getspr("APBX", i - 13, 2, wp_sprd + i * 2 + 1);
1635 for(; i < 20; i++) {
1636 wp_spr[i * 2] = Z_getspr("BFS1", i - 18, 1, wp_sprd + i * 2);
1637 wp_spr[i * 2 + 1] = Z_getspr("BFS1", i - 18, 2, wp_sprd + i * 2 + 1);
1639 for (; i < 26; i++) {
1640 wp_spr[i * 2] = Z_getspr("BFE1", i - 20, 1, wp_sprd + i * 2);
1641 wp_spr[i * 2 + 1] = Z_getspr("BFE1", i - 20, 2, wp_sprd + i * 2 + 1);
1643 for (; i < 30; i++) {
1644 wp_spr[i * 2] = Z_getspr("BFE2", i - 26, 1, wp_sprd + i * 2);
1645 wp_spr[i * 2 + 1] = Z_getspr("BFE2", i - 26, 2, wp_sprd + i * 2 + 1);
1647 for (; i < 32; i++) {
1648 wp_spr[i * 2] = Z_getspr("MISL", i - 30 + 4, 1, wp_sprd + i * 2);
1649 wp_spr[i * 2 + 1] = Z_getspr("MISL", i - 30 + 4, 2, wp_sprd + i * 2 + 1);
1651 for (; i < 37; i++) {
1652 wp_spr[i * 2] = Z_getspr("BAL1", i - 32, 1, wp_sprd + i * 2);
1653 wp_spr[i * 2 + 1] = Z_getspr("BAL1", i - 32, 2, wp_sprd + i * 2 + 1);
1655 for (; i < 42; i++) {
1656 wp_spr[i * 2] = Z_getspr("BAL7", i - 37, 1, wp_sprd + i * 2);
1657 wp_spr[i * 2 + 1] = Z_getspr("BAL7", i - 37, 2, wp_sprd + i * 2 + 1);
1659 for (; i < 47; i++) {
1660 wp_spr[i * 2] = Z_getspr("BAL2", i - 42, 1, wp_sprd + i * 2);
1661 wp_spr[i * 2 + 1] = Z_getspr("BAL2", i - 42, 2, wp_sprd + i * 2 + 1);
1663 for (; i < 49; i++) {
1664 wp_spr[i * 2] = Z_getspr("MANF", i - 47, 1, wp_sprd + i * 2);
1665 wp_spr[i * 2 + 1] = Z_getspr("MANF", i - 47, 2, wp_sprd + i * 2 + 1);
1667 /* Items */
1668 static const char snm[18][4] = {
1669 "CLIP", "SHEL", "ROCK", "CELL", "AMMO", "SBOX", "BROK", "CELP",
1670 "STIM", "MEDI", "BPAK",
1671 "CSAW", "SHOT", "SGN2", "MGUN", "LAUN", "PLAS", "BFUG"
1672 };
1673 static const char n4[4][4] = {
1674 "SOUL", "SMRT", "SMGT", "SMBT"
1675 };
1676 static const char n3[2][4] = {
1677 "GOR1", "FCAN"
1678 };
1679 for (i = 0; i < 18; i++) {
1680 item_spr[i] = Z_getspr(snm[i], 0, 0, item_sprd + i);
1682 for (; i < 20; i++) {
1683 item_spr[i] = Z_getspr("ARM1", i - 18, 0, item_sprd + i);
1684 item_spr[i + 2] = Z_getspr("ARM2", i - 18, 0, item_sprd + i);
1686 i+=2;
1687 for (; i < 26; i++) {
1688 item_spr[i] = Z_getspr("MEGA", i - 22, 0, item_sprd + i);
1690 for (; i < 30; i++) {
1691 item_spr[i] = Z_getspr("PINV", i - 26, 0, item_sprd + i);
1693 item_spr[30] = Z_getspr("AQUA", 0, 0, item_sprd + 30);
1694 item_spr[31] = Z_getspr("KEYR", 0, 0, item_sprd + 31);
1695 item_spr[32] = Z_getspr("KEYG", 0, 0, item_sprd + 32);
1696 item_spr[33] = Z_getspr("KEYB", 0, 0, item_sprd + 33);
1697 item_spr[34] = Z_getspr("SUIT", 0, 0, item_sprd + 34);
1698 for (n = 35, j = 0; j < 4; j++) {
1699 for (i = 0; i < 4; i++, n++) {
1700 item_spr[n] = Z_getspr(n4[j], i, 0, item_sprd + n);
1703 for (j = 0; j < 2; j++) {
1704 for (i = 0; i < 3; i++, n++) {
1705 item_spr[n] = Z_getspr(n3[j], i, 0, item_sprd + n);
1708 item_spr[57] = Z_getspr("GUN2", 0, 0, item_sprd + 57);
1709 /* Player */
1710 for (i = 0; i < 27; i++) {
1711 plr_spr[i * 2] = Z_getspr("PLAY", i, 1, plr_sprd + i * 2);
1712 plr_msk[i * 2] = R_gl_get_special_spr("PLAY", i, 1, &R_extract_mask_spr);
1713 plr_spr[i * 2 + 1] = Z_getspr("PLAY", i, 2, plr_sprd + i * 2 + 1);
1714 plr_msk[i * 2 + 1] = R_gl_get_special_spr("PLAY", i, 2, &R_extract_mask_spr);
1716 strncpy(s, "PWPx", 4);
1717 for (i = 1; i < 11; i++) {
1718 s[3] = (i < 10 ? '0' : 'A' - 10) + i;
1719 for (j = 0; j < 6; j++) {
1720 plr_wpn[i][j] = Z_getspr(s, j, 1, NULL);
1723 /* Monsters */
1724 static const char msn[MN_TN][4] = {
1725 "SARG", "TROO", "POSS", "SPOS", "CYBR", "CPOS", "BOSS", "BOS2", "HEAD", "SKUL",
1726 "PAIN", "SPID", "BSPI", "FATT", "SKEL", "VILE", "FISH", "BAR1", "ROBO", "PLAY"
1727 };
1728 static const int mms[MN_TN] = {
1729 14*2, 21*2, 21*2, 21*2, 16*2, 20*2, 15*2, 15*2, 12*2, 11*2,
1730 13*2, 19*2, 16*2, 20*2, 17*2, 29*2, 6*2, 2*2, 17*2, 23*2
1731 };
1732 mn_sgun[0] = Z_getspr("PWP4", 0, 1, NULL);
1733 mn_sgun[1] = Z_getspr("PWP4", 1, 1, NULL);
1734 for (j = 0; j < MN_TN; j++) {
1735 for (i = 0; i < mms[j]; i++) {
1736 mn_spr[j][i] = Z_getspr(msn[j], i / 2, (i & 1) + 1, &mn_sprd[j][i]);
1737 if (j == MN_MAN - 1) {
1738 mn_man_msk[i] = R_gl_get_special_spr(msn[j], i / 2, (i & 1) + 1, &R_extract_mask_spr);
1741 if (j == MN_BARREL - 1) {
1742 for (i = 4; i < 14; i++) {
1743 mn_spr[j][i] = Z_getspr("BEXP", i / 2 - 2, (i & 1) + 1, &mn_sprd[j][i]);
1747 for (i = 0; i < 8; i++) {
1748 mn_fspr[i] = Z_getspr("FIRE", i, 0, NULL);
1750 pl_spr[0] = Z_getspr("PLAY", 'N' - 'A', 0, NULL);
1751 pl_msk[0] = R_gl_get_special_spr("PLAY", 'N' - 'A', 0, &R_extract_mask_spr);
1752 pl_spr[1] = Z_getspr("PLAY", 'W' - 'A', 0, NULL);
1753 pl_msk[1] = R_gl_get_special_spr("PLAY", 'W' - 'A', 0, &R_extract_mask_spr);
1754 /* Misc */
1755 static const char mnm[22][8]={
1756 "STTNUM0", "STTNUM1", "STTNUM2", "STTNUM3", "STTNUM4",
1757 "STTNUM5", "STTNUM6", "STTNUM7", "STTNUM8", "STTNUM9",
1758 "STTMINUS", "STTPRCNT",
1759 "FISTA0", "CSAWA0", "PISTA0", "SHOTA0", "SGN2A0", "MGUNA0", "LAUNA0",
1760 "PLASA0", "BFUGA0", "GUN2A0"
1761 };
1762 stone = R_gl_loadimage("STONE");
1763 stone2 = R_gl_loadimage("STONE2");
1764 keys[0] = R_gl_loadimage("KEYRA0");
1765 keys[1] = R_gl_loadimage("KEYGA0");
1766 keys[2] = R_gl_loadimage("KEYBA0");
1767 for (i = 0; i < 22; i++) {
1768 sth[i] = R_gl_loadimage(mnm[i]);
1770 strcpy(s, "STBF_*");
1771 for (i = '!'; i < 160; i++) {
1772 s[5] = i;
1773 bfh[i - '!'] = R_gl_getimage(F_findres(s));
1775 for (i = '!'; i < 160; i++) {
1776 sprintf(s, "STCFN%03d", i);
1777 sfh[i - '!'] = R_gl_getimage(F_findres(s));
1779 strcpy(s, "WINUM*");
1780 for (i = '0'; i <= '9'; i++) {
1781 s[5] = i;
1782 bfh[i - '!'] = R_gl_loadimage(s);
1784 bfh[':' - '!'] = R_gl_loadimage("WICOLON");
1785 // menu
1786 msklh[0] = R_gl_loadimage("M_SKULL1");
1787 msklh[1] = R_gl_loadimage("M_SKULL2");
1788 mbarl = R_gl_loadimage("M_THERML");
1789 mbarm = R_gl_loadimage("M_THERMM");
1790 mbarr = R_gl_loadimage("M_THERMR");
1791 mbaro = R_gl_loadimage("M_THERMO");
1792 mslotl = R_gl_loadimage("M_LSLEFT");
1793 mslotm = R_gl_loadimage("M_LSCNTR");
1794 mslotr = R_gl_loadimage("M_LSRGHT");
1795 // walls
1796 for (i = 1; i < ANIT; i++) {
1797 for (j = 0; j < 5 && anm[i - 1][j]; j++) {
1798 anip[i][j] = R_gl_loadimage(anm[i - 1][j]);
1800 for(; j < 5; j++) {
1801 anip[i][j] = (image) {
1802 .n = NULL,
1803 .w = 8,
1804 .h = 8,
1805 .res = -1,
1806 };
1811 static void R_reload_textures (void);
1813 void R_set_videomode (int w, int h, int fullscreen) {
1814 assert(w > 0);
1815 assert(h > 0);
1816 int was = Y_videomode_setted();
1817 if (root != NULL) {
1818 R_cache_free(root, 0);
1819 root = NULL;
1821 int res = Y_set_videomode_opengl(w, h, fullscreen);
1822 if (res == 0) {
1823 if (was == 0) {
1824 ERR_failinit("Unable to set video mode\n");
1826 } else {
1827 Y_get_videomode(&SCRW, &SCRH);
1828 root = R_cache_new();
1829 assert(root);
1830 R_alloc();
1831 R_reload_textures();
1835 void R_toggle_fullscreen (void) {
1836 R_cache_free(root, 0);
1837 Y_set_fullscreen(!Y_get_fullscreen());
1838 fullscreen = Y_get_fullscreen();
1839 Y_get_videomode(&SCRW, &SCRH);
1840 root = R_cache_new();
1841 assert(root);
1842 R_alloc();
1843 R_reload_textures();
1846 void R_init (void) {
1847 logo("R_init: intialize opengl render\n");
1848 R_init_playpal();
1849 R_set_videomode(SCRW, SCRH, fullscreen);
1852 void R_done (void) {
1853 R_cache_free(root, 1);
1854 Y_unset_videomode();
1855 root = NULL;
1858 void R_setgamma (int g) {
1859 gamma = g < 0 ? 0 : (g > 4 ? 4 : g);
1862 int R_getgamma (void) {
1863 return gamma;
1866 void R_get_name (int n, char s[8]) {
1867 assert(n >= 0 && n < 256);
1868 if (walp[n].res == -1) {
1869 memset(s, 0, 8);
1870 } else if (walp[n].res == -2) {
1871 memcpy(s, "_WATER_", 8);
1872 s[7] = '0' + (intptr_t)walp[n].n - 1;
1873 } else if (walani[n] > 0) {
1874 memcpy(s, anm[walani[n] - 1][0], 8);
1875 } else {
1876 F_getresname(s, walp[n].res & 0x7FFF);
1880 static short getani (char n[8]) {
1881 short i = 0;
1882 while (i < ANIT - 1 && strncasecmp(n, anm[i][0], 8) != 0) {
1883 i++;
1885 return i < ANIT - 1 ? i + 1 : 0;
1888 int R_get_special_id (int n) {
1889 assert(n >= 0 && n <= 256);
1890 return walp[n].res == -2 ? (intptr_t)walp[n].n : -1;
1893 static void R_reload_textures (void) {
1894 int i;
1895 char s[8];
1896 for (i = 0; i < max_textures; i++) {
1897 R_get_name(i, s);
1898 if (walp[i].res >= 0) {
1899 walp[i] = R_gl_getimage(walp[i].res);
1902 if (horiz.n) {
1903 horiz = R_gl_getimage(horiz.res);
1907 void R_begin_load (void) {
1908 int i;
1909 for (i = 0; i < 256; i++) {
1910 if (walp[i].n != NULL && walp[i].res >= 0 && walani[i] == 0) {
1911 R_gl_free_image(&walp[i]);
1913 memset(&walp[i], 0, sizeof(image));
1914 walp[i].res = -1;
1915 walswp[i] = i;
1916 walani[i] = 0;
1918 memset(anic, 0, sizeof(anic));
1919 max_wall_width = 0;
1920 max_wall_height = 0;
1921 max_textures = 1;
1924 void R_load (char s[8], int f) {
1925 assert(max_textures < 256);
1926 if (!s[0]) {
1927 walp[max_textures] = (image) {
1928 .n = NULL,
1929 .x = 0,
1930 .y = 0,
1931 .w = 0,
1932 .h = 0,
1933 .res = -1,
1934 };
1935 } else if (strncasecmp(s, "_WATER_", 7) == 0) {
1936 walp[max_textures] = (image) {
1937 .n = (void*)((intptr_t)s[7] - '0' + 1),
1938 .x = 0,
1939 .y = 0,
1940 .w = 8,
1941 .h = 8,
1942 .res = -2,
1943 };
1944 } else {
1945 walp[max_textures] = R_gl_loadimage(s);
1946 if (f) {
1947 walp[max_textures].res |= 0x8000;
1949 if (s[0] == 'S' && s[1] == 'W' && s[4] == '_') {
1950 walswp[max_textures] = 0;
1952 walani[max_textures] = getani(s);
1954 max_wall_width = max(max_wall_width, walp[max_textures].w);
1955 max_wall_height = max(max_wall_height, walp[max_textures].h);
1956 max_textures++;
1959 void R_end_load (void) {
1960 int i, j, k, g;
1961 char s[8];
1962 j = max_textures;
1963 for (i = 1; i < 256 && j < 256; i++) {
1964 if (walswp[i] == 0) {
1965 R_get_name(i, s);
1966 s[5] ^= 1;
1967 g = F_getresid(s) | (walp[i].res & 0x8000);
1968 k = 1;
1969 while (k < 256 && walp[k].res != g) {
1970 k += 1;
1972 if (k >= 256) {
1973 k = j;
1974 j += 1;
1975 max_textures += 1;
1976 walp[k] = R_gl_getimage(g);
1977 walf[k] = g & 0x8000 ? 1 : 0;
1979 walswp[i] = k;
1980 walswp[k] = i;
1985 void R_loadsky (int sky) {
1986 char s[6];
1987 strcpy(s, "RSKYx");
1988 s[4] = '0' + sky;
1989 R_gl_free_image(&horiz);
1990 horiz = R_gl_loadimage(s);