DEADSOFTWARE

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