DEADSOFTWARE

opengl: fix building on non osx
[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 int l, t, r, b;
61 int leaf;
62 } node;
64 typedef struct cache {
65 struct cache *next;
66 struct node root;
67 GLuint id;
68 } cache;
70 typedef struct image {
71 node *n;
72 GLint x, y;
73 GLuint w, h;
74 int res;
75 } image;
77 /* Render Specific */
78 int SCRW = 320; // public
79 int SCRH = 200; // public
80 static int gamma;
81 static int fullscreen;
82 static SDL_Surface *surf;
83 static rgb playpal[256];
84 static byte bright[256];
85 static cache *root;
87 /* Game */
88 static image scrnh[3]; // TITLEPIC INTERPIC ENDPIC
89 static image ltn[2][2];
91 /* Smoke */
92 static image smk_spr[SMSN];
93 static image smk_fspr[FLSN];
95 /* Effects */
96 static image fx_spr[15];
97 static char fx_sprd[15];
99 /* Weapons */
100 static image wp_spr[49*2];
101 static char wp_sprd[49*2];
103 /* Items */
104 static image item_spr[58];
105 static char item_sprd[58];
107 /* Player */
108 static image plr_spr[27*2];
109 static image plr_msk[27*2];
110 static char plr_sprd[27*2];
111 static image plr_wpn[11][6];
113 /* Monsters */
114 static image pl_spr[2];
115 static image pl_msk[2];
116 static image mn_spr[MN_TN][29*2];
117 static image mn_man_msk[29*2];
118 static char mn_sprd[MN_TN][29*2];
119 static image mn_fspr[8];
120 static image mn_sgun[2];
122 /* Misc */
123 static image sth[22];
124 static image bfh[160 - '!'];
125 static image sfh[160 - '!'];
126 static image stone;
127 static image stone2;
128 static image keys[3];
129 static int prx = 0;
130 static int pry = 0;
132 /* Menu */
133 static int gm_tm;
134 static image msklh[2];
135 static image mbarl;
136 static image mbarm;
137 static image mbarr;
138 static image mbaro;
139 static image mslotl;
140 static image mslotm;
141 static image mslotr;
143 /* Map */
144 static const char *anm[ANIT - 1][5] = {
145 {"WALL22_1", "WALL23_1", "WALL23_2", NULL, NULL},
146 {"WALL58_1", "WALL58_2", "WALL58_3", NULL, NULL},
147 {"W73A_1", "W73A_2", NULL, NULL, NULL},
148 {"RP2_1", "RP2_2", "RP2_3", "RP2_4", NULL}
149 };
150 static int max_wall_width;
151 static int max_wall_height;
152 static int max_textures;
153 static image walp[256];
154 static byte walani[256];
155 static image anip[ANIT][5];
156 static byte anic[ANIT];
157 static image horiz;
159 /* Texture cache */
161 // https://blackpawn.com/texts/lightmaps/
162 static node *R_node_alloc (node *p, int w, int h) {
163 assert(p);
164 assert(w > 0);
165 assert(h > 0);
166 if (p->left) {
167 assert(p->right);
168 node *n = R_node_alloc(p->left, w, h);
169 return n ? n : R_node_alloc(p->right, w, h);
170 } else {
171 int pw = p->r - p->l + 1;
172 int ph = p->b - p->t + 1;
173 if (p->leaf || pw < w || ph < h) {
174 return NULL;
175 } else if (pw == w && ph == h) {
176 p->leaf = 1;
177 return p;
178 } else {
179 p->left = malloc(sizeof(node));
180 p->right = malloc(sizeof(node));
181 if (pw - w > ph - h) {
182 *p->left = (node) {
183 .l = p->l,
184 .t = p->t,
185 .r = p->l + w - 1,
186 .b = p->b
187 };
188 *p->right = (node) {
189 .l = p->l + w,
190 .t = p->t,
191 .r = p->r,
192 .b = p->b
193 };
194 } else {
195 *p->left = (node) {
196 .l = p->l,
197 .t = p->t,
198 .r = p->r,
199 .b = p->t + h - 1
200 };
201 *p->right = (node) {
202 .l = p->l,
203 .t = p->t + h,
204 .r = p->r,
205 .b = p->b
206 };
208 return R_node_alloc(p->left, w, h);
213 static cache *R_cache_new (void) {
214 GLuint id = 0;
215 GLint size = 0;
216 cache *c = NULL;
217 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
218 size /= 2; // TODO remove hack or detect ibook bug
219 if (size) {
220 glGenTextures(1, &id);
221 if (id) {
222 glBindTexture(GL_TEXTURE_2D, id);
223 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
224 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
225 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
226 int ok = glGetError() == GL_NO_ERROR;
227 glBindTexture(GL_TEXTURE_2D, 0);
228 if (ok) {
229 c = malloc(sizeof(cache));
230 if (c != NULL) {
231 *c = (cache) {
232 .id = id,
233 .root.r = size - 1,
234 .root.b = size - 1
235 };
238 if (c == NULL) {
239 glDeleteTextures(1, &id);
243 logo("new cache %p\n", c);
244 return c;
247 static node *R_cache_alloc (cache *root, int w, int h) {
248 assert(root);
249 assert(w > 0 && h > 0);
250 node *n = NULL;
251 cache *p = NULL;
252 cache *c = root;
253 while (c && !n) {
254 n = R_node_alloc(&c->root, w, h);
255 if (n) {
256 n->base = c;
258 p = c;
259 c = c->next;
261 if (!n) {
262 c = R_cache_new();
263 if (c) {
264 p->next = c;
265 n = R_node_alloc(&c->root, w, h);
266 if (n) {
267 n->base = c;
271 return n;
274 static void R_cache_update (node *n, const void *data, int w, int h) {
275 assert(n);
276 assert(n->base);
277 assert(data);
278 int nw = n->r - n->l + 1;
279 int nh = n->b - n->t + 1;
280 assert(w == nw);
281 assert(h == nh);
282 glBindTexture(GL_TEXTURE_2D, n->base->id);
283 glTexSubImage2D(GL_TEXTURE_2D, 0, n->l, n->t, nw, nh, GL_RGBA, GL_UNSIGNED_BYTE, data);
284 assert(glGetError() == GL_NO_ERROR);
285 glBindTexture(GL_TEXTURE_2D, 0);
288 /* Generic helpers */
290 static void R_init_playpal (void) {
291 int i;
292 byte *vgapal = M_lock(F_getresid("PLAYPAL"));
293 for (i = 0; i < 256; i++) {
294 playpal[i] = (rgb) {
295 .r = vgapal[i * 3 + 0] * 255 / 63,
296 .g = vgapal[i * 3 + 1] * 255 / 63,
297 .b = vgapal[i * 3 + 2] * 255 / 63,
298 };
299 bright[i] = ((int)vgapal[i * 3 + 0] + vgapal[i * 3 + 1] + vgapal[i * 3 + 2]) * 8 / (63 * 3);
301 M_unlock(vgapal);
304 static vgaimg *R_getvga (int id) {
305 int loaded = M_was_locked(id);
306 vgaimg *v = M_lock(id);
307 if (v != NULL && !loaded) {
308 v->w = short2host(v->w);
309 v->h = short2host(v->h);
310 v->x = short2host(v->x);
311 v->y = short2host(v->y);
313 return v;
316 static rgba *R_extract_flame_spr (vgaimg *v) {
317 static const byte flametab[16] = {
318 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0xD5,
319 0xD6, 0xD7, 0xA1, 0xA0, 0xE3, 0xE2, 0xE1, 0xE0
320 };
321 int i, j;
322 rgba *s = malloc(v->w * v->h * sizeof(rgba));
323 if (s != NULL) {
324 for (j = 0; j < v->h; j++) {
325 for (i = 0; i < v->w; i++) {
326 int k = j * v->w + i;
327 byte c = v->data[k] + bright[DEFAULT_SKY_COLOR];
328 s[k] = (rgba) {
329 .r = playpal[flametab[c]].r,
330 .g = playpal[flametab[c]].g,
331 .b = playpal[flametab[c]].b,
332 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
333 };
337 return s;
340 static rgba *R_extract_smoke_spr (vgaimg *v) {
341 int i, j;
342 rgba *s = malloc(v->w * v->h * sizeof(rgba));
343 if (s != NULL) {
344 for (j = 0; j < v->h; j++) {
345 for (i = 0; i < v->w; i++) {
346 int k = j * v->w + i;
347 byte c = ((v->data[k] + bright[DEFAULT_SKY_COLOR]) + 0x60) ^ 0x0F;
348 byte a = 0xFF - ((int)playpal[c].r + playpal[c].g + playpal[c].b) / 3;
349 s[k] = (rgba) {
350 .r = playpal[c].r,
351 .g = playpal[c].g,
352 .b = playpal[c].b,
353 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : a,
354 };
358 return s;
361 static rgba *R_extract_mask_spr (vgaimg *v) {
362 int i, j;
363 rgba *s = malloc(v->w * v->h * sizeof(rgba));
364 if (s != NULL) {
365 for (j = 0; j < v->h; j++) {
366 for (i = 0; i < v->w; i++) {
367 int k = j * v->w + i;
368 byte c = v->data[k];
369 if (c >= 0x70 && c <= 0x7F) {
370 byte mask = c - 0x70;
371 mask = 0xFF - ((mask << 4) | mask);
372 s[k] = (rgba) {
373 .r = mask,
374 .g = mask,
375 .b = mask,
376 .a = 0xFF,
377 };
378 } else {
379 s[k] = (rgba) {
380 .r = 0,
381 .g = 0,
382 .b = 0,
383 .a = 0,
384 };
389 return s;
392 static rgba *R_extract_rgba_spr (vgaimg *v) {
393 int i, j;
394 rgba *s = malloc(v->w * v->h * sizeof(rgba));
395 if (s != NULL) {
396 for (j = 0; j < v->h; j++) {
397 for (i = 0; i < v->w; i++) {
398 int k = j * v->w + i;
399 byte c = v->data[k];
400 s[k] = (rgba) {
401 .r = playpal[c].r,
402 .g = playpal[c].g,
403 .b = playpal[c].b,
404 .a = c == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
405 };
409 return s;
412 /* OpenGL helpers */
414 static image R_gl_create_image (const rgba *buf, int w, int h) {
415 node *n = R_cache_alloc(root, w, h);
416 if (n) {
417 R_cache_update(n, buf, w, h);
419 return (image) {
420 .n = n,
421 .w = w,
422 .h = h,
423 .res = -1
424 };
427 static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
428 image img = (image) { .res = -1 };
429 vgaimg *v = R_getvga(id);
430 if (v != NULL) {
431 rgba *buf = (*fn)(v);
432 img = R_gl_create_image(buf, v->w, v->h);
433 img.x = v->x;
434 img.y = v->y;
435 img.res = id;
436 M_unlock(v);
437 free(buf);
439 return img;
442 static image R_gl_getimage (int id) {
443 return R_gl_get_special_image(id, &R_extract_rgba_spr);
446 static image R_gl_loadimage (const char name[8]) {
447 return R_gl_getimage(F_getresid(name));
450 static image R_gl_get_special_spr (const char n[4], int s, int d, rgba *(*fn)(vgaimg*)) {
451 return R_gl_get_special_image(F_getsprid(n, s, d), fn);
454 static void R_gl_free_image (image *img) {
455 if (img->n != NULL && img->res >= 0) {
456 // TODO delete node
458 img->n = NULL;
461 static void R_gl_draw_quad (int x, int y, int w, int h) {
462 glBegin(GL_QUADS);
463 glVertex2i(x + w, y);
464 glVertex2i(x, y);
465 glVertex2i(x, y + h);
466 glVertex2i(x + w, y + h);
467 glEnd();
470 static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip) {
471 if (img->n) {
472 GLfloat nw = img->n->base->root.r + 1;
473 GLfloat nh = img->n->base->root.b + 1;
474 GLfloat ax = (flip ? img->n->l : img->n->r + 1) / nw;
475 GLfloat bx = (flip ? img->n->r + 1 : img->n->l) / nh;
476 GLfloat ay = (img->n->t) / nw;
477 GLfloat by = (img->n->b + 1) / nh;
478 glBindTexture(GL_TEXTURE_2D, img->n->base->id);
479 glEnable(GL_TEXTURE_2D);
480 glBegin(GL_QUADS);
481 glTexCoord2f(ax, ay); glVertex2i(x + w, y);
482 glTexCoord2f(bx, ay); glVertex2i(x, y);
483 glTexCoord2f(bx, by); glVertex2i(x, y + h);
484 glTexCoord2f(ax, by); glVertex2i(x + w, y + h);
485 glEnd();
486 glDisable(GL_TEXTURE_2D);
487 glBindTexture(GL_TEXTURE_2D, 0);
488 } else {
489 glColor3ub(255, 0, 0);
490 glDisable(GL_BLEND);
491 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
492 R_gl_draw_quad(x, y, w, h);
496 /* fit image into rectangle without applying offset and transparency */
497 static void R_gl_draw_image_ext (image *img, int x, int y, int w, int h) {
498 glDisable(GL_BLEND);
499 glColor3ub(255, 255, 255);
500 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
501 R_gl_draw_textured(img, x, y, w, h, 0);
504 /* draw sprite with offset and coloring */
505 static void R_gl_draw_image_color (image *img, int x, int y, int flip) {
506 int xx = flip ? x - img->w + img->x : x - img->x;
507 glEnable(GL_BLEND);
508 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
509 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
510 glDisable(GL_BLEND);
513 /* draw sprite with offset */
514 static void R_gl_draw_image (image *img, int x, int y, int flip) {
515 int xx = flip ? x - img->w + img->x : x - img->x;
516 glEnable(GL_BLEND);
517 glColor3ub(255, 255, 255);
518 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
519 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
522 static void R_gl_set_color (byte c) {
523 glColor3ub(playpal[c].r, playpal[c].g, playpal[c].b);
526 static void R_gl_setclip (int x, int y, int w, int h) {
527 glScissor(x, SCRH - h - y, w, h);
530 static void R_gl_setmatrix (void) {
531 glScissor(0, 0, SCRW, SCRH);
532 glViewport(0, 0, SCRW, SCRH);
533 glMatrixMode(GL_PROJECTION);
534 glLoadIdentity();
535 glOrtho(0, SCRW, SCRH, 0, 0, 1);
536 glMatrixMode(GL_MODELVIEW);
537 glLoadIdentity();
540 /* --- Misc --- */
542 static image Z_getspr (const char n[4], int s, int d, char *dir) {
543 int h = F_getsprid(n, s, d);
544 if (dir != NULL) {
545 *dir = (h & 0x8000) ? 1 : 0;
547 return R_gl_getimage(h);
550 static void Z_putch_generic (image img[], int off, int ch) {
551 image *p = NULL;
552 if (ch > 32 && ch < 160) {
553 p = &img[ch - '!'];
555 if (p != NULL) {
556 R_gl_draw_image(p, prx, pry, 0);
557 prx += p->w - 1;
558 } else {
559 prx += off;
563 static void Z_printf_generic (image img[], int off, const char *fmt, va_list ap) {
564 int i;
565 char buf[80];
566 vsprintf(buf, fmt, ap);
567 for (i = 0; buf[i]; ++i) {
568 switch (buf[i]) {
569 case '\n':
570 pry += off + 1;
571 case '\r':
572 prx = 0;
573 break;
574 default:
575 Z_putch_generic(img, off, (byte)buf[i]);
580 static void Z_gotoxy (int x, int y) {
581 prx = x;
582 pry = y;
585 static void Z_printbf (const char *fmt, ...) {
586 va_list a;
587 va_start(a, fmt);
588 Z_printf_generic(bfh, 12, fmt, a);
589 va_end(a);
592 static void Z_printsf (const char *fmt, ...) {
593 va_list a;
594 va_start(a, fmt);
595 Z_printf_generic(sfh, 7, fmt, a);
596 va_end(a);
599 static void Z_printhf (const char *fmt, ...) {
600 int i, c;
601 char buf[80];
602 va_list a;
603 va_start(a, fmt);
604 vsprintf(buf, fmt, a);
605 va_end(a);
606 for (i = 0; buf[i]; ++i) {
607 switch (buf[i]) {
608 case '0':
609 case '1':
610 case '2':
611 case '3':
612 case '4':
613 case '5':
614 case '6':
615 case '7':
616 case '8':
617 case '9':
618 c = buf[i] - '0';
619 break;
620 case '-':
621 c = 10;
622 break;
623 case '%':
624 c = 11;
625 break;
626 case '\n':
627 pry += 19;
628 case '\r':
629 c = -1;
630 prx = 0;
631 break;
632 default:
633 c = -1;
634 break;
636 if (c >= 0) {
637 R_gl_draw_image(&sth[c], prx, pry, 0);
639 prx += 14;
643 /* --- Menu --- */
645 static image *PL_getspr (int s, int d, int msk) {
646 int i = (s - 'A') * 2 + d;
647 return msk ? &plr_msk[i] : &plr_spr[i];
650 static void GM_draw (void) {
651 enum {MENU, MSG}; // copypasted from menu.c!
652 enum {
653 CANCEL, NEWGAME, LOADGAME, SAVEGAME, OPTIONS, QUITGAME, QUIT, ENDGAME, ENDGM,
654 PLR1, PLR2, COOP, DM, VOLUME, GAMMA, LOAD, SAVE, PLCOLOR, PLCEND, MUSIC, INTERP,
655 SVOLM, SVOLP, MVOLM, MVOLP, GAMMAM, GAMMAP, PL1CM, PL1CP, PL2CM, PL2CP
656 }; // copypasted from menu.c!
657 int i, j, k, x, y, cx, cy;
658 image *img;
659 gm_tm += 1;
660 if (mnu != NULL) {
661 cx = SCRW / 2;
662 cy = SCRH / 2;
663 if (mnu->type == MENU) {
664 y = cy - (mnu->n * 16 - 20) / 2;
665 Z_gotoxy(cx - mnu->x, y - 10); Z_printbf("%s", mnu->ttl);
666 for (i = 0; i < mnu->n; i++) {
667 if (mnu->t[i] == LOAD || mnu->t[i] == SAVE) {
668 j = y + i * 16 + 29;
669 R_gl_draw_image(&mslotl, cx - mnu->x, j, 0);
670 for (k = 8; k < 184; k += 8) {
671 R_gl_draw_image(&mslotm, cx - mnu->x + k, j, 0);
673 R_gl_draw_image(&mslotr, cx - mnu->x + 184, j, 0);
674 Z_gotoxy(cx - mnu->x + 4, j - 8);
675 if (input && i == save_mnu.cur) {
676 Z_printsf("%s_", ibuf);
677 } else {
678 Z_printsf("%s", savname[i]);
680 } else {
681 x = mnu->t[i] >= SVOLM ? (mnu->t[i] >= PL1CM ? 50 : 152) : 0;
682 Z_gotoxy(cx - mnu->x + x, y + i * 16 + 20);
683 Z_printbf("%s", mnu->m[i]);
684 switch (mnu->t[i]) {
685 case MUSIC:
686 Z_printbf(" '%s'", g_music);
687 break;
688 case INTERP:
689 Z_printbf("%s", fullscreen ? "ON" : "OFF");
690 break;
691 case PL1CM:
692 case PL1CP:
693 case PL2CM:
694 case PL2CP:
695 img = PL_getspr(*panimp, 0, 0);
696 R_gl_draw_image(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
697 img = PL_getspr(*panimp, 0, 1);
698 R_gl_set_color(pcolortab[(mnu->t[i] == PL1CM) ? p1color : p2color] + PLAYER_COLOR_OFFSET);
699 R_gl_draw_image_color(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
700 break;
701 case SVOLM:
702 case SVOLP:
703 case MVOLM:
704 case MVOLP:
705 case GAMMAM:
706 case GAMMAP:
707 j = y + i * 16 + 20;
708 R_gl_draw_image(&mbarl, cx - mnu->x, j, 0);
709 for (k = 8; k < 144; k += 8) {
710 R_gl_draw_image(&mbarm, cx - mnu->x + k, j, 0);
712 R_gl_draw_image(&mbarr, cx - mnu->x + 144, j, 0);
713 switch (mnu->t[i]) {
714 case SVOLM:
715 k = snd_vol;
716 break;
717 case MVOLM:
718 k = mus_vol;
719 break;
720 case GAMMAM:
721 k = gamma << 5;
722 break;
723 default:
724 k = 0;
725 break;
727 R_gl_draw_image(&mbaro, cx - mnu->x + 8 + k, j, 0);
728 break;
732 R_gl_draw_image(&msklh[(gm_tm / 6) & 1], cx - mnu->x - 25, y + mnu->cur * 16 + 20 - 8, 0);
733 } else if (mnu->type == MSG) {
734 Z_gotoxy(cx - strlen(mnu->ttl) * 7 / 2, cy - 10); Z_printsf(mnu->ttl);
735 Z_gotoxy(cx - 24, SCRH / 2); Z_printsf("(Y/N)");
736 } else {
737 ERR_fatal("Unknown menu type %i\n", mnu->type);
742 /* --- View --- */
744 static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int fg) {
745 int i, j;
746 assert(minx >= 0 && minx <= FLDW);
747 assert(miny >= 0 && miny <= FLDH);
748 assert(maxx >= 0 && maxx <= FLDW);
749 assert(maxy >= 0 && maxy <= FLDH);
750 for (j = miny; j < maxy; j++) {
751 for (i = minx; i < maxx; i++) {
752 byte id = fld[j * FLDW + i];
753 if (id != 0) {
754 if (walp[id].res < 0) {
755 if (fg) {
756 switch (R_get_special_id(id)) {
757 case 1:
758 glColor4ub(0, 0, 255, 127);
759 break;
760 case 2:
761 glColor4ub(0, 127, 0, 127);
762 break;
763 case 3:
764 glColor4ub(127, 0, 0, 127);
765 break;
766 default:
767 glColor4ub(0, 0, 0, 127);
768 break;
770 glEnable(GL_BLEND);
771 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
772 R_gl_draw_quad(i * CELW, j * CELW, CELW, CELH);
774 } else {
775 R_gl_draw_image(&walp[id], i * CELW, j * CELH, 0);
782 static void R_draw_dots (void) {
783 int i;
784 glBegin(GL_POINTS);
785 for (i = 0; i < MAXDOT; i++) {
786 if (dot[i].t != 0) {
787 R_gl_set_color(dot[i].c);
788 glVertex2i(dot[i].o.x, dot[i].o.y + 1);
791 glEnd();
794 static void R_draw_items (void) {
795 int i, s;
796 for (i = 0; i < MAXITEM; ++i) {
797 s = -1;
798 if (it[i].t && it[i].s >= 0) {
799 switch (it[i].t & 0x7FFF) {
800 case I_ARM1:
801 s = it[i].s / 9 + 18;
802 break;
803 case I_ARM2:
804 s = it[i].s / 9 + 20;
805 break;
806 case I_MEGA:
807 s = it[i].s / 2 + 22;
808 break;
809 case I_INVL:
810 s = it[i].s / 2 + 26;
811 break;
812 case I_SUPER:
813 case I_RTORCH:
814 case I_GTORCH:
815 case I_BTORCH:
816 s = it[i].s / 2 + (it[i].t - I_SUPER) * 4 + 35;
817 break;
818 case I_GOR1: case I_FCAN:
819 s = it[i].s / 2 + (it[i].t - I_GOR1) * 3 + 51;
820 break;
821 case I_AQUA:
822 s = 30;
823 break;
824 case I_SUIT:
825 s = 34;
826 break;
827 case I_KEYR:
828 case I_KEYG:
829 case I_KEYB:
830 s = (it[i].t & 0x7FFF) - I_KEYR + 31;
831 break;
832 case I_GUN2:
833 s = 57;
834 break;
835 default:
836 s = (it[i].t & 0x7FFF) - 1;
839 if (s >= 0) {
840 R_gl_draw_image(&item_spr[s], it[i].o.x, it[i].o.y, item_sprd[s]);
845 static int standspr (player_t *p) {
846 if (p->f & PLF_UP) {
847 return 'X';
848 } else if (p->f & PLF_DOWN) {
849 return 'Z';
850 } else {
851 return 'E';
855 static int wpnspr (player_t *p) {
856 if (p->f & PLF_UP) {
857 return 'C';
858 } else if(p->f & PLF_DOWN) {
859 return 'E';
860 } else {
861 return 'A';
865 static void R_draw_player (player_t *p) {
866 enum {STAND, GO, DIE, SLOP, DEAD, MESS, OUT, FALL}; // copypasted from player.c!
867 static const int wytab[] = {-1, -2, -1, 0};
868 int s = 'A';
869 int w = 0;
870 int wx = 0;
871 int wy = 0;
872 switch (p->st) {
873 case STAND:
874 if (p->f & PLF_FIRE) {
875 s = standspr(p) + 1;
876 w = wpnspr(p) + 1;
877 } else if (p->pain) {
878 s = 'G';
879 w = 'A';
880 wx = p->d ? 2 : -2;
881 wy = 1;
882 } else {
883 s = standspr(p);
884 w = wpnspr(p);
886 break;
887 case DEAD:
888 s = 'N';
889 break;
890 case MESS:
891 s = 'W';
892 break;
893 case GO:
894 if (p->pain) {
895 s = 'G';
896 w = 'A';
897 wx = p->d ? 2 : -2;
898 wy = 1;
899 } else {
900 s = plr_goanim[p->s / 8];
901 w = (p->f & PLF_FIRE) ? 'B' : 'A';
902 wx = p->d ? 2 : -2;
903 wy = 1 + wytab[s - 'A'];
905 break;
906 case DIE:
907 s = plr_dieanim[p->s];
908 break;
909 case SLOP:
910 s = plr_slopanim[p->s];
911 break;
912 case OUT:
913 s = 0;
914 break;
916 if (p->wpn == 0) {
917 w = 0;
919 if (w) {
920 R_gl_draw_image(&plr_wpn[(int)p->wpn][w -'A'], p->o.x + wx, p->o.y + wy, p->d);
922 if (s) {
923 R_gl_draw_image(&plr_spr[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
924 R_gl_set_color(p->color + PLAYER_COLOR_OFFSET);
925 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]);
929 static void R_draw_monsters (void) {
930 enum {SLEEP, GO, RUN, CLIMB, DIE, DEAD, ATTACK, SHOOT, PAIN, WAIT, REVIVE, RUNOUT}; // copypasted from monster.c!
931 int i;
932 for (i = 0; i < MAXMN; i++) {
933 if (mn[i].t != MN_NONE) {
934 int x = mn[i].o.x;
935 int y = mn[i].o.y;
936 if (mn[i].t < MN__LAST) {
937 if ((mn[i].t != MN_SOUL && mn[i].t != MN_PAIN) || mn[i].st != DEAD) {
938 int ap = mn[i].ap[mn[i].ac];
939 int d = (ap - 'A') * 2 + mn[i].d;
940 int dir = mn_sprd[mn[i].t - 1][d];
941 if (mn[i].t == MN_MAN && (ap == 'E' || ap == 'F')) {
942 R_gl_draw_image(&mn_sgun[ap - 'E'], x, y, mn[i].d);
944 R_gl_draw_image(&mn_spr[mn[i].t - 1][d], x, y, dir);
945 if (mn[i].t == MN_MAN) {
946 R_gl_set_color(MANCOLOR + PLAYER_COLOR_OFFSET);
947 R_gl_draw_image_color(&mn_man_msk[d], x, y, dir);
950 if (mn[i].t == MN_VILE && mn[i].st == SHOOT) {
951 R_gl_draw_image(&mn_fspr[mn[i].ac / 3], mn[i].tx, mn[i].ty, 0);
953 } else if (mn[i].t == MN_PL_DEAD || mn[i].t == MN_PL_MESS) {
954 int type = mn[i].t - MN_PL_DEAD;
955 R_gl_draw_image(&pl_spr[type], x, y, 0);
956 R_gl_set_color(mn[i].d);
957 R_gl_draw_image_color(&pl_msk[type], x, y, 0);
963 static void R_draw_weapons (void) {
964 enum {NONE, ROCKET, PLASMA, APLASMA, BALL1, BALL2, BALL7, BFGBALL, BFGHIT, MANF, REVF, FIRE}; // copypasted from weapons.c!
965 int i, s, d, x, y;
966 for (i = 0; i < MAXWPN; ++i) {
967 s = -1;
968 d = 0;
969 switch (wp[i].t) {
970 case REVF:
971 case ROCKET:
972 d = wp[i].s;
973 if (d < 2) {
974 d = wp[i].o.xv > 0 ? 1 : 0;
975 x = abs(wp[i].o.xv);
976 y = wp[i].o.yv;
977 s = 0;
978 if (y < 0) {
979 if (-y >= x) {
980 s = 30;
982 } else if (y > 0) {
983 if (y >= x / 2) {
984 s = 31;
987 } else {
988 s = (d - 2) / 2 + 1;
989 d = 0;
991 break;
992 case MANF:
993 s=wp[i].s;
994 if (s >= 2) {
995 s /= 2;
996 break;
998 case PLASMA:
999 case APLASMA:
1000 case BALL1:
1001 case BALL7:
1002 case BALL2:
1003 s = wp[i].s;
1004 if (s >= 2) {
1005 s = s / 2 + 1;
1007 switch (wp[i].t) {
1008 case PLASMA:
1009 s += 4;
1010 break;
1011 case APLASMA:
1012 s += 11;
1013 break;
1014 case BALL1:
1015 s += 32;
1016 break;
1017 case BALL2:
1018 s += 42;
1019 break;
1020 case BALL7:
1021 s += 37;
1022 d = wp[i].o.xv >= 0 ? 1 : 0;
1023 break;
1024 case MANF:
1025 s += 47;
1026 d= wp[i].o.xv>=0 ? 1 : 0;
1027 break;
1029 break;
1030 case BFGBALL:
1031 s = wp[i].s;
1032 if (s >= 2) {
1033 s = s / 2 + 1;
1035 s += 18;
1036 break;
1037 case BFGHIT:
1038 s = wp[i].s / 2 + 26;
1039 break;
1041 if (s >= 0) {
1042 R_gl_draw_image(&wp_spr[s * 2 + d], wp[i].o.x, wp[i].o.y, wp_sprd[s * 2 + d]);
1047 static void R_draw_smoke (void) {
1048 int i, s;
1049 for (i = 0; i < MAXSMOK; ++i) {
1050 if (sm[i].t) {
1051 switch (sm[i].s) {
1052 case 0:
1053 s = sm[i].t;
1054 if (s >= (SMSN - 1) * 3) {
1055 s = 0;
1056 } else {
1057 s = SMSN - 1 - s / 3;
1059 R_gl_draw_image(&smk_spr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1060 break;
1061 case 1:
1062 s = sm[i].t;
1063 if (s >= FLSN - 1) {
1064 s = 0;
1065 } else {
1066 s = FLSN - 1 - s;
1068 R_gl_draw_image(&smk_fspr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1069 break;
1075 static void R_draw_effects (void) {
1076 enum {NONE, TFOG, IFOG, BUBL}; // copypasted from fx.c
1077 int i, s;
1078 for (i = 0; i < MAXFX; ++i) {
1079 switch (fx[i].t) {
1080 case TFOG:
1081 s = fx[i].s / 2;
1082 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1083 break;
1084 case IFOG:
1085 s = fx[i].s / 2 + 10;
1086 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1087 break;
1088 case BUBL:
1089 glBegin(GL_POINTS);
1090 R_gl_set_color(0xC0 + fx[i].s);
1091 glVertex2i(fx[i].x >> 8, (fx[i].y >> 8) + 1);
1092 glEnd();
1093 break;
1098 static int get_pu_st (int t) {
1099 if (t >= PL_FLASH) {
1100 return 1;
1101 } else if((t / 9) & 1) {
1102 return 0;
1103 } else {
1104 return 1;
1108 static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
1109 glPushMatrix();
1110 R_gl_setclip(x, y, w, h);
1111 glTranslatef(x, y, 0);
1112 if (w_horiz && horiz.n != NULL) {
1113 R_gl_draw_image_ext(&horiz, 0, 0, w, h);
1114 if (sky_type == 2 && lt_time < 0) {
1115 image *tanderbolt = &ltn[lt_type][lt_time < -5 ? 0 : 1];
1116 if (!lt_side) {
1117 R_gl_draw_image(tanderbolt, 0, lt_ypos, 0);
1118 } else {
1119 R_gl_draw_image(tanderbolt, w - 1, lt_ypos, 1);
1122 } else {
1123 glDisable(GL_BLEND);
1124 R_gl_set_color(DEFAULT_SKY_COLOR);
1125 R_gl_draw_quad(0, 0, w, h);
1127 int maxx = min((camx + w) / CELW + 1, FLDW);
1128 int maxy = min((camy + h) / CELH + 1, FLDH);
1129 int minx = max((camx - max_wall_width) / CELW, 0);
1130 int miny = max((camy - max_wall_height) / CELH, 0);
1131 glTranslatef(-camx, -camy, 0);
1132 R_draw_fld((byte*)fldb, minx, miny, maxx, maxy, 0);
1133 R_draw_dots();
1134 R_draw_items();
1135 R_draw_player(&pl1);
1136 if (_2pl) {
1137 R_draw_player(&pl2);
1139 R_draw_monsters();
1140 R_draw_weapons();
1141 R_draw_smoke();
1142 R_draw_effects();
1143 R_draw_fld((byte*)fldf, minx, miny, maxx, maxy, 1);
1144 glTranslatef(camx, camy, 0);
1145 if (sky_type == 2 && (lt_time == -4 || lt_time == -2)) {
1146 glColor4ub(255, 255, 255, 255);
1147 glEnable(GL_BLEND);
1148 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1149 R_gl_draw_quad(0, 0, w, h);
1151 glPopMatrix();
1154 static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
1155 p->looky = min(max(p->looky, -SCRH / 4), SCRH / 4); // TODO remove writeback
1156 int st = stone.w;
1157 int cw = w - st;
1158 int cx = min(max(p->o.x, cw / 2), FLDW * CELW - cw / 2);
1159 int cy = min(max(p->o.y - 12 + p->looky, h / 2), FLDH * CELH - h / 2);
1160 int camx = max(cx - cw / 2, 0);
1161 int camy = max(cy - h / 2, 0);
1162 glPushMatrix();
1163 R_draw_view(x, y + 1, cw, h - 2, camx, camy);
1164 glTranslatef(x, y, 0);
1165 if (p->invl) {
1166 if (get_pu_st(p->invl)) {
1167 glEnable(GL_BLEND);
1168 glColor4ub(191, 191, 191, 255);
1169 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
1170 R_gl_draw_quad(0, 0, cw, h);
1172 } else {
1173 if (p->suit && get_pu_st(p->suit)) {
1174 glEnable(GL_BLEND);
1175 glColor4ub(0, 255, 0, 192);
1176 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1177 R_gl_draw_quad(0, 0, cw, h);
1179 int f = min(max(p->pain * 3, 0), 255);
1180 if (f > 0) {
1181 glEnable(GL_BLEND);
1182 glColor4ub(255, 0, 0, f);
1183 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1184 R_gl_draw_quad(0, 0, cw, h);
1187 R_gl_setclip(x, y, w, h);
1188 glTranslatef(-x + cw, 0, 0);
1189 R_gl_draw_image(&stone, 0, 0, 0);
1190 int i = stone.h;
1191 while (i < h) {
1192 R_gl_draw_image(&stone2, 0, i, 0);
1193 i += stone2.h;
1195 if (p->drawst & PL_DRAWAIR) {
1196 if (p->air < PL_AIR) {
1197 int a = min(max(p->air, 0), MAXAIR) * 100 / MAXAIR;
1198 glDisable(GL_BLEND);
1199 R_gl_set_color(0xC8);
1200 R_gl_draw_quad(10, 49, a, 2);
1203 if (p->drawst & PL_DRAWLIFE) {
1204 Z_gotoxy(10, 7);
1205 Z_printhf("%3d%%", p->life);
1207 if (p->drawst & PL_DRAWARMOR) {
1208 Z_gotoxy(10, 7 + 19);
1209 Z_printhf("%3d%%", p->armor);
1211 if (p->drawst & PL_DRAWWPN) {
1212 switch(p->wpn) {
1213 case 2:
1214 case 5:
1215 i = p->ammo;
1216 break;
1217 case 3:
1218 case 4:
1219 case 9:
1220 i = p->shel;
1221 break;
1222 case 6:
1223 i = p->rock;
1224 break;
1225 case 7:
1226 case 8:
1227 i = p->cell;
1228 break;
1229 case 10:
1230 i = p->fuel;
1231 break;
1232 default:
1233 i = -1;
1234 break;
1236 // weapon
1237 if (p->wpn >= 0) {
1238 R_gl_draw_image(&sth[12 + p->wpn], st - 88, 58 + 19, 0);
1240 // ammo
1241 if (p->wpn >= 2) {
1242 Z_gotoxy(st - 10 - 5 * 14, 58 + 2);
1243 Z_printhf("%5d", i);
1246 if (p->drawst & PL_DRAWFRAG && g_dm) {
1247 Z_gotoxy(st - 5 - 5 * 14, 77 + 5);
1248 Z_printhf("%5d", p->frag);
1250 if (p->drawst & PL_DRAWKEYS) {
1251 int x, k, n;
1252 for (k = p->keys >> 4, n = 0, x = st - 75; n < 3; n++, k >>= 1, x += 9) {
1253 if (k & 1) {
1254 R_gl_draw_image(&keys[n], x, 91, 0);
1258 if (p->drawst & PL_DRAWLIVES && !_2pl) {
1259 Z_gotoxy(st - 35, 17);
1260 Z_printhf("%d", p->lives);
1262 glPopMatrix();
1265 /* --- Game --- */
1267 static void pl_info (player_t *p, int x, int y) {
1268 dword t = p->kills * 10920 / g_time;
1269 Z_gotoxy(x + 25, y); Z_printbf("KILLS");
1270 Z_gotoxy(x + 25, y + 15); Z_printbf("KPM");
1271 Z_gotoxy(x + 25, y + 30); Z_printbf("SECRETS %u / %u", p->secrets, sw_secrets);
1272 Z_gotoxy(x + 255, y); Z_printbf("%u", p->kills);
1273 Z_gotoxy(x + 255, y + 15); Z_printbf("%u.%u", t / 10, t % 10);
1276 static void R_draw_intermission (void) {
1277 int cx = SCRW / 2;
1278 word hr, mn, sc, h;
1279 Z_gotoxy(cx - 14*12/2, 20);
1280 Z_printbf("LEVEL COMPLETE");
1281 Z_calc_time(g_time, &hr, &mn, &sc);
1282 Z_gotoxy(cx - 12*12/2, 40);
1283 Z_printbf("TIME %u:%02u:%02u", hr, mn, sc);
1284 h = 40 + SCRH / 10;
1285 if (_2pl) {
1286 Z_gotoxy(cx - 10*12/2, h);
1287 Z_printbf("PLAYER ONE");
1288 h += 20;
1290 pl_info(&pl1, cx - 160, h);
1291 if (_2pl) {
1292 h += 30 + SCRH / 10;
1293 Z_gotoxy(cx - 10*12/2, h);
1294 Z_printbf("PLAYER TWO");
1295 h += 20;
1296 pl_info(&pl2, cx - 160, h);
1300 static void W_act (void) {
1301 int i, a;
1302 if (g_time % 3 == 0) {
1303 for (i = 1; i < max_textures; i++) {
1304 a = walani[i];
1305 if (a != 0) {
1306 anic[a]++;
1307 if (anip[a][anic[a]].res == -1) {
1308 anic[a] = 0;
1310 walp[i] = anip[a][anic[a]];
1316 void R_draw (void) {
1317 W_act();
1318 glClearColor(0, 0, 0, 1);
1319 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1320 glEnable(GL_SCISSOR_TEST);
1321 R_gl_setmatrix();
1322 switch (g_st) {
1323 case GS_ENDANIM:
1324 case GS_END2ANIM:
1325 case GS_DARKEN:
1326 case GS_BVIDEO:
1327 case GS_EVIDEO:
1328 case GS_END3ANIM:
1329 break;
1330 case GS_TITLE:
1331 R_gl_draw_image_ext(&scrnh[0], 0, 0, SCRW, SCRH);
1332 break;
1333 case GS_INTER:
1334 R_gl_draw_image_ext(&scrnh[1], 0, 0, SCRW, SCRH);
1335 R_draw_intermission();
1336 break;
1337 case GS_ENDSCR:
1338 R_gl_draw_image_ext(&scrnh[2], 0, 0, SCRW, SCRH);
1339 break;
1340 case GS_GAME:
1341 if (_2pl) {
1342 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH / 2);
1343 R_draw_player_view(&pl2, 0, SCRH / 2, SCRW, SCRH / 2);
1344 } else {
1345 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH);
1347 R_gl_setclip(0, 0, SCRW, SCRH);
1348 break;
1350 GM_draw();
1351 SDL_GL_SwapBuffers();
1354 void R_alloc (void) {
1355 char s[10];
1356 int i, j, n;
1357 logo("R_alloc: load graphics\n");
1358 /* Game */
1359 scrnh[0] = R_gl_loadimage("TITLEPIC");
1360 assert(scrnh[0].n);
1361 scrnh[1] = R_gl_loadimage("INTERPIC");
1362 scrnh[2] = R_gl_loadimage("ENDPIC");
1363 for (i = 0; i < 2; i++) {
1364 sprintf(s, "LTN%c", '1' + i);
1365 for (j = 0; j < 2; j++) {
1366 ltn[i][j] = Z_getspr(s, j, 0, NULL);
1369 /* Smoke */
1370 for (i = 0; i < SMSN; i++) {
1371 smk_spr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_smoke_spr);
1373 for (i = 0; i < FLSN; i++) {
1374 smk_fspr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_flame_spr);
1376 /* Effects */
1377 for (i = 0; i < 10; i++) {
1378 fx_spr[i] = Z_getspr("TFOG", i, 0, fx_sprd + i);
1380 for (; i < 15; i++) {
1381 fx_spr[i] = Z_getspr("IFOG", i - 10, 0, fx_sprd + i);
1383 /* Weapons */
1384 for (i = 0; i < 4; i++) {
1385 wp_spr[i * 2] = Z_getspr("MISL", i, 1, wp_sprd + i * 2);
1386 wp_spr[i * 2 + 1] = Z_getspr("MISL", i, 2, wp_sprd + i * 2 + 1);
1388 for (; i < 6; i++) {
1389 wp_spr[i * 2] = Z_getspr("PLSS", i - 4, 1, wp_sprd + i * 2);
1390 wp_spr[i * 2 + 1] = Z_getspr("PLSS", i - 4, 2, wp_sprd + i * 2 + 1);
1392 for (; i < 11; i++) {
1393 wp_spr[i * 2] = Z_getspr("PLSE", i - 6, 1, wp_sprd + i * 2);
1394 wp_spr[i * 2 + 1] = Z_getspr("PLSE", i - 6, 2, wp_sprd + i * 2 + 1);
1396 for (; i < 13; i++) {
1397 wp_spr[i * 2] = Z_getspr("APLS", i - 11, 1, wp_sprd + i * 2);
1398 wp_spr[i * 2 + 1] = Z_getspr("APLS", i - 11, 2, wp_sprd + i * 2 + 1);
1400 for (; i < 18; i++) {
1401 wp_spr[i * 2] = Z_getspr("APBX", i - 13, 1, wp_sprd + i * 2);
1402 wp_spr[i * 2 + 1] = Z_getspr("APBX", i - 13, 2, wp_sprd + i * 2 + 1);
1404 for(; i < 20; i++) {
1405 wp_spr[i * 2] = Z_getspr("BFS1", i - 18, 1, wp_sprd + i * 2);
1406 wp_spr[i * 2 + 1] = Z_getspr("BFS1", i - 18, 2, wp_sprd + i * 2 + 1);
1408 for (; i < 26; i++) {
1409 wp_spr[i * 2] = Z_getspr("BFE1", i - 20, 1, wp_sprd + i * 2);
1410 wp_spr[i * 2 + 1] = Z_getspr("BFE1", i - 20, 2, wp_sprd + i * 2 + 1);
1412 for (; i < 30; i++) {
1413 wp_spr[i * 2] = Z_getspr("BFE2", i - 26, 1, wp_sprd + i * 2);
1414 wp_spr[i * 2 + 1] = Z_getspr("BFE2", i - 26, 2, wp_sprd + i * 2 + 1);
1416 for (; i < 32; i++) {
1417 wp_spr[i * 2] = Z_getspr("MISL", i - 30 + 4, 1, wp_sprd + i * 2);
1418 wp_spr[i * 2 + 1] = Z_getspr("MISL", i - 30 + 4, 2, wp_sprd + i * 2 + 1);
1420 for (; i < 37; i++) {
1421 wp_spr[i * 2] = Z_getspr("BAL1", i - 32, 1, wp_sprd + i * 2);
1422 wp_spr[i * 2 + 1] = Z_getspr("BAL1", i - 32, 2, wp_sprd + i * 2 + 1);
1424 for (; i < 42; i++) {
1425 wp_spr[i * 2] = Z_getspr("BAL7", i - 37, 1, wp_sprd + i * 2);
1426 wp_spr[i * 2 + 1] = Z_getspr("BAL7", i - 37, 2, wp_sprd + i * 2 + 1);
1428 for (; i < 47; i++) {
1429 wp_spr[i * 2] = Z_getspr("BAL2", i - 42, 1, wp_sprd + i * 2);
1430 wp_spr[i * 2 + 1] = Z_getspr("BAL2", i - 42, 2, wp_sprd + i * 2 + 1);
1432 for (; i < 49; i++) {
1433 wp_spr[i * 2] = Z_getspr("MANF", i - 47, 1, wp_sprd + i * 2);
1434 wp_spr[i * 2 + 1] = Z_getspr("MANF", i - 47, 2, wp_sprd + i * 2 + 1);
1436 /* Items */
1437 static const char snm[18][4] = {
1438 "CLIP", "SHEL", "ROCK", "CELL", "AMMO", "SBOX", "BROK", "CELP",
1439 "STIM", "MEDI", "BPAK",
1440 "CSAW", "SHOT", "SGN2", "MGUN", "LAUN", "PLAS", "BFUG"
1441 };
1442 static const char n4[4][4] = {
1443 "SOUL", "SMRT", "SMGT", "SMBT"
1444 };
1445 static const char n3[2][4] = {
1446 "GOR1", "FCAN"
1447 };
1448 for (i = 0; i < 18; i++) {
1449 item_spr[i] = Z_getspr(snm[i], 0, 0, item_sprd + i);
1451 for (; i < 20; i++) {
1452 item_spr[i] = Z_getspr("ARM1", i - 18, 0, item_sprd + i);
1453 item_spr[i + 2] = Z_getspr("ARM2", i - 18, 0, item_sprd + i);
1455 i+=2;
1456 for (; i < 26; i++) {
1457 item_spr[i] = Z_getspr("MEGA", i - 22, 0, item_sprd + i);
1459 for (; i < 30; i++) {
1460 item_spr[i] = Z_getspr("PINV", i - 26, 0, item_sprd + i);
1462 item_spr[30] = Z_getspr("AQUA", 0, 0, item_sprd + 30);
1463 item_spr[31] = Z_getspr("KEYR", 0, 0, item_sprd + 31);
1464 item_spr[32] = Z_getspr("KEYG", 0, 0, item_sprd + 32);
1465 item_spr[33] = Z_getspr("KEYB", 0, 0, item_sprd + 33);
1466 item_spr[34] = Z_getspr("SUIT", 0, 0, item_sprd + 34);
1467 for (n = 35, j = 0; j < 4; j++) {
1468 for (i = 0; i < 4; i++, n++) {
1469 item_spr[n] = Z_getspr(n4[j], i, 0, item_sprd + n);
1472 for (j = 0; j < 2; j++) {
1473 for (i = 0; i < 3; i++, n++) {
1474 item_spr[n] = Z_getspr(n3[j], i, 0, item_sprd + n);
1477 item_spr[57] = Z_getspr("GUN2", 0, 0, item_sprd + 57);
1478 /* Player */
1479 for (i = 0; i < 27; i++) {
1480 plr_spr[i * 2] = Z_getspr("PLAY", i, 1, plr_sprd + i * 2);
1481 plr_msk[i * 2] = R_gl_get_special_spr("PLAY", i, 1, &R_extract_mask_spr);
1482 plr_spr[i * 2 + 1] = Z_getspr("PLAY", i, 2, plr_sprd + i * 2 + 1);
1483 plr_msk[i * 2 + 1] = R_gl_get_special_spr("PLAY", i, 2, &R_extract_mask_spr);
1485 strncpy(s, "PWPx", 4);
1486 for (i = 1; i < 11; i++) {
1487 s[3] = (i < 10 ? '0' : 'A' - 10) + i;
1488 for (j = 0; j < 6; j++) {
1489 plr_wpn[i][j] = Z_getspr(s, j, 1, NULL);
1492 /* Monsters */
1493 static const char msn[MN_TN][4] = {
1494 "SARG", "TROO", "POSS", "SPOS", "CYBR", "CPOS", "BOSS", "BOS2", "HEAD", "SKUL",
1495 "PAIN", "SPID", "BSPI", "FATT", "SKEL", "VILE", "FISH", "BAR1", "ROBO", "PLAY"
1496 };
1497 static const int mms[MN_TN] = {
1498 14*2, 21*2, 21*2, 21*2, 16*2, 20*2, 15*2, 15*2, 12*2, 11*2,
1499 13*2, 19*2, 16*2, 20*2, 17*2, 29*2, 6*2, 2*2, 17*2, 23*2
1500 };
1501 mn_sgun[0] = Z_getspr("PWP4", 0, 1, NULL);
1502 mn_sgun[1] = Z_getspr("PWP4", 1, 1, NULL);
1503 for (j = 0; j < MN_TN; j++) {
1504 for (i = 0; i < mms[j]; i++) {
1505 mn_spr[j][i] = Z_getspr(msn[j], i / 2, (i & 1) + 1, &mn_sprd[j][i]);
1506 if (j == MN_MAN - 1) {
1507 mn_man_msk[i] = R_gl_get_special_spr(msn[j], i / 2, (i & 1) + 1, &R_extract_mask_spr);
1510 if (j == MN_BARREL - 1) {
1511 for (i = 4; i < 14; i++) {
1512 mn_spr[j][i] = Z_getspr("BEXP", i / 2 - 2, (i & 1) + 1, &mn_sprd[j][i]);
1516 for (i = 0; i < 8; i++) {
1517 mn_fspr[i] = Z_getspr("FIRE", i, 0, NULL);
1519 pl_spr[0] = Z_getspr("PLAY", 'N' - 'A', 0, NULL);
1520 pl_msk[0] = R_gl_get_special_spr("PLAY", 'N' - 'A', 0, &R_extract_mask_spr);
1521 pl_spr[1] = Z_getspr("PLAY", 'W' - 'A', 0, NULL);
1522 pl_msk[1] = R_gl_get_special_spr("PLAY", 'W' - 'A', 0, &R_extract_mask_spr);
1523 /* Misc */
1524 static const char mnm[22][8]={
1525 "STTNUM0", "STTNUM1", "STTNUM2", "STTNUM3", "STTNUM4",
1526 "STTNUM5", "STTNUM6", "STTNUM7", "STTNUM8", "STTNUM9",
1527 "STTMINUS", "STTPRCNT",
1528 "FISTA0", "CSAWA0", "PISTA0", "SHOTA0", "SGN2A0", "MGUNA0", "LAUNA0",
1529 "PLASA0", "BFUGA0", "GUN2A0"
1530 };
1531 stone = R_gl_loadimage("STONE");
1532 stone2 = R_gl_loadimage("STONE2");
1533 keys[0] = R_gl_loadimage("KEYRA0");
1534 keys[1] = R_gl_loadimage("KEYGA0");
1535 keys[2] = R_gl_loadimage("KEYBA0");
1536 for (i = 0; i < 22; i++) {
1537 sth[i] = R_gl_loadimage(mnm[i]);
1539 strcpy(s, "STBF_*");
1540 for (i = '!'; i < 160; i++) {
1541 s[5] = i;
1542 bfh[i - '!'] = R_gl_getimage(F_findres(s));
1544 for (i = '!'; i < 160; i++) {
1545 sprintf(s, "STCFN%03d", i);
1546 sfh[i - '!'] = R_gl_getimage(F_findres(s));
1548 strcpy(s, "WINUM*");
1549 for (i = '0'; i <= '9'; i++) {
1550 s[5] = i;
1551 bfh[i - '!'] = R_gl_loadimage(s);
1553 bfh[':' - '!'] = R_gl_loadimage("WICOLON");
1554 // menu
1555 msklh[0] = R_gl_loadimage("M_SKULL1");
1556 msklh[1] = R_gl_loadimage("M_SKULL2");
1557 mbarl = R_gl_loadimage("M_THERML");
1558 mbarm = R_gl_loadimage("M_THERMM");
1559 mbarr = R_gl_loadimage("M_THERMR");
1560 mbaro = R_gl_loadimage("M_THERMO");
1561 mslotl = R_gl_loadimage("M_LSLEFT");
1562 mslotm = R_gl_loadimage("M_LSCNTR");
1563 mslotr = R_gl_loadimage("M_LSRGHT");
1564 // walls
1565 for (i = 1; i < ANIT; i++) {
1566 for (j = 0; anm[i - 1][j]; j++) {
1567 anip[i][j] = R_gl_loadimage(anm[i - 1][j]);
1569 for(; j < 5; j++) {
1570 anip[i][j] = (image) {
1571 .n = NULL,
1572 .w = 8,
1573 .h = 8,
1574 .res = -1,
1575 };
1580 void R_init (void) {
1581 Uint32 flags = SDL_OPENGL;
1582 if (fullscreen) {
1583 flags = flags | SDL_FULLSCREEN;
1585 if (SCRW <= 0 || SCRH <= 0) {
1586 ERR_failinit("Invalid screen size %ix%i\n", SCRW, SCRH);
1588 if (surf == NULL) {
1589 R_init_playpal(); // only onece
1591 surf = SDL_SetVideoMode(SCRW, SCRH, 0, flags);
1592 if (surf == NULL) {
1593 ERR_failinit("Unable to set video mode: %s\n", SDL_GetError());
1595 root = R_cache_new();
1596 assert(root);
1597 R_alloc();
1600 void R_done (void) {
1601 // do nothing
1604 void R_setgamma (int g) {
1605 gamma = g < 0 ? 0 : (g > 4 ? 4 : g);
1608 int R_getgamma (void) {
1609 return gamma;
1612 void R_toggle_fullscreen (void) {
1613 fullscreen = !fullscreen;
1614 if (surf) {
1615 R_init(); // recreate window
1619 void R_get_name (int n, char s[8]) {
1620 assert(n >= 0 && n < 256);
1621 if (walp[n].res == -1) {
1622 memset(s, 0, 8);
1623 } else if (walp[n].res == -2) {
1624 memcpy(s, "_WATER_", 8);
1625 s[7] = '0' + (intptr_t)walp[n].n - 1;
1626 } else if (walani[n] > 0) {
1627 memcpy(s, anm[walani[n] - 1][0], 8);
1628 } else {
1629 F_getresname(s, walp[n].res & 0x7FFF);
1633 static short getani (char n[8]) {
1634 short i = 0;
1635 while (i < ANIT && strncasecmp(n, anm[i][0], 8) != 0) {
1636 i++;
1638 return i < ANIT ? i + 1 : 0;
1641 int R_get_special_id (int n) {
1642 assert(n >= 0 && n <= 256);
1643 return walp[n].res == -2 ? (intptr_t)walp[n].n : -1;
1646 void R_begin_load (void) {
1647 int i;
1648 for (i = 0; i < 256; i++) {
1649 if (walp[i].n != NULL && walp[i].res >= 0 && walani[i] == 0) {
1650 R_gl_free_image(&walp[i]);
1652 memset(&walp[i], 0, sizeof(image));
1653 walp[i].res = -1;
1654 walswp[i] = i;
1655 walani[i] = 0;
1657 memset(anic, 0, sizeof(anic));
1658 max_wall_width = 0;
1659 max_wall_height = 0;
1660 max_textures = 1;
1663 void R_load (char s[8], int f) {
1664 assert(max_textures < 256);
1665 if (!s[0]) {
1666 walp[max_textures] = (image) {
1667 .n = NULL,
1668 .x = 0,
1669 .y = 0,
1670 .w = 0,
1671 .h = 0,
1672 .res = -1,
1673 };
1674 } else if (strncasecmp(s, "_WATER_", 7) == 0) {
1675 walp[max_textures] = (image) {
1676 .n = (void*)(s[7] - '0' + 1),
1677 .x = 0,
1678 .y = 0,
1679 .w = 8,
1680 .h = 8,
1681 .res = -2,
1682 };
1683 } else {
1684 walp[max_textures] = R_gl_loadimage(s);
1685 if (f) {
1686 walp[max_textures].res |= 0x8000;
1688 if (s[0] == 'S' && s[1] == 'W' && s[4] == '_') {
1689 walswp[max_textures] = 0;
1691 walani[max_textures] = getani(s);
1693 max_wall_width = max(max_wall_width, walp[max_textures].w);
1694 max_wall_height = max(max_wall_height, walp[max_textures].h);
1695 max_textures++;
1698 void R_end_load (void) {
1699 int i, j, k, g;
1700 char s[8];
1701 j = max_textures;
1702 for (i = 1; i < 256 && j < 256; i++) {
1703 if (walswp[i] == 0) {
1704 R_get_name(i, s);
1705 s[5] ^= 1;
1706 g = F_getresid(s) | (walp[i].res & 0x8000);
1707 k = 1;
1708 while (k < 256 && walp[k].res != g) {
1709 k += 1;
1711 if (k >= 256) {
1712 k = j;
1713 j += 1;
1714 walp[k] = R_gl_getimage(g);
1715 walf[k] = g & 0x8000 ? 1 : 0;
1717 walswp[i] = k;
1718 walswp[k] = i;
1723 void R_loadsky (int sky) {
1724 char s[6];
1725 logo("R_loadsky(%i)\n", sky);
1726 strcpy(s, "RSKYx");
1727 s[4] = '0' + sky;
1728 R_gl_free_image(&horiz);
1729 horiz = R_gl_loadimage(s);