DEADSOFTWARE

b2c625fb4f57ec63162c97a027594aa537905cc0
[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 <assert.h>
35 #define VGA_TRANSPARENT_COLOR 0
36 #define DEFAULT_SKY_COLOR 0x97
37 #define MANCOLOR 0xD0
38 #define PLAYER_COLOR_OFFSET 7
39 #define MAXAIR 1091
40 #define ANIT 5
41 #define PL_FLASH 90
43 #pragma pack(1)
44 typedef struct vgaimg {
45 word w, h;
46 short x, y;
47 byte data[];
48 } vgaimg;
50 typedef struct rgb {
51 byte r, g, b;
52 } rgb;
54 typedef struct rgba {
55 byte r, g, b, a;
56 } rgba;
57 #pragma pack()
59 typedef struct node {
60 struct cache *base;
61 struct node *left, *right;
62 struct node *up;
63 int l, t, r, b;
64 int leaf;
65 } node;
67 typedef struct cache {
68 struct cache *next;
69 struct node root;
70 GLuint id;
71 } cache;
73 typedef struct image {
74 node *n;
75 GLint x, y;
76 GLuint w, h;
77 int res;
78 } image;
80 /* Render Specific */
81 int SCRW = 320; // public
82 int SCRH = 200; // public
83 static int gamma;
84 static int fullscreen;
85 static rgb playpal[256];
86 static byte bright[256];
87 static GLuint lastTexture;
88 static cache *root;
90 /* Game */
91 static image scrnh[3]; // TITLEPIC INTERPIC ENDPIC
92 static image ltn[2][2];
94 /* Smoke */
95 static image smk_spr[SMSN];
96 static image smk_fspr[FLSN];
98 /* Effects */
99 static image fx_spr[15];
100 static char fx_sprd[15];
102 /* Weapons */
103 static image wp_spr[49*2];
104 static char wp_sprd[49*2];
106 /* Items */
107 static image item_spr[58];
108 static char item_sprd[58];
110 /* Player */
111 static image plr_spr[27*2];
112 static image plr_msk[27*2];
113 static char plr_sprd[27*2];
114 static image plr_wpn[11][6];
116 /* Monsters */
117 static image pl_spr[2];
118 static image pl_msk[2];
119 static image mn_spr[MN_TN][29*2];
120 static image mn_man_msk[29*2];
121 static char mn_sprd[MN_TN][29*2];
122 static image mn_fspr[8];
123 static image mn_sgun[2];
125 /* Misc */
126 static image sth[22];
127 static image bfh[160 - '!'];
128 static image sfh[160 - '!'];
129 static image stone;
130 static image stone2;
131 static image keys[3];
132 static int prx = 0;
133 static int pry = 0;
135 /* Menu */
136 static int gm_tm;
137 static image msklh[2];
138 static image mbarl;
139 static image mbarm;
140 static image mbarr;
141 static image mbaro;
142 static image mslotl;
143 static image mslotm;
144 static image mslotr;
146 /* Map */
147 static const char *anm[ANIT - 1][5] = {
148 {"WALL22_1", "WALL23_1", "WALL23_2", NULL, NULL},
149 {"WALL58_1", "WALL58_2", "WALL58_3", NULL, NULL},
150 {"W73A_1", "W73A_2", NULL, NULL, NULL},
151 {"RP2_1", "RP2_2", "RP2_3", "RP2_4", NULL}
152 };
153 static int max_wall_width;
154 static int max_wall_height;
155 static int max_textures;
156 static image walp[256];
157 static byte walani[256];
158 static image anip[ANIT][5];
159 static byte anic[ANIT];
160 static image horiz;
162 /* Texture cache */
164 // https://blackpawn.com/texts/lightmaps/
165 static node *R_node_alloc (node *p, int w, int h) {
166 assert(p);
167 assert(w > 0);
168 assert(h > 0);
169 if (p->left) {
170 assert(p->right);
171 node *n = R_node_alloc(p->left, w, h);
172 return n ? n : R_node_alloc(p->right, w, h);
173 } else {
174 int pw = p->r - p->l + 1;
175 int ph = p->b - p->t + 1;
176 if (p->leaf || pw < w || ph < h) {
177 return NULL;
178 } else if (pw == w && ph == h) {
179 p->leaf = 1;
180 return p;
181 } else {
182 p->left = malloc(sizeof(node));
183 p->right = malloc(sizeof(node));
184 if (pw - w > ph - h) {
185 *p->left = (node) {
186 .up = p,
187 .l = p->l,
188 .t = p->t,
189 .r = p->l + w - 1,
190 .b = p->b
191 };
192 *p->right = (node) {
193 .up = p,
194 .l = p->l + w,
195 .t = p->t,
196 .r = p->r,
197 .b = p->b
198 };
199 } else {
200 *p->left = (node) {
201 .up = p,
202 .l = p->l,
203 .t = p->t,
204 .r = p->r,
205 .b = p->t + h - 1
206 };
207 *p->right = (node) {
208 .up = p,
209 .l = p->l,
210 .t = p->t + h,
211 .r = p->r,
212 .b = p->b
213 };
215 return R_node_alloc(p->left, w, h);
220 static int R_node_have_leaf (node *n) {
221 return n && (n->leaf || R_node_have_leaf(n->left) || R_node_have_leaf(n->right));
224 static void R_node_free_recursive (node *n) {
225 if (n) {
226 R_node_free_recursive(n->left);
227 R_node_free_recursive(n->right);
228 free(n);
232 static void R_node_free (node *n) {
233 if (n) {
234 //logo("free node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
235 assert(n->leaf);
236 assert(n->left == NULL);
237 assert(n->right == NULL);
238 n->leaf = 0;
239 n->base = NULL;
240 node *p = n->up;
241 while (p != NULL) {
242 assert(p->leaf == 0);
243 assert(p->left);
244 assert(p->right);
245 if (R_node_have_leaf(p) == 0) {
246 R_node_free_recursive(p->left);
247 p->left = NULL;
248 R_node_free_recursive(p->right);
249 p->right = NULL;
250 p = p->up;
251 } else {
252 p = NULL;
258 static void R_cache_get_max_texture_size (int *w, int *h) {
259 GLint size = 0;
260 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
261 size = min(max(size, 0), 512); // more can be buggy on older hardware
262 *w = size;
263 *h = size;
266 static void R_gl_bind_texture (GLuint id) {
267 if (id != lastTexture) {
268 glBindTexture(GL_TEXTURE_2D, id);
272 static cache *R_cache_new (void) {
273 int w, h;
274 GLuint id;
275 cache *c = NULL;
276 R_cache_get_max_texture_size(&w, &h);
277 if (w && h) {
278 glGenTextures(1, &id);
279 if (id) {
280 R_gl_bind_texture(id);
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
283 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
284 c = malloc(sizeof(cache));
285 if (c != NULL) {
286 *c = (cache) {
287 .id = id,
288 .root.r = w - 1,
289 .root.b = h - 1
290 };
291 } else {
292 glDeleteTextures(1, &id);
296 //logo("new cache %p\n", c);
297 return c;
300 static void R_cache_free (cache *root, int freetexture) {
301 cache *next;
302 cache *c = root;
303 while (c != NULL) {
304 next = c->next;
305 R_node_free_recursive(c->root.left);
306 R_node_free_recursive(c->root.right);
307 if (freetexture && c->id != 0) {
308 glDeleteTextures(1, &c->id);
310 free(c);
311 c = next;
315 static node *R_cache_alloc (cache *root, int w, int h) {
316 assert(root);
317 assert(w > 0 && h > 0);
318 node *n = NULL;
319 cache *p = NULL;
320 cache *c = root;
321 int maxw, maxh;
322 R_cache_get_max_texture_size(&maxw, &maxh);
323 if (w <= maxw && h <= maxh) {
324 while (c && !n) {
325 n = R_node_alloc(&c->root, w, h);
326 if (n) {
327 assert(n->leaf);
328 n->base = c;
330 p = c;
331 c = c->next;
333 if (!n) {
334 c = R_cache_new();
335 if (c) {
336 p->next = c;
337 n = R_node_alloc(&c->root, w, h);
338 if (n) {
339 assert(n->leaf);
340 n->base = c;
345 if (n) {
346 //logo("new node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
347 } else {
348 logo("new node failed\n");
350 return n;
353 static void R_cache_update (node *n, const void *data, int x, int y, int w, int h) {
354 assert(n);
355 assert(n->leaf);
356 assert(n->base);
357 assert(data);
358 assert(x >= 0);
359 assert(y >= 0);
360 assert(n->l + x + w - 1 <= n->r);
361 assert(n->t + y + h - 1 <= n->b);
362 R_gl_bind_texture(n->base->id);
363 glTexSubImage2D(GL_TEXTURE_2D, 0, n->l + x, n->t + y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
366 /* Generic helpers */
368 static void R_init_playpal (void) {
369 int i;
370 byte *vgapal = M_lock(F_getresid("PLAYPAL"));
371 for (i = 0; i < 256; i++) {
372 playpal[i] = (rgb) {
373 .r = vgapal[i * 3 + 0] * 255 / 63,
374 .g = vgapal[i * 3 + 1] * 255 / 63,
375 .b = vgapal[i * 3 + 2] * 255 / 63,
376 };
377 bright[i] = ((int)vgapal[i * 3 + 0] + vgapal[i * 3 + 1] + vgapal[i * 3 + 2]) * 8 / (63 * 3);
379 M_unlock(vgapal);
382 static vgaimg *R_getvga (int id) {
383 int loaded = M_was_locked(id);
384 vgaimg *v = M_lock(id);
385 if (v != NULL && !loaded) {
386 v->w = short2host(v->w);
387 v->h = short2host(v->h);
388 v->x = short2host(v->x);
389 v->y = short2host(v->y);
391 return v;
394 static rgba *R_extract_flame_spr (vgaimg *v) {
395 static const byte flametab[16] = {
396 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0xD5,
397 0xD6, 0xD7, 0xA1, 0xA0, 0xE3, 0xE2, 0xE1, 0xE0
398 };
399 int i, j;
400 rgba *s = malloc(v->w * v->h * sizeof(rgba));
401 if (s != NULL) {
402 for (j = 0; j < v->h; j++) {
403 for (i = 0; i < v->w; i++) {
404 int k = j * v->w + i;
405 byte c = v->data[k] + bright[DEFAULT_SKY_COLOR];
406 s[k] = (rgba) {
407 .r = playpal[flametab[c]].r,
408 .g = playpal[flametab[c]].g,
409 .b = playpal[flametab[c]].b,
410 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
411 };
415 return s;
418 static rgba *R_extract_smoke_spr (vgaimg *v) {
419 int i, j;
420 rgba *s = malloc(v->w * v->h * sizeof(rgba));
421 if (s != NULL) {
422 for (j = 0; j < v->h; j++) {
423 for (i = 0; i < v->w; i++) {
424 int k = j * v->w + i;
425 byte c = ((v->data[k] + bright[DEFAULT_SKY_COLOR]) + 0x60) ^ 0x0F;
426 byte a = 0xFF - ((int)playpal[c].r + playpal[c].g + playpal[c].b) / 3;
427 s[k] = (rgba) {
428 .r = playpal[c].r,
429 .g = playpal[c].g,
430 .b = playpal[c].b,
431 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : a,
432 };
436 return s;
439 static rgba *R_extract_mask_spr (vgaimg *v) {
440 int i, j;
441 rgba *s = malloc(v->w * v->h * sizeof(rgba));
442 if (s != NULL) {
443 for (j = 0; j < v->h; j++) {
444 for (i = 0; i < v->w; i++) {
445 int k = j * v->w + i;
446 byte c = v->data[k];
447 if (c >= 0x70 && c <= 0x7F) {
448 byte mask = c - 0x70;
449 mask = 0xFF - ((mask << 4) | mask);
450 s[k] = (rgba) {
451 .r = mask,
452 .g = mask,
453 .b = mask,
454 .a = 0xFF,
455 };
456 } else {
457 s[k] = (rgba) {
458 .r = 0,
459 .g = 0,
460 .b = 0,
461 .a = 0,
462 };
467 return s;
470 static rgba *R_extract_rgba_spr (vgaimg *v) {
471 int i, j;
472 rgba *s = malloc(v->w * v->h * sizeof(rgba));
473 if (s != NULL) {
474 for (j = 0; j < v->h; j++) {
475 for (i = 0; i < v->w; i++) {
476 int k = j * v->w + i;
477 byte c = v->data[k];
478 s[k] = (rgba) {
479 .r = playpal[c].r,
480 .g = playpal[c].g,
481 .b = playpal[c].b,
482 .a = c == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
483 };
487 return s;
490 /* OpenGL helpers */
492 static image R_gl_create_image (const rgba *buf, int w, int h) {
493 node *n = R_cache_alloc(root, w, h);
494 if (n) {
495 R_cache_update(n, buf, 0, 0, w, h);
497 return (image) {
498 .n = n,
499 .w = w,
500 .h = h,
501 .res = -1
502 };
505 static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
506 image img;
507 vgaimg *v = R_getvga(id);
508 if (v != NULL) {
509 rgba *buf = (*fn)(v);
510 img = R_gl_create_image(buf, v->w, v->h);
511 img.x = v->x;
512 img.y = v->y;
513 img.res = id;
514 M_unlock(v);
515 free(buf);
516 } else {
517 img = (image) {
518 .res = id
519 };
521 return img;
524 static image R_gl_getimage (int id) {
525 return R_gl_get_special_image(id, &R_extract_rgba_spr);
528 static image R_gl_loadimage (const char name[8]) {
529 return R_gl_getimage(F_getresid(name));
532 static image R_gl_get_special_spr (const char n[4], int s, int d, rgba *(*fn)(vgaimg*)) {
533 return R_gl_get_special_image(F_getsprid(n, s, d), fn);
536 static void R_gl_free_image (image *img) {
537 if (img->n != NULL && img->res >= 0) {
538 R_node_free(img->n);
540 img->n = NULL;
541 img->res = -1;
544 static void R_gl_draw_quad (int x, int y, int w, int h) {
545 glBegin(GL_QUADS);
546 glVertex2i(x + w, y);
547 glVertex2i(x, y);
548 glVertex2i(x, y + h);
549 glVertex2i(x + w, y + h);
550 glEnd();
553 static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip) {
554 if (img->n) {
555 GLfloat nw = img->n->base->root.r + 1;
556 GLfloat nh = img->n->base->root.b + 1;
557 GLfloat ax = (flip ? img->n->l : img->n->r + 1) / nw;
558 GLfloat bx = (flip ? img->n->r + 1 : img->n->l) / nh;
559 GLfloat ay = (img->n->t) / nw;
560 GLfloat by = (img->n->b + 1) / nh;
561 R_gl_bind_texture(img->n->base->id);
562 glEnable(GL_TEXTURE_2D);
563 glBegin(GL_QUADS);
564 glTexCoord2f(ax, ay); glVertex2i(x + w, y);
565 glTexCoord2f(bx, ay); glVertex2i(x, y);
566 glTexCoord2f(bx, by); glVertex2i(x, y + h);
567 glTexCoord2f(ax, by); glVertex2i(x + w, y + h);
568 glEnd();
569 } else {
570 glColor3ub(255, 0, 0);
571 glDisable(GL_BLEND);
572 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
573 glDisable(GL_TEXTURE_2D);
574 R_gl_draw_quad(x, y, w, h);
578 /* fit image into rectangle without applying offset and transparency */
579 static void R_gl_draw_image_ext (image *img, int x, int y, int w, int h) {
580 glDisable(GL_BLEND);
581 glColor3ub(255, 255, 255);
582 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
583 R_gl_draw_textured(img, x, y, w, h, 0);
586 /* draw sprite with offset and coloring */
587 static void R_gl_draw_image_color (image *img, int x, int y, int flip) {
588 int xx = flip ? x - img->w + img->x : x - img->x;
589 glEnable(GL_BLEND);
590 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
591 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
594 /* draw sprite with offset */
595 static void R_gl_draw_image (image *img, int x, int y, int flip) {
596 glColor3ub(255, 255, 255);
597 R_gl_draw_image_color(img, x, y, flip);
600 static void R_gl_set_color (byte c) {
601 glColor3ub(playpal[c].r, playpal[c].g, playpal[c].b);
604 static void R_gl_setclip (int x, int y, int w, int h) {
605 glScissor(x, SCRH - h - y, w, h);
608 static void R_gl_setmatrix (void) {
609 glScissor(0, 0, SCRW, SCRH);
610 glViewport(0, 0, SCRW, SCRH);
611 glMatrixMode(GL_PROJECTION);
612 glLoadIdentity();
613 glOrtho(0, SCRW, SCRH, 0, 0, 1);
614 glMatrixMode(GL_MODELVIEW);
615 glLoadIdentity();
618 /* --- Misc --- */
620 static image Z_getspr (const char n[4], int s, int d, char *dir) {
621 int h = F_getsprid(n, s, d);
622 if (dir != NULL) {
623 *dir = (h & 0x8000) ? 1 : 0;
625 return R_gl_getimage(h);
628 static void Z_putch_generic (image img[], int off, int ch) {
629 image *p = NULL;
630 if (ch > 32 && ch < 160) {
631 p = &img[ch - '!'];
633 if (p != NULL) {
634 R_gl_draw_image(p, prx, pry, 0);
635 prx += p->w - 1;
636 } else {
637 prx += off;
641 static void Z_printf_generic (image img[], int off, const char *fmt, va_list ap) {
642 int i;
643 char buf[80];
644 vsprintf(buf, fmt, ap);
645 for (i = 0; buf[i]; ++i) {
646 switch (buf[i]) {
647 case '\n':
648 pry += off + 1;
649 case '\r':
650 prx = 0;
651 break;
652 default:
653 Z_putch_generic(img, off, (byte)buf[i]);
658 static void Z_gotoxy (int x, int y) {
659 prx = x;
660 pry = y;
663 static void Z_printbf (const char *fmt, ...) {
664 va_list a;
665 va_start(a, fmt);
666 Z_printf_generic(bfh, 12, fmt, a);
667 va_end(a);
670 static void Z_printsf (const char *fmt, ...) {
671 va_list a;
672 va_start(a, fmt);
673 Z_printf_generic(sfh, 7, fmt, a);
674 va_end(a);
677 static void Z_printhf (const char *fmt, ...) {
678 int i, c;
679 char buf[80];
680 va_list a;
681 va_start(a, fmt);
682 vsprintf(buf, fmt, a);
683 va_end(a);
684 for (i = 0; buf[i]; ++i) {
685 switch (buf[i]) {
686 case '0':
687 case '1':
688 case '2':
689 case '3':
690 case '4':
691 case '5':
692 case '6':
693 case '7':
694 case '8':
695 case '9':
696 c = buf[i] - '0';
697 break;
698 case '-':
699 c = 10;
700 break;
701 case '%':
702 c = 11;
703 break;
704 case '\n':
705 pry += 19;
706 case '\r':
707 c = -1;
708 prx = 0;
709 break;
710 default:
711 c = -1;
712 break;
714 if (c >= 0) {
715 R_gl_draw_image(&sth[c], prx, pry, 0);
717 prx += 14;
721 /* --- Menu --- */
723 static image *PL_getspr (int s, int d, int msk) {
724 int i = (s - 'A') * 2 + d;
725 return msk ? &plr_msk[i] : &plr_spr[i];
728 static void GM_draw (void) {
729 enum {MENU, MSG}; // copypasted from menu.c!
730 enum {
731 CANCEL, NEWGAME, LOADGAME, SAVEGAME, OPTIONS, QUITGAME, QUIT, ENDGAME, ENDGM,
732 PLR1, PLR2, COOP, DM, VOLUME, GAMMA, LOAD, SAVE, PLCOLOR, PLCEND, MUSIC, INTERP,
733 SVOLM, SVOLP, MVOLM, MVOLP, GAMMAM, GAMMAP, PL1CM, PL1CP, PL2CM, PL2CP
734 }; // copypasted from menu.c!
735 int i, j, k, x, y, cx, cy;
736 image *img;
737 gm_tm += 1;
738 if (mnu != NULL) {
739 cx = SCRW / 2;
740 cy = SCRH / 2;
741 if (mnu->type == MENU) {
742 y = cy - (mnu->n * 16 - 20) / 2;
743 Z_gotoxy(cx - mnu->x, y - 10); Z_printbf("%s", mnu->ttl);
744 for (i = 0; i < mnu->n; i++) {
745 if (mnu->t[i] == LOAD || mnu->t[i] == SAVE) {
746 j = y + i * 16 + 29;
747 R_gl_draw_image(&mslotl, cx - mnu->x, j, 0);
748 for (k = 8; k < 184; k += 8) {
749 R_gl_draw_image(&mslotm, cx - mnu->x + k, j, 0);
751 R_gl_draw_image(&mslotr, cx - mnu->x + 184, j, 0);
752 Z_gotoxy(cx - mnu->x + 4, j - 8);
753 if (input && i == save_mnu.cur) {
754 Z_printsf("%s_", ibuf);
755 } else {
756 Z_printsf("%s", savname[i]);
758 } else {
759 x = mnu->t[i] >= SVOLM ? (mnu->t[i] >= PL1CM ? 50 : 152) : 0;
760 Z_gotoxy(cx - mnu->x + x, y + i * 16 + 20);
761 Z_printbf("%s", mnu->m[i]);
762 switch (mnu->t[i]) {
763 case MUSIC:
764 Z_printbf(" '%s'", g_music);
765 break;
766 case INTERP:
767 Z_printbf("%s", fullscreen ? "ON" : "OFF");
768 break;
769 case PL1CM:
770 case PL1CP:
771 case PL2CM:
772 case PL2CP:
773 img = PL_getspr(*panimp, 0, 0);
774 R_gl_draw_image(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
775 img = PL_getspr(*panimp, 0, 1);
776 R_gl_set_color(pcolortab[(mnu->t[i] == PL1CM) ? p1color : p2color] + PLAYER_COLOR_OFFSET);
777 R_gl_draw_image_color(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
778 break;
779 case SVOLM:
780 case SVOLP:
781 case MVOLM:
782 case MVOLP:
783 case GAMMAM:
784 case GAMMAP:
785 j = y + i * 16 + 20;
786 R_gl_draw_image(&mbarl, cx - mnu->x, j, 0);
787 for (k = 8; k < 144; k += 8) {
788 R_gl_draw_image(&mbarm, cx - mnu->x + k, j, 0);
790 R_gl_draw_image(&mbarr, cx - mnu->x + 144, j, 0);
791 switch (mnu->t[i]) {
792 case SVOLM:
793 k = snd_vol;
794 break;
795 case MVOLM:
796 k = mus_vol;
797 break;
798 case GAMMAM:
799 k = gamma << 5;
800 break;
801 default:
802 k = 0;
803 break;
805 R_gl_draw_image(&mbaro, cx - mnu->x + 8 + k, j, 0);
806 break;
810 R_gl_draw_image(&msklh[(gm_tm / 6) & 1], cx - mnu->x - 25, y + mnu->cur * 16 + 20 - 8, 0);
811 } else if (mnu->type == MSG) {
812 Z_gotoxy(cx - strlen(mnu->ttl) * 7 / 2, cy - 10); Z_printsf(mnu->ttl);
813 Z_gotoxy(cx - 24, SCRH / 2); Z_printsf("(Y/N)");
814 } else {
815 ERR_fatal("Unknown menu type %i\n", mnu->type);
820 /* --- View --- */
822 static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int fg) {
823 int i, j;
824 assert(minx >= 0 && minx <= FLDW);
825 assert(miny >= 0 && miny <= FLDH);
826 assert(maxx >= 0 && maxx <= FLDW);
827 assert(maxy >= 0 && maxy <= FLDH);
828 for (j = miny; j < maxy; j++) {
829 for (i = minx; i < maxx; i++) {
830 byte id = fld[j * FLDW + i];
831 if (id != 0) {
832 if (walp[id].res < 0) {
833 if (fg) {
834 switch (R_get_special_id(id)) {
835 case 1:
836 glColor4ub(0, 0, 255, 127);
837 break;
838 case 2:
839 glColor4ub(0, 127, 0, 127);
840 break;
841 case 3:
842 glColor4ub(127, 0, 0, 127);
843 break;
844 default:
845 glColor4ub(0, 0, 0, 127);
846 break;
848 glEnable(GL_BLEND);
849 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
850 glDisable(GL_TEXTURE_2D);
851 R_gl_draw_quad(i * CELW, j * CELW, CELW, CELH);
853 } else {
854 R_gl_draw_image(&walp[id], i * CELW, j * CELH, 0);
861 static void R_draw_dots (void) {
862 int i;
863 glDisable(GL_BLEND);
864 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
865 glDisable(GL_TEXTURE_2D);
866 glBegin(GL_POINTS);
867 for (i = 0; i < MAXDOT; i++) {
868 if (dot[i].t != 0) {
869 R_gl_set_color(dot[i].c);
870 glVertex2i(dot[i].o.x, dot[i].o.y + 1);
873 glEnd();
876 static void R_draw_items (void) {
877 int i, s;
878 for (i = 0; i < MAXITEM; ++i) {
879 s = -1;
880 if (it[i].t && it[i].s >= 0) {
881 switch (it[i].t & 0x7FFF) {
882 case I_ARM1:
883 s = it[i].s / 9 + 18;
884 break;
885 case I_ARM2:
886 s = it[i].s / 9 + 20;
887 break;
888 case I_MEGA:
889 s = it[i].s / 2 + 22;
890 break;
891 case I_INVL:
892 s = it[i].s / 2 + 26;
893 break;
894 case I_SUPER:
895 case I_RTORCH:
896 case I_GTORCH:
897 case I_BTORCH:
898 s = it[i].s / 2 + (it[i].t - I_SUPER) * 4 + 35;
899 break;
900 case I_GOR1: case I_FCAN:
901 s = it[i].s / 2 + (it[i].t - I_GOR1) * 3 + 51;
902 break;
903 case I_AQUA:
904 s = 30;
905 break;
906 case I_SUIT:
907 s = 34;
908 break;
909 case I_KEYR:
910 case I_KEYG:
911 case I_KEYB:
912 s = (it[i].t & 0x7FFF) - I_KEYR + 31;
913 break;
914 case I_GUN2:
915 s = 57;
916 break;
917 default:
918 s = (it[i].t & 0x7FFF) - 1;
921 if (s >= 0) {
922 R_gl_draw_image(&item_spr[s], it[i].o.x, it[i].o.y, item_sprd[s]);
927 static int standspr (player_t *p) {
928 if (p->f & PLF_UP) {
929 return 'X';
930 } else if (p->f & PLF_DOWN) {
931 return 'Z';
932 } else {
933 return 'E';
937 static int wpnspr (player_t *p) {
938 if (p->f & PLF_UP) {
939 return 'C';
940 } else if(p->f & PLF_DOWN) {
941 return 'E';
942 } else {
943 return 'A';
947 static void R_draw_player (player_t *p) {
948 enum {STAND, GO, DIE, SLOP, DEAD, MESS, OUT, FALL}; // copypasted from player.c!
949 static const int wytab[] = {-1, -2, -1, 0};
950 int s = 'A';
951 int w = 0;
952 int wx = 0;
953 int wy = 0;
954 switch (p->st) {
955 case STAND:
956 if (p->f & PLF_FIRE) {
957 s = standspr(p) + 1;
958 w = wpnspr(p) + 1;
959 } else if (p->pain) {
960 s = 'G';
961 w = 'A';
962 wx = p->d ? 2 : -2;
963 wy = 1;
964 } else {
965 s = standspr(p);
966 w = wpnspr(p);
968 break;
969 case DEAD:
970 s = 'N';
971 break;
972 case MESS:
973 s = 'W';
974 break;
975 case GO:
976 if (p->pain) {
977 s = 'G';
978 w = 'A';
979 wx = p->d ? 2 : -2;
980 wy = 1;
981 } else {
982 s = plr_goanim[p->s / 8];
983 w = (p->f & PLF_FIRE) ? 'B' : 'A';
984 wx = p->d ? 2 : -2;
985 wy = 1 + wytab[s - 'A'];
987 break;
988 case DIE:
989 s = plr_dieanim[p->s];
990 break;
991 case SLOP:
992 s = plr_slopanim[p->s];
993 break;
994 case OUT:
995 s = 0;
996 break;
998 if (p->wpn == 0) {
999 w = 0;
1001 if (w) {
1002 R_gl_draw_image(&plr_wpn[(int)p->wpn][w -'A'], p->o.x + wx, p->o.y + wy, p->d);
1004 if (s) {
1005 R_gl_draw_image(&plr_spr[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
1006 R_gl_set_color(p->color + PLAYER_COLOR_OFFSET);
1007 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]);
1011 static void R_draw_monsters (void) {
1012 enum {SLEEP, GO, RUN, CLIMB, DIE, DEAD, ATTACK, SHOOT, PAIN, WAIT, REVIVE, RUNOUT}; // copypasted from monster.c!
1013 int i;
1014 for (i = 0; i < MAXMN; i++) {
1015 if (mn[i].t != MN_NONE) {
1016 int x = mn[i].o.x;
1017 int y = mn[i].o.y;
1018 if (mn[i].t < MN__LAST) {
1019 if ((mn[i].t != MN_SOUL && mn[i].t != MN_PAIN) || mn[i].st != DEAD) {
1020 int ap = mn[i].ap[mn[i].ac];
1021 int d = (ap - 'A') * 2 + mn[i].d;
1022 int dir = mn_sprd[mn[i].t - 1][d];
1023 if (mn[i].t == MN_MAN && (ap == 'E' || ap == 'F')) {
1024 R_gl_draw_image(&mn_sgun[ap - 'E'], x, y, mn[i].d);
1026 R_gl_draw_image(&mn_spr[mn[i].t - 1][d], x, y, dir);
1027 if (mn[i].t == MN_MAN) {
1028 R_gl_set_color(MANCOLOR + PLAYER_COLOR_OFFSET);
1029 R_gl_draw_image_color(&mn_man_msk[d], x, y, dir);
1032 if (mn[i].t == MN_VILE && mn[i].st == SHOOT) {
1033 R_gl_draw_image(&mn_fspr[mn[i].ac / 3], mn[i].tx, mn[i].ty, 0);
1035 } else if (mn[i].t == MN_PL_DEAD || mn[i].t == MN_PL_MESS) {
1036 int type = mn[i].t - MN_PL_DEAD;
1037 R_gl_draw_image(&pl_spr[type], x, y, 0);
1038 R_gl_set_color(mn[i].d);
1039 R_gl_draw_image_color(&pl_msk[type], x, y, 0);
1045 static void R_draw_weapons (void) {
1046 enum {NONE, ROCKET, PLASMA, APLASMA, BALL1, BALL2, BALL7, BFGBALL, BFGHIT, MANF, REVF, FIRE}; // copypasted from weapons.c!
1047 int i, s, d, x, y;
1048 for (i = 0; i < MAXWPN; ++i) {
1049 s = -1;
1050 d = 0;
1051 switch (wp[i].t) {
1052 case REVF:
1053 case ROCKET:
1054 d = wp[i].s;
1055 if (d < 2) {
1056 d = wp[i].o.xv > 0 ? 1 : 0;
1057 x = abs(wp[i].o.xv);
1058 y = wp[i].o.yv;
1059 s = 0;
1060 if (y < 0) {
1061 if (-y >= x) {
1062 s = 30;
1064 } else if (y > 0) {
1065 if (y >= x / 2) {
1066 s = 31;
1069 } else {
1070 s = (d - 2) / 2 + 1;
1071 d = 0;
1073 break;
1074 case MANF:
1075 s=wp[i].s;
1076 if (s >= 2) {
1077 s /= 2;
1078 break;
1080 case PLASMA:
1081 case APLASMA:
1082 case BALL1:
1083 case BALL7:
1084 case BALL2:
1085 s = wp[i].s;
1086 if (s >= 2) {
1087 s = s / 2 + 1;
1089 switch (wp[i].t) {
1090 case PLASMA:
1091 s += 4;
1092 break;
1093 case APLASMA:
1094 s += 11;
1095 break;
1096 case BALL1:
1097 s += 32;
1098 break;
1099 case BALL2:
1100 s += 42;
1101 break;
1102 case BALL7:
1103 s += 37;
1104 d = wp[i].o.xv >= 0 ? 1 : 0;
1105 break;
1106 case MANF:
1107 s += 47;
1108 d= wp[i].o.xv>=0 ? 1 : 0;
1109 break;
1111 break;
1112 case BFGBALL:
1113 s = wp[i].s;
1114 if (s >= 2) {
1115 s = s / 2 + 1;
1117 s += 18;
1118 break;
1119 case BFGHIT:
1120 s = wp[i].s / 2 + 26;
1121 break;
1123 if (s >= 0) {
1124 R_gl_draw_image(&wp_spr[s * 2 + d], wp[i].o.x, wp[i].o.y, wp_sprd[s * 2 + d]);
1129 static void R_draw_smoke (void) {
1130 int i, s;
1131 for (i = 0; i < MAXSMOK; ++i) {
1132 if (sm[i].t) {
1133 switch (sm[i].s) {
1134 case 0:
1135 s = sm[i].t;
1136 if (s >= (SMSN - 1) * 3) {
1137 s = 0;
1138 } else {
1139 s = SMSN - 1 - s / 3;
1141 R_gl_draw_image(&smk_spr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1142 break;
1143 case 1:
1144 s = sm[i].t;
1145 if (s >= FLSN - 1) {
1146 s = 0;
1147 } else {
1148 s = FLSN - 1 - s;
1150 R_gl_draw_image(&smk_fspr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1151 break;
1157 static void R_draw_effects (void) {
1158 enum {NONE, TFOG, IFOG, BUBL}; // copypasted from fx.c
1159 int i, s;
1160 for (i = 0; i < MAXFX; ++i) {
1161 switch (fx[i].t) {
1162 case TFOG:
1163 s = fx[i].s / 2;
1164 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1165 break;
1166 case IFOG:
1167 s = fx[i].s / 2 + 10;
1168 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1169 break;
1170 case BUBL:
1171 glDisable(GL_BLEND);
1172 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1173 glDisable(GL_TEXTURE_2D);
1174 glBegin(GL_POINTS);
1175 R_gl_set_color(0xC0 + fx[i].s);
1176 glVertex2i(fx[i].x >> 8, (fx[i].y >> 8) + 1);
1177 glEnd();
1178 break;
1183 static int get_pu_st (int t) {
1184 if (t >= PL_FLASH) {
1185 return 1;
1186 } else if((t / 9) & 1) {
1187 return 0;
1188 } else {
1189 return 1;
1193 static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
1194 glPushMatrix();
1195 R_gl_setclip(x, y, w, h);
1196 glTranslatef(x, y, 0);
1197 if (w_horiz && horiz.n != NULL) {
1198 R_gl_draw_image_ext(&horiz, 0, 0, w, h);
1199 if (sky_type == 2 && lt_time < 0) {
1200 image *tanderbolt = &ltn[lt_type][lt_time < -5 ? 0 : 1];
1201 if (!lt_side) {
1202 R_gl_draw_image(tanderbolt, 0, lt_ypos, 0);
1203 } else {
1204 R_gl_draw_image(tanderbolt, w - 1, lt_ypos, 1);
1207 } else {
1208 glDisable(GL_BLEND);
1209 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1210 glDisable(GL_TEXTURE_2D);
1211 R_gl_set_color(DEFAULT_SKY_COLOR);
1212 R_gl_draw_quad(0, 0, w, h);
1214 int maxx = min((camx + w) / CELW + 1, FLDW);
1215 int maxy = min((camy + h) / CELH + 1, FLDH);
1216 int minx = max((camx - max_wall_width) / CELW, 0);
1217 int miny = max((camy - max_wall_height) / CELH, 0);
1218 glTranslatef(-camx, -camy, 0);
1219 R_draw_fld((byte*)fldb, minx, miny, maxx, maxy, 0);
1220 R_draw_dots();
1221 R_draw_items();
1222 R_draw_player(&pl1);
1223 if (_2pl) {
1224 R_draw_player(&pl2);
1226 R_draw_monsters();
1227 R_draw_weapons();
1228 R_draw_smoke();
1229 R_draw_effects();
1230 R_draw_fld((byte*)fldf, minx, miny, maxx, maxy, 1);
1231 glTranslatef(camx, camy, 0);
1232 if (sky_type == 2 && (lt_time == -4 || lt_time == -2)) {
1233 glColor4ub(255, 255, 255, 255);
1234 glEnable(GL_BLEND);
1235 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1236 glDisable(GL_TEXTURE_2D);
1237 R_gl_draw_quad(0, 0, w, h);
1239 glPopMatrix();
1242 static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
1243 p->looky = min(max(p->looky, -SCRH / 4), SCRH / 4); // TODO remove writeback
1244 int st = stone.w;
1245 int cw = w - st;
1246 int cx = min(max(p->o.x, cw / 2), FLDW * CELW - cw / 2);
1247 int cy = min(max(p->o.y - 12 + p->looky, h / 2), FLDH * CELH - h / 2);
1248 int camx = max(cx - cw / 2, 0);
1249 int camy = max(cy - h / 2, 0);
1250 glPushMatrix();
1251 R_draw_view(x, y + 1, cw, h - 2, camx, camy);
1252 glTranslatef(x, y, 0);
1253 if (p->invl) {
1254 if (get_pu_st(p->invl)) {
1255 glEnable(GL_BLEND);
1256 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
1257 glDisable(GL_TEXTURE_2D);
1258 glColor4ub(191, 191, 191, 255);
1259 R_gl_draw_quad(0, 0, cw, h);
1261 } else {
1262 if (p->suit && get_pu_st(p->suit)) {
1263 glEnable(GL_BLEND);
1264 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1265 glDisable(GL_TEXTURE_2D);
1266 glColor4ub(0, 255, 0, 192);
1267 R_gl_draw_quad(0, 0, cw, h);
1269 int f = min(max(p->pain * 3, 0), 255);
1270 if (f > 0) {
1271 glEnable(GL_BLEND);
1272 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1273 glDisable(GL_TEXTURE_2D);
1274 glColor4ub(255, 0, 0, f);
1275 R_gl_draw_quad(0, 0, cw, h);
1278 R_gl_setclip(x, y, w, h);
1279 glTranslatef(-x + cw, 0, 0);
1280 R_gl_draw_image(&stone, 0, 0, 0);
1281 int i = stone.h;
1282 while (i < h) {
1283 R_gl_draw_image(&stone2, 0, i, 0);
1284 i += stone2.h;
1286 if (p->drawst & PL_DRAWAIR) {
1287 if (p->air < PL_AIR) {
1288 int a = min(max(p->air, 0), MAXAIR) * 100 / MAXAIR;
1289 glDisable(GL_BLEND);
1290 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1291 glDisable(GL_TEXTURE_2D);
1292 R_gl_set_color(0xC8);
1293 R_gl_draw_quad(10, 49, a, 2);
1296 if (p->drawst & PL_DRAWLIFE) {
1297 Z_gotoxy(10, 7);
1298 Z_printhf("%3d%%", p->life);
1300 if (p->drawst & PL_DRAWARMOR) {
1301 Z_gotoxy(10, 7 + 19);
1302 Z_printhf("%3d%%", p->armor);
1304 if (p->drawst & PL_DRAWWPN) {
1305 switch(p->wpn) {
1306 case 2:
1307 case 5:
1308 i = p->ammo;
1309 break;
1310 case 3:
1311 case 4:
1312 case 9:
1313 i = p->shel;
1314 break;
1315 case 6:
1316 i = p->rock;
1317 break;
1318 case 7:
1319 case 8:
1320 i = p->cell;
1321 break;
1322 case 10:
1323 i = p->fuel;
1324 break;
1325 default:
1326 i = -1;
1327 break;
1329 // weapon
1330 if (p->wpn >= 0) {
1331 R_gl_draw_image(&sth[12 + p->wpn], st - 88, 58 + 19, 0);
1333 // ammo
1334 if (p->wpn >= 2) {
1335 Z_gotoxy(st - 10 - 5 * 14, 58 + 2);
1336 Z_printhf("%5d", i);
1339 if (p->drawst & PL_DRAWFRAG && g_dm) {
1340 Z_gotoxy(st - 5 - 5 * 14, 77 + 5);
1341 Z_printhf("%5d", p->frag);
1343 if (p->drawst & PL_DRAWKEYS) {
1344 int x, k, n;
1345 for (k = p->keys >> 4, n = 0, x = st - 75; n < 3; n++, k >>= 1, x += 9) {
1346 if (k & 1) {
1347 R_gl_draw_image(&keys[n], x, 91, 0);
1351 if (p->drawst & PL_DRAWLIVES && !_2pl) {
1352 Z_gotoxy(st - 35, 17);
1353 Z_printhf("%d", p->lives);
1355 glPopMatrix();
1358 /* --- Game --- */
1360 static void pl_info (player_t *p, int x, int y) {
1361 dword t = p->kills * 10920 / g_time;
1362 Z_gotoxy(x + 25, y); Z_printbf("KILLS");
1363 Z_gotoxy(x + 25, y + 15); Z_printbf("KPM");
1364 Z_gotoxy(x + 25, y + 30); Z_printbf("SECRETS %u / %u", p->secrets, sw_secrets);
1365 Z_gotoxy(x + 255, y); Z_printbf("%u", p->kills);
1366 Z_gotoxy(x + 255, y + 15); Z_printbf("%u.%u", t / 10, t % 10);
1369 static void R_draw_intermission (void) {
1370 int cx = SCRW / 2;
1371 word hr, mn, sc, h;
1372 Z_gotoxy(cx - 14*12/2, 20);
1373 Z_printbf("LEVEL COMPLETE");
1374 Z_calc_time(g_time, &hr, &mn, &sc);
1375 Z_gotoxy(cx - 12*12/2, 40);
1376 Z_printbf("TIME %u:%02u:%02u", hr, mn, sc);
1377 h = 40 + SCRH / 10;
1378 if (_2pl) {
1379 Z_gotoxy(cx - 10*12/2, h);
1380 Z_printbf("PLAYER ONE");
1381 h += 20;
1383 pl_info(&pl1, cx - 160, h);
1384 if (_2pl) {
1385 h += 30 + SCRH / 10;
1386 Z_gotoxy(cx - 10*12/2, h);
1387 Z_printbf("PLAYER TWO");
1388 h += 20;
1389 pl_info(&pl2, cx - 160, h);
1393 static void W_act (void) {
1394 int i, a;
1395 if (g_time % 3 == 0) {
1396 for (i = 1; i < max_textures; i++) {
1397 a = walani[i];
1398 if (a != 0) {
1399 anic[a]++;
1400 if (anip[a][anic[a]].res == -1) {
1401 anic[a] = 0;
1403 walp[i] = anip[a][anic[a]];
1409 void R_draw (void) {
1410 W_act();
1411 glClearColor(0, 0, 0, 1);
1412 glClear(GL_COLOR_BUFFER_BIT);
1413 glEnable(GL_SCISSOR_TEST);
1414 R_gl_setmatrix();
1415 switch (g_st) {
1416 case GS_ENDANIM:
1417 case GS_END2ANIM:
1418 case GS_DARKEN:
1419 case GS_BVIDEO:
1420 case GS_EVIDEO:
1421 case GS_END3ANIM:
1422 break;
1423 case GS_TITLE:
1424 R_gl_draw_image_ext(&scrnh[0], 0, 0, SCRW, SCRH);
1425 break;
1426 case GS_INTER:
1427 R_gl_draw_image_ext(&scrnh[1], 0, 0, SCRW, SCRH);
1428 R_draw_intermission();
1429 break;
1430 case GS_ENDSCR:
1431 R_gl_draw_image_ext(&scrnh[2], 0, 0, SCRW, SCRH);
1432 break;
1433 case GS_GAME:
1434 if (_2pl) {
1435 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH / 2);
1436 R_draw_player_view(&pl2, 0, SCRH / 2, SCRW, SCRH / 2);
1437 } else {
1438 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH);
1440 R_gl_setclip(0, 0, SCRW, SCRH);
1441 break;
1443 GM_draw();
1444 Y_swap_buffers();
1447 void R_alloc (void) {
1448 char s[10];
1449 int i, j, n;
1450 logo("R_alloc: load graphics\n");
1451 /* Game */
1452 scrnh[0] = R_gl_loadimage("TITLEPIC");
1453 assert(scrnh[0].n);
1454 scrnh[1] = R_gl_loadimage("INTERPIC");
1455 scrnh[2] = R_gl_loadimage("ENDPIC");
1456 for (i = 0; i < 2; i++) {
1457 sprintf(s, "LTN%c", '1' + i);
1458 for (j = 0; j < 2; j++) {
1459 ltn[i][j] = Z_getspr(s, j, 0, NULL);
1462 /* Smoke */
1463 for (i = 0; i < SMSN; i++) {
1464 smk_spr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_smoke_spr);
1466 for (i = 0; i < FLSN; i++) {
1467 smk_fspr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_flame_spr);
1469 /* Effects */
1470 for (i = 0; i < 10; i++) {
1471 fx_spr[i] = Z_getspr("TFOG", i, 0, fx_sprd + i);
1473 for (; i < 15; i++) {
1474 fx_spr[i] = Z_getspr("IFOG", i - 10, 0, fx_sprd + i);
1476 /* Weapons */
1477 for (i = 0; i < 4; i++) {
1478 wp_spr[i * 2] = Z_getspr("MISL", i, 1, wp_sprd + i * 2);
1479 wp_spr[i * 2 + 1] = Z_getspr("MISL", i, 2, wp_sprd + i * 2 + 1);
1481 for (; i < 6; i++) {
1482 wp_spr[i * 2] = Z_getspr("PLSS", i - 4, 1, wp_sprd + i * 2);
1483 wp_spr[i * 2 + 1] = Z_getspr("PLSS", i - 4, 2, wp_sprd + i * 2 + 1);
1485 for (; i < 11; i++) {
1486 wp_spr[i * 2] = Z_getspr("PLSE", i - 6, 1, wp_sprd + i * 2);
1487 wp_spr[i * 2 + 1] = Z_getspr("PLSE", i - 6, 2, wp_sprd + i * 2 + 1);
1489 for (; i < 13; i++) {
1490 wp_spr[i * 2] = Z_getspr("APLS", i - 11, 1, wp_sprd + i * 2);
1491 wp_spr[i * 2 + 1] = Z_getspr("APLS", i - 11, 2, wp_sprd + i * 2 + 1);
1493 for (; i < 18; i++) {
1494 wp_spr[i * 2] = Z_getspr("APBX", i - 13, 1, wp_sprd + i * 2);
1495 wp_spr[i * 2 + 1] = Z_getspr("APBX", i - 13, 2, wp_sprd + i * 2 + 1);
1497 for(; i < 20; i++) {
1498 wp_spr[i * 2] = Z_getspr("BFS1", i - 18, 1, wp_sprd + i * 2);
1499 wp_spr[i * 2 + 1] = Z_getspr("BFS1", i - 18, 2, wp_sprd + i * 2 + 1);
1501 for (; i < 26; i++) {
1502 wp_spr[i * 2] = Z_getspr("BFE1", i - 20, 1, wp_sprd + i * 2);
1503 wp_spr[i * 2 + 1] = Z_getspr("BFE1", i - 20, 2, wp_sprd + i * 2 + 1);
1505 for (; i < 30; i++) {
1506 wp_spr[i * 2] = Z_getspr("BFE2", i - 26, 1, wp_sprd + i * 2);
1507 wp_spr[i * 2 + 1] = Z_getspr("BFE2", i - 26, 2, wp_sprd + i * 2 + 1);
1509 for (; i < 32; i++) {
1510 wp_spr[i * 2] = Z_getspr("MISL", i - 30 + 4, 1, wp_sprd + i * 2);
1511 wp_spr[i * 2 + 1] = Z_getspr("MISL", i - 30 + 4, 2, wp_sprd + i * 2 + 1);
1513 for (; i < 37; i++) {
1514 wp_spr[i * 2] = Z_getspr("BAL1", i - 32, 1, wp_sprd + i * 2);
1515 wp_spr[i * 2 + 1] = Z_getspr("BAL1", i - 32, 2, wp_sprd + i * 2 + 1);
1517 for (; i < 42; i++) {
1518 wp_spr[i * 2] = Z_getspr("BAL7", i - 37, 1, wp_sprd + i * 2);
1519 wp_spr[i * 2 + 1] = Z_getspr("BAL7", i - 37, 2, wp_sprd + i * 2 + 1);
1521 for (; i < 47; i++) {
1522 wp_spr[i * 2] = Z_getspr("BAL2", i - 42, 1, wp_sprd + i * 2);
1523 wp_spr[i * 2 + 1] = Z_getspr("BAL2", i - 42, 2, wp_sprd + i * 2 + 1);
1525 for (; i < 49; i++) {
1526 wp_spr[i * 2] = Z_getspr("MANF", i - 47, 1, wp_sprd + i * 2);
1527 wp_spr[i * 2 + 1] = Z_getspr("MANF", i - 47, 2, wp_sprd + i * 2 + 1);
1529 /* Items */
1530 static const char snm[18][4] = {
1531 "CLIP", "SHEL", "ROCK", "CELL", "AMMO", "SBOX", "BROK", "CELP",
1532 "STIM", "MEDI", "BPAK",
1533 "CSAW", "SHOT", "SGN2", "MGUN", "LAUN", "PLAS", "BFUG"
1534 };
1535 static const char n4[4][4] = {
1536 "SOUL", "SMRT", "SMGT", "SMBT"
1537 };
1538 static const char n3[2][4] = {
1539 "GOR1", "FCAN"
1540 };
1541 for (i = 0; i < 18; i++) {
1542 item_spr[i] = Z_getspr(snm[i], 0, 0, item_sprd + i);
1544 for (; i < 20; i++) {
1545 item_spr[i] = Z_getspr("ARM1", i - 18, 0, item_sprd + i);
1546 item_spr[i + 2] = Z_getspr("ARM2", i - 18, 0, item_sprd + i);
1548 i+=2;
1549 for (; i < 26; i++) {
1550 item_spr[i] = Z_getspr("MEGA", i - 22, 0, item_sprd + i);
1552 for (; i < 30; i++) {
1553 item_spr[i] = Z_getspr("PINV", i - 26, 0, item_sprd + i);
1555 item_spr[30] = Z_getspr("AQUA", 0, 0, item_sprd + 30);
1556 item_spr[31] = Z_getspr("KEYR", 0, 0, item_sprd + 31);
1557 item_spr[32] = Z_getspr("KEYG", 0, 0, item_sprd + 32);
1558 item_spr[33] = Z_getspr("KEYB", 0, 0, item_sprd + 33);
1559 item_spr[34] = Z_getspr("SUIT", 0, 0, item_sprd + 34);
1560 for (n = 35, j = 0; j < 4; j++) {
1561 for (i = 0; i < 4; i++, n++) {
1562 item_spr[n] = Z_getspr(n4[j], i, 0, item_sprd + n);
1565 for (j = 0; j < 2; j++) {
1566 for (i = 0; i < 3; i++, n++) {
1567 item_spr[n] = Z_getspr(n3[j], i, 0, item_sprd + n);
1570 item_spr[57] = Z_getspr("GUN2", 0, 0, item_sprd + 57);
1571 /* Player */
1572 for (i = 0; i < 27; i++) {
1573 plr_spr[i * 2] = Z_getspr("PLAY", i, 1, plr_sprd + i * 2);
1574 plr_msk[i * 2] = R_gl_get_special_spr("PLAY", i, 1, &R_extract_mask_spr);
1575 plr_spr[i * 2 + 1] = Z_getspr("PLAY", i, 2, plr_sprd + i * 2 + 1);
1576 plr_msk[i * 2 + 1] = R_gl_get_special_spr("PLAY", i, 2, &R_extract_mask_spr);
1578 strncpy(s, "PWPx", 4);
1579 for (i = 1; i < 11; i++) {
1580 s[3] = (i < 10 ? '0' : 'A' - 10) + i;
1581 for (j = 0; j < 6; j++) {
1582 plr_wpn[i][j] = Z_getspr(s, j, 1, NULL);
1585 /* Monsters */
1586 static const char msn[MN_TN][4] = {
1587 "SARG", "TROO", "POSS", "SPOS", "CYBR", "CPOS", "BOSS", "BOS2", "HEAD", "SKUL",
1588 "PAIN", "SPID", "BSPI", "FATT", "SKEL", "VILE", "FISH", "BAR1", "ROBO", "PLAY"
1589 };
1590 static const int mms[MN_TN] = {
1591 14*2, 21*2, 21*2, 21*2, 16*2, 20*2, 15*2, 15*2, 12*2, 11*2,
1592 13*2, 19*2, 16*2, 20*2, 17*2, 29*2, 6*2, 2*2, 17*2, 23*2
1593 };
1594 mn_sgun[0] = Z_getspr("PWP4", 0, 1, NULL);
1595 mn_sgun[1] = Z_getspr("PWP4", 1, 1, NULL);
1596 for (j = 0; j < MN_TN; j++) {
1597 for (i = 0; i < mms[j]; i++) {
1598 mn_spr[j][i] = Z_getspr(msn[j], i / 2, (i & 1) + 1, &mn_sprd[j][i]);
1599 if (j == MN_MAN - 1) {
1600 mn_man_msk[i] = R_gl_get_special_spr(msn[j], i / 2, (i & 1) + 1, &R_extract_mask_spr);
1603 if (j == MN_BARREL - 1) {
1604 for (i = 4; i < 14; i++) {
1605 mn_spr[j][i] = Z_getspr("BEXP", i / 2 - 2, (i & 1) + 1, &mn_sprd[j][i]);
1609 for (i = 0; i < 8; i++) {
1610 mn_fspr[i] = Z_getspr("FIRE", i, 0, NULL);
1612 pl_spr[0] = Z_getspr("PLAY", 'N' - 'A', 0, NULL);
1613 pl_msk[0] = R_gl_get_special_spr("PLAY", 'N' - 'A', 0, &R_extract_mask_spr);
1614 pl_spr[1] = Z_getspr("PLAY", 'W' - 'A', 0, NULL);
1615 pl_msk[1] = R_gl_get_special_spr("PLAY", 'W' - 'A', 0, &R_extract_mask_spr);
1616 /* Misc */
1617 static const char mnm[22][8]={
1618 "STTNUM0", "STTNUM1", "STTNUM2", "STTNUM3", "STTNUM4",
1619 "STTNUM5", "STTNUM6", "STTNUM7", "STTNUM8", "STTNUM9",
1620 "STTMINUS", "STTPRCNT",
1621 "FISTA0", "CSAWA0", "PISTA0", "SHOTA0", "SGN2A0", "MGUNA0", "LAUNA0",
1622 "PLASA0", "BFUGA0", "GUN2A0"
1623 };
1624 stone = R_gl_loadimage("STONE");
1625 stone2 = R_gl_loadimage("STONE2");
1626 keys[0] = R_gl_loadimage("KEYRA0");
1627 keys[1] = R_gl_loadimage("KEYGA0");
1628 keys[2] = R_gl_loadimage("KEYBA0");
1629 for (i = 0; i < 22; i++) {
1630 sth[i] = R_gl_loadimage(mnm[i]);
1632 strcpy(s, "STBF_*");
1633 for (i = '!'; i < 160; i++) {
1634 s[5] = i;
1635 bfh[i - '!'] = R_gl_getimage(F_findres(s));
1637 for (i = '!'; i < 160; i++) {
1638 sprintf(s, "STCFN%03d", i);
1639 sfh[i - '!'] = R_gl_getimage(F_findres(s));
1641 strcpy(s, "WINUM*");
1642 for (i = '0'; i <= '9'; i++) {
1643 s[5] = i;
1644 bfh[i - '!'] = R_gl_loadimage(s);
1646 bfh[':' - '!'] = R_gl_loadimage("WICOLON");
1647 // menu
1648 msklh[0] = R_gl_loadimage("M_SKULL1");
1649 msklh[1] = R_gl_loadimage("M_SKULL2");
1650 mbarl = R_gl_loadimage("M_THERML");
1651 mbarm = R_gl_loadimage("M_THERMM");
1652 mbarr = R_gl_loadimage("M_THERMR");
1653 mbaro = R_gl_loadimage("M_THERMO");
1654 mslotl = R_gl_loadimage("M_LSLEFT");
1655 mslotm = R_gl_loadimage("M_LSCNTR");
1656 mslotr = R_gl_loadimage("M_LSRGHT");
1657 // walls
1658 for (i = 1; i < ANIT; i++) {
1659 for (j = 0; anm[i - 1][j]; j++) {
1660 anip[i][j] = R_gl_loadimage(anm[i - 1][j]);
1662 for(; j < 5; j++) {
1663 anip[i][j] = (image) {
1664 .n = NULL,
1665 .w = 8,
1666 .h = 8,
1667 .res = -1,
1668 };
1673 static void R_reload_textures (void);
1675 void R_set_videomode (int w, int h, int fullscreen) {
1676 assert(w > 0);
1677 assert(h > 0);
1678 int was = Y_videomode_setted();
1679 int flags = SYSTEM_USE_OPENGL;
1680 if (fullscreen) {
1681 flags |= SYSTEM_USE_FULLSCREEN;
1683 if (root != NULL) {
1684 R_cache_free(root, 0);
1685 root = NULL;
1687 int res = Y_set_videomode(w, h, flags);
1688 if (res == 0) {
1689 if (was == 0) {
1690 ERR_failinit("Unable to set video mode\n");
1692 } else {
1693 Y_get_videomode(&SCRW, &SCRH);
1694 root = R_cache_new();
1695 assert(root);
1696 R_alloc();
1697 R_reload_textures();
1701 void R_toggle_fullscreen (void) {
1702 R_cache_free(root, 0);
1703 Y_set_fullscreen(!Y_get_fullscreen());
1704 fullscreen = Y_get_fullscreen();
1705 Y_get_videomode(&SCRW, &SCRH);
1706 root = R_cache_new();
1707 assert(root);
1708 R_alloc();
1709 R_reload_textures();
1712 void R_init (void) {
1713 logo("R_init: intialize opengl render\n");
1714 R_init_playpal();
1715 R_set_videomode(SCRW, SCRH, fullscreen);
1718 void R_done (void) {
1719 R_cache_free(root, 1);
1720 Y_unset_videomode();
1721 root = NULL;
1724 void R_setgamma (int g) {
1725 gamma = g < 0 ? 0 : (g > 4 ? 4 : g);
1728 int R_getgamma (void) {
1729 return gamma;
1732 void R_get_name (int n, char s[8]) {
1733 assert(n >= 0 && n < 256);
1734 if (walp[n].res == -1) {
1735 memset(s, 0, 8);
1736 } else if (walp[n].res == -2) {
1737 memcpy(s, "_WATER_", 8);
1738 s[7] = '0' + (intptr_t)walp[n].n - 1;
1739 } else if (walani[n] > 0) {
1740 memcpy(s, anm[walani[n] - 1][0], 8);
1741 } else {
1742 F_getresname(s, walp[n].res & 0x7FFF);
1746 static short getani (char n[8]) {
1747 short i = 0;
1748 while (i < ANIT - 1 && strncasecmp(n, anm[i][0], 8) != 0) {
1749 i++;
1751 return i < ANIT - 1 ? i + 1 : 0;
1754 int R_get_special_id (int n) {
1755 assert(n >= 0 && n <= 256);
1756 return walp[n].res == -2 ? (intptr_t)walp[n].n : -1;
1759 static void R_reload_textures (void) {
1760 int i;
1761 char s[8];
1762 for (i = 0; i < max_textures; i++) {
1763 R_get_name(i, s);
1764 if (walp[i].res >= 0) {
1765 walp[i] = R_gl_getimage(walp[i].res);
1768 horiz = R_gl_getimage(horiz.res);
1771 void R_begin_load (void) {
1772 int i;
1773 for (i = 0; i < 256; i++) {
1774 if (walp[i].n != NULL && walp[i].res >= 0 && walani[i] == 0) {
1775 R_gl_free_image(&walp[i]);
1777 memset(&walp[i], 0, sizeof(image));
1778 walp[i].res = -1;
1779 walswp[i] = i;
1780 walani[i] = 0;
1782 memset(anic, 0, sizeof(anic));
1783 max_wall_width = 0;
1784 max_wall_height = 0;
1785 max_textures = 1;
1788 void R_load (char s[8], int f) {
1789 assert(max_textures < 256);
1790 if (!s[0]) {
1791 walp[max_textures] = (image) {
1792 .n = NULL,
1793 .x = 0,
1794 .y = 0,
1795 .w = 0,
1796 .h = 0,
1797 .res = -1,
1798 };
1799 } else if (strncasecmp(s, "_WATER_", 7) == 0) {
1800 walp[max_textures] = (image) {
1801 .n = (void*)((intptr_t)s[7] - '0' + 1),
1802 .x = 0,
1803 .y = 0,
1804 .w = 8,
1805 .h = 8,
1806 .res = -2,
1807 };
1808 } else {
1809 walp[max_textures] = R_gl_loadimage(s);
1810 if (f) {
1811 walp[max_textures].res |= 0x8000;
1813 if (s[0] == 'S' && s[1] == 'W' && s[4] == '_') {
1814 walswp[max_textures] = 0;
1816 walani[max_textures] = getani(s);
1818 max_wall_width = max(max_wall_width, walp[max_textures].w);
1819 max_wall_height = max(max_wall_height, walp[max_textures].h);
1820 max_textures++;
1823 void R_end_load (void) {
1824 int i, j, k, g;
1825 char s[8];
1826 j = max_textures;
1827 for (i = 1; i < 256 && j < 256; i++) {
1828 if (walswp[i] == 0) {
1829 R_get_name(i, s);
1830 s[5] ^= 1;
1831 g = F_getresid(s) | (walp[i].res & 0x8000);
1832 k = 1;
1833 while (k < 256 && walp[k].res != g) {
1834 k += 1;
1836 if (k >= 256) {
1837 k = j;
1838 j += 1;
1839 max_textures += 1;
1840 walp[k] = R_gl_getimage(g);
1841 walf[k] = g & 0x8000 ? 1 : 0;
1843 walswp[i] = k;
1844 walswp[k] = i;
1849 void R_loadsky (int sky) {
1850 char s[6];
1851 strcpy(s, "RSKYx");
1852 s[4] = '0' + sky;
1853 R_gl_free_image(&horiz);
1854 horiz = R_gl_loadimage(s);