DEADSOFTWARE

e9dc5b5addb443537778fc50ed596e4a0f05244b
[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 #include <OpenGL/GL.h>
25 #include <stdlib.h> // malloc free abs
26 #include <assert.h> // assert
27 #include <SDL.h>
29 #define VGA_TRANSPARENT_COLOR 0
30 #define DEFAULT_SKY_COLOR 0x97
31 #define MANCOLOR 0xD0
32 #define PLAYER_COLOR_OFFSET 7
33 #define MAXAIR 1091
34 #define ANIT 5
35 #define PL_FLASH 90
37 #pragma pack(1)
38 typedef struct vgaimg {
39 word w, h;
40 short x, y;
41 byte data[];
42 } vgaimg;
44 typedef struct rgb {
45 byte r, g, b;
46 } rgb;
48 typedef struct rgba {
49 byte r, g, b, a;
50 } rgba;
51 #pragma pack()
53 typedef struct node {
54 struct cache *base;
55 struct node *left, *right;
56 int l, t, r, b;
57 int leaf;
58 } node;
60 typedef struct cache {
61 struct cache *next;
62 struct node root;
63 GLuint id;
64 } cache;
66 typedef struct image {
67 node *n;
68 GLint x, y;
69 GLuint w, h;
70 int res;
71 } image;
73 /* Render Specific */
74 int SCRW = 320; // public
75 int SCRH = 200; // public
76 static int gamma;
77 static int fullscreen;
78 static SDL_Surface *surf;
79 static rgb playpal[256];
80 static byte bright[256];
81 static cache *root;
83 /* Game */
84 static image scrnh[3]; // TITLEPIC INTERPIC ENDPIC
85 static image ltn[2][2];
87 /* Smoke */
88 static image smk_spr[SMSN];
89 static image smk_fspr[FLSN];
91 /* Effects */
92 static image fx_spr[15];
93 static char fx_sprd[15];
95 /* Weapons */
96 static image wp_spr[49*2];
97 static char wp_sprd[49*2];
99 /* Items */
100 static image item_spr[58];
101 static char item_sprd[58];
103 /* Player */
104 static image plr_spr[27*2];
105 static image plr_msk[27*2];
106 static char plr_sprd[27*2];
107 static image plr_wpn[11][6];
109 /* Monsters */
110 static image pl_spr[2];
111 static image pl_msk[2];
112 static image mn_spr[MN_TN][29*2];
113 static image mn_man_msk[29*2];
114 static char mn_sprd[MN_TN][29*2];
115 static image mn_fspr[8];
116 static image mn_sgun[2];
118 /* Misc */
119 static image sth[22];
120 static image bfh[160 - '!'];
121 static image sfh[160 - '!'];
122 static image stone;
123 static image stone2;
124 static image keys[3];
125 static int prx = 0;
126 static int pry = 0;
128 /* Menu */
129 static int gm_tm;
130 static image msklh[2];
131 static image mbarl;
132 static image mbarm;
133 static image mbarr;
134 static image mbaro;
135 static image mslotl;
136 static image mslotm;
137 static image mslotr;
139 /* Map */
140 static const char *anm[ANIT - 1][5] = {
141 {"WALL22_1", "WALL23_1", "WALL23_2", NULL, NULL},
142 {"WALL58_1", "WALL58_2", "WALL58_3", NULL, NULL},
143 {"W73A_1", "W73A_2", NULL, NULL, NULL},
144 {"RP2_1", "RP2_2", "RP2_3", "RP2_4", NULL}
145 };
146 static int max_wall_width;
147 static int max_wall_height;
148 static int max_textures;
149 static image walp[256];
150 static byte walani[256];
151 static image anip[ANIT][5];
152 static byte anic[ANIT];
153 static image horiz;
155 /* Texture cache */
157 // https://blackpawn.com/texts/lightmaps/
158 static node *R_node_alloc (node *p, int w, int h) {
159 assert(p);
160 assert(w > 0);
161 assert(h > 0);
162 if (p->left) {
163 assert(p->right);
164 node *n = R_node_alloc(p->left, w, h);
165 return n ? n : R_node_alloc(p->right, w, h);
166 } else {
167 int pw = p->r - p->l + 1;
168 int ph = p->b - p->t + 1;
169 if (p->leaf || pw < w || ph < h) {
170 return NULL;
171 } else if (pw == w && ph == h) {
172 p->leaf = 1;
173 return p;
174 } else {
175 p->left = malloc(sizeof(node));
176 p->right = malloc(sizeof(node));
177 if (pw - w > ph - h) {
178 *p->left = (node) {
179 .l = p->l,
180 .t = p->t,
181 .r = p->l + w - 1,
182 .b = p->b
183 };
184 *p->right = (node) {
185 .l = p->l + w,
186 .t = p->t,
187 .r = p->r,
188 .b = p->b
189 };
190 } else {
191 *p->left = (node) {
192 .l = p->l,
193 .t = p->t,
194 .r = p->r,
195 .b = p->t + h - 1
196 };
197 *p->right = (node) {
198 .l = p->l,
199 .t = p->t + h,
200 .r = p->r,
201 .b = p->b
202 };
204 return R_node_alloc(p->left, w, h);
209 static cache *R_cache_new (void) {
210 GLuint id = 0;
211 GLint size = 0;
212 cache *c = NULL;
213 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
214 size /= 2; // TODO remove hack or detect ibook bug
215 if (size) {
216 glGenTextures(1, &id);
217 if (id) {
218 glBindTexture(GL_TEXTURE_2D, id);
219 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
220 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
221 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
222 int ok = glGetError() == GL_NO_ERROR;
223 glBindTexture(GL_TEXTURE_2D, 0);
224 if (ok) {
225 c = malloc(sizeof(cache));
226 if (c != NULL) {
227 *c = (cache) {
228 .id = id,
229 .root.r = size - 1,
230 .root.b = size - 1
231 };
234 if (c == NULL) {
235 glDeleteTextures(1, &id);
239 logo("new cache %p\n", c);
240 return c;
243 static node *R_cache_alloc (cache *root, int w, int h) {
244 assert(root);
245 assert(w > 0 && h > 0);
246 node *n = NULL;
247 cache *p = NULL;
248 cache *c = root;
249 while (c && !n) {
250 n = R_node_alloc(&c->root, w, h);
251 if (n) {
252 n->base = c;
254 p = c;
255 c = c->next;
257 if (!n) {
258 c = R_cache_new();
259 if (c) {
260 p->next = c;
261 n = R_node_alloc(&c->root, w, h);
262 if (n) {
263 n->base = c;
267 return n;
270 static void R_cache_update (node *n, const void *data, int w, int h) {
271 assert(n);
272 assert(n->base);
273 assert(data);
274 int nw = n->r - n->l + 1;
275 int nh = n->b - n->t + 1;
276 assert(w == nw);
277 assert(h == nh);
278 glBindTexture(GL_TEXTURE_2D, n->base->id);
279 glTexSubImage2D(GL_TEXTURE_2D, 0, n->l, n->t, nw, nh, GL_RGBA, GL_UNSIGNED_BYTE, data);
280 assert(glGetError() == GL_NO_ERROR);
281 glBindTexture(GL_TEXTURE_2D, 0);
284 /* Generic helpers */
286 static void R_init_playpal (void) {
287 int i;
288 byte *vgapal = M_lock(F_getresid("PLAYPAL"));
289 for (i = 0; i < 256; i++) {
290 playpal[i] = (rgb) {
291 .r = vgapal[i * 3 + 0] * 255 / 63,
292 .g = vgapal[i * 3 + 1] * 255 / 63,
293 .b = vgapal[i * 3 + 2] * 255 / 63,
294 };
295 bright[i] = ((int)vgapal[i * 3 + 0] + vgapal[i * 3 + 1] + vgapal[i * 3 + 2]) * 8 / (63 * 3);
297 M_unlock(vgapal);
300 static vgaimg *R_getvga (int id) {
301 int loaded = M_was_locked(id);
302 vgaimg *v = M_lock(id);
303 if (v != NULL && !loaded) {
304 v->w = short2host(v->w);
305 v->h = short2host(v->h);
306 v->x = short2host(v->x);
307 v->y = short2host(v->y);
309 return v;
312 static rgba *R_extract_flame_spr (vgaimg *v) {
313 static const byte flametab[16] = {
314 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0xD5,
315 0xD6, 0xD7, 0xA1, 0xA0, 0xE3, 0xE2, 0xE1, 0xE0
316 };
317 int i, j;
318 rgba *s = malloc(v->w * v->h * sizeof(rgba));
319 if (s != NULL) {
320 for (j = 0; j < v->h; j++) {
321 for (i = 0; i < v->w; i++) {
322 int k = j * v->w + i;
323 byte c = v->data[k] + bright[DEFAULT_SKY_COLOR];
324 s[k] = (rgba) {
325 .r = playpal[flametab[c]].r,
326 .g = playpal[flametab[c]].g,
327 .b = playpal[flametab[c]].b,
328 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
329 };
333 return s;
336 static rgba *R_extract_smoke_spr (vgaimg *v) {
337 int i, j;
338 rgba *s = malloc(v->w * v->h * sizeof(rgba));
339 if (s != NULL) {
340 for (j = 0; j < v->h; j++) {
341 for (i = 0; i < v->w; i++) {
342 int k = j * v->w + i;
343 byte c = ((v->data[k] + bright[DEFAULT_SKY_COLOR]) + 0x60) ^ 0x0F;
344 byte a = 0xFF - ((int)playpal[c].r + playpal[c].g + playpal[c].b) / 3;
345 s[k] = (rgba) {
346 .r = playpal[c].r,
347 .g = playpal[c].g,
348 .b = playpal[c].b,
349 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : a,
350 };
354 return s;
357 static rgba *R_extract_mask_spr (vgaimg *v) {
358 int i, j;
359 rgba *s = malloc(v->w * v->h * sizeof(rgba));
360 if (s != NULL) {
361 for (j = 0; j < v->h; j++) {
362 for (i = 0; i < v->w; i++) {
363 int k = j * v->w + i;
364 byte c = v->data[k];
365 if (c >= 0x70 && c <= 0x7F) {
366 byte mask = c - 0x70;
367 mask = 0xFF - ((mask << 4) | mask);
368 s[k] = (rgba) {
369 .r = mask,
370 .g = mask,
371 .b = mask,
372 .a = 0xFF,
373 };
374 } else {
375 s[k] = (rgba) {
376 .r = 0,
377 .g = 0,
378 .b = 0,
379 .a = 0,
380 };
385 return s;
388 static rgba *R_extract_rgba_spr (vgaimg *v) {
389 int i, j;
390 rgba *s = malloc(v->w * v->h * sizeof(rgba));
391 if (s != NULL) {
392 for (j = 0; j < v->h; j++) {
393 for (i = 0; i < v->w; i++) {
394 int k = j * v->w + i;
395 byte c = v->data[k];
396 s[k] = (rgba) {
397 .r = playpal[c].r,
398 .g = playpal[c].g,
399 .b = playpal[c].b,
400 .a = c == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
401 };
405 return s;
408 /* OpenGL helpers */
410 static image R_gl_create_image (const rgba *buf, int w, int h) {
411 node *n = R_cache_alloc(root, w, h);
412 if (n) {
413 R_cache_update(n, buf, w, h);
415 return (image) {
416 .n = n,
417 .w = w,
418 .h = h,
419 .res = -1
420 };
423 static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
424 image img = (image) { .res = -1 };
425 vgaimg *v = R_getvga(id);
426 if (v != NULL) {
427 rgba *buf = (*fn)(v);
428 img = R_gl_create_image(buf, v->w, v->h);
429 img.x = v->x;
430 img.y = v->y;
431 img.res = id;
432 M_unlock(v);
433 free(buf);
435 return img;
438 static image R_gl_getimage (int id) {
439 return R_gl_get_special_image(id, &R_extract_rgba_spr);
442 static image R_gl_loadimage (const char name[8]) {
443 return R_gl_getimage(F_getresid(name));
446 static image R_gl_get_special_spr (const char n[4], int s, int d, rgba *(*fn)(vgaimg*)) {
447 return R_gl_get_special_image(F_getsprid(n, s, d), fn);
450 static void R_gl_free_image (image *img) {
451 if (img->n != NULL && img->res >= 0) {
452 // TODO delete node
454 img->n = NULL;
457 static void R_gl_draw_quad (int x, int y, int w, int h) {
458 glBegin(GL_QUADS);
459 glVertex2i(x + w, y);
460 glVertex2i(x, y);
461 glVertex2i(x, y + h);
462 glVertex2i(x + w, y + h);
463 glEnd();
466 static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip) {
467 if (img->n) {
468 GLfloat nw = img->n->base->root.r + 1;
469 GLfloat nh = img->n->base->root.b + 1;
470 GLfloat ax = (flip ? img->n->l : img->n->r + 1) / nw;
471 GLfloat bx = (flip ? img->n->r + 1 : img->n->l) / nh;
472 GLfloat ay = (img->n->t) / nw;
473 GLfloat by = (img->n->b + 1) / nh;
474 glBindTexture(GL_TEXTURE_2D, img->n->base->id);
475 glEnable(GL_TEXTURE_2D);
476 glBegin(GL_QUADS);
477 glTexCoord2f(ax, ay); glVertex2i(x + w, y);
478 glTexCoord2f(bx, ay); glVertex2i(x, y);
479 glTexCoord2f(bx, by); glVertex2i(x, y + h);
480 glTexCoord2f(ax, by); glVertex2i(x + w, y + h);
481 glEnd();
482 glDisable(GL_TEXTURE_2D);
483 glBindTexture(GL_TEXTURE_2D, 0);
484 } else {
485 glColor3ub(255, 0, 0);
486 glDisable(GL_BLEND);
487 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
488 R_gl_draw_quad(x, y, w, h);
492 /* fit image into rectangle without applying offset and transparency */
493 static void R_gl_draw_image_ext (image *img, int x, int y, int w, int h) {
494 glDisable(GL_BLEND);
495 glColor3ub(255, 255, 255);
496 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
497 R_gl_draw_textured(img, x, y, w, h, 0);
500 /* draw sprite with offset and coloring */
501 static void R_gl_draw_image_color (image *img, int x, int y, int flip) {
502 int xx = flip ? x - img->w + img->x : x - img->x;
503 glEnable(GL_BLEND);
504 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
505 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
506 glDisable(GL_BLEND);
509 /* draw sprite with offset */
510 static void R_gl_draw_image (image *img, int x, int y, int flip) {
511 int xx = flip ? x - img->w + img->x : x - img->x;
512 glEnable(GL_BLEND);
513 glColor3ub(255, 255, 255);
514 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
515 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
518 static void R_gl_set_color (byte c) {
519 glColor3ub(playpal[c].r, playpal[c].g, playpal[c].b);
522 static void R_gl_setclip (int x, int y, int w, int h) {
523 glScissor(x, SCRH - h - y, w, h);
526 static void R_gl_setmatrix (void) {
527 glScissor(0, 0, SCRW, SCRH);
528 glViewport(0, 0, SCRW, SCRH);
529 glMatrixMode(GL_PROJECTION);
530 glLoadIdentity();
531 glOrtho(0, SCRW, SCRH, 0, 0, 1);
532 glMatrixMode(GL_MODELVIEW);
533 glLoadIdentity();
536 /* --- Misc --- */
538 static image Z_getspr (const char n[4], int s, int d, char *dir) {
539 int h = F_getsprid(n, s, d);
540 if (dir != NULL) {
541 *dir = (h & 0x8000) ? 1 : 0;
543 return R_gl_getimage(h);
546 static void Z_putch_generic (image img[], int off, int ch) {
547 image *p = NULL;
548 if (ch > 32 && ch < 160) {
549 p = &img[ch - '!'];
551 if (p != NULL) {
552 R_gl_draw_image(p, prx, pry, 0);
553 prx += p->w - 1;
554 } else {
555 prx += off;
559 static void Z_printf_generic (image img[], int off, const char *fmt, va_list ap) {
560 int i;
561 char buf[80];
562 vsprintf(buf, fmt, ap);
563 for (i = 0; buf[i]; ++i) {
564 switch (buf[i]) {
565 case '\n':
566 pry += off + 1;
567 case '\r':
568 prx = 0;
569 break;
570 default:
571 Z_putch_generic(img, off, (byte)buf[i]);
576 static void Z_gotoxy (int x, int y) {
577 prx = x;
578 pry = y;
581 static void Z_printbf (const char *fmt, ...) {
582 va_list a;
583 va_start(a, fmt);
584 Z_printf_generic(bfh, 12, fmt, a);
585 va_end(a);
588 static void Z_printsf (const char *fmt, ...) {
589 va_list a;
590 va_start(a, fmt);
591 Z_printf_generic(sfh, 7, fmt, a);
592 va_end(a);
595 static void Z_printhf (const char *fmt, ...) {
596 int i, c;
597 char buf[80];
598 va_list a;
599 va_start(a, fmt);
600 vsprintf(buf, fmt, a);
601 va_end(a);
602 for (i = 0; buf[i]; ++i) {
603 switch (buf[i]) {
604 case '0':
605 case '1':
606 case '2':
607 case '3':
608 case '4':
609 case '5':
610 case '6':
611 case '7':
612 case '8':
613 case '9':
614 c = buf[i] - '0';
615 break;
616 case '-':
617 c = 10;
618 break;
619 case '%':
620 c = 11;
621 break;
622 case '\n':
623 pry += 19;
624 case '\r':
625 c = -1;
626 prx = 0;
627 break;
628 default:
629 c = -1;
630 break;
632 if (c >= 0) {
633 R_gl_draw_image(&sth[c], prx, pry, 0);
635 prx += 14;
639 /* --- Menu --- */
641 static image *PL_getspr (int s, int d, int msk) {
642 int i = (s - 'A') * 2 + d;
643 return msk ? &plr_msk[i] : &plr_spr[i];
646 static void GM_draw (void) {
647 enum {MENU, MSG}; // copypasted from menu.c!
648 enum {
649 CANCEL, NEWGAME, LOADGAME, SAVEGAME, OPTIONS, QUITGAME, QUIT, ENDGAME, ENDGM,
650 PLR1, PLR2, COOP, DM, VOLUME, GAMMA, LOAD, SAVE, PLCOLOR, PLCEND, MUSIC, INTERP,
651 SVOLM, SVOLP, MVOLM, MVOLP, GAMMAM, GAMMAP, PL1CM, PL1CP, PL2CM, PL2CP
652 }; // copypasted from menu.c!
653 int i, j, k, x, y, cx, cy;
654 image *img;
655 gm_tm += 1;
656 if (mnu != NULL) {
657 cx = SCRW / 2;
658 cy = SCRH / 2;
659 if (mnu->type == MENU) {
660 y = cy - (mnu->n * 16 - 20) / 2;
661 Z_gotoxy(cx - mnu->x, y - 10); Z_printbf("%s", mnu->ttl);
662 for (i = 0; i < mnu->n; i++) {
663 if (mnu->t[i] == LOAD || mnu->t[i] == SAVE) {
664 j = y + i * 16 + 29;
665 R_gl_draw_image(&mslotl, cx - mnu->x, j, 0);
666 for (k = 8; k < 184; k += 8) {
667 R_gl_draw_image(&mslotm, cx - mnu->x + k, j, 0);
669 R_gl_draw_image(&mslotr, cx - mnu->x + 184, j, 0);
670 Z_gotoxy(cx - mnu->x + 4, j - 8);
671 if (input && i == save_mnu.cur) {
672 Z_printsf("%s_", ibuf);
673 } else {
674 Z_printsf("%s", savname[i]);
676 } else {
677 x = mnu->t[i] >= SVOLM ? (mnu->t[i] >= PL1CM ? 50 : 152) : 0;
678 Z_gotoxy(cx - mnu->x + x, y + i * 16 + 20);
679 Z_printbf("%s", mnu->m[i]);
680 switch (mnu->t[i]) {
681 case MUSIC:
682 Z_printbf(" '%s'", g_music);
683 break;
684 case INTERP:
685 Z_printbf("%s", fullscreen ? "ON" : "OFF");
686 break;
687 case PL1CM:
688 case PL1CP:
689 case PL2CM:
690 case PL2CP:
691 img = PL_getspr(*panimp, 0, 0);
692 R_gl_draw_image(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
693 img = PL_getspr(*panimp, 0, 1);
694 R_gl_set_color(pcolortab[(mnu->t[i] == PL1CM) ? p1color : p2color] + PLAYER_COLOR_OFFSET);
695 R_gl_draw_image_color(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
696 break;
697 case SVOLM:
698 case SVOLP:
699 case MVOLM:
700 case MVOLP:
701 case GAMMAM:
702 case GAMMAP:
703 j = y + i * 16 + 20;
704 R_gl_draw_image(&mbarl, cx - mnu->x, j, 0);
705 for (k = 8; k < 144; k += 8) {
706 R_gl_draw_image(&mbarm, cx - mnu->x + k, j, 0);
708 R_gl_draw_image(&mbarr, cx - mnu->x + 144, j, 0);
709 switch (mnu->t[i]) {
710 case SVOLM:
711 k = snd_vol;
712 break;
713 case MVOLM:
714 k = mus_vol;
715 break;
716 case GAMMAM:
717 k = gamma << 5;
718 break;
719 default:
720 k = 0;
721 break;
723 R_gl_draw_image(&mbaro, cx - mnu->x + 8 + k, j, 0);
724 break;
728 R_gl_draw_image(&msklh[(gm_tm / 6) & 1], cx - mnu->x - 25, y + mnu->cur * 16 + 20 - 8, 0);
729 } else if (mnu->type == MSG) {
730 Z_gotoxy(cx - strlen(mnu->ttl) * 7 / 2, cy - 10); Z_printsf(mnu->ttl);
731 Z_gotoxy(cx - 24, SCRH / 2); Z_printsf("(Y/N)");
732 } else {
733 ERR_fatal("Unknown menu type %i\n", mnu->type);
738 /* --- View --- */
740 static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int fg) {
741 int i, j;
742 assert(minx >= 0 && minx <= FLDW);
743 assert(miny >= 0 && miny <= FLDH);
744 assert(maxx >= 0 && maxx <= FLDW);
745 assert(maxy >= 0 && maxy <= FLDH);
746 for (j = miny; j < maxy; j++) {
747 for (i = minx; i < maxx; i++) {
748 byte id = fld[j * FLDW + i];
749 if (id != 0) {
750 if (walp[id].res < 0) {
751 if (fg) {
752 switch (R_get_special_id(id)) {
753 case 1:
754 glColor4ub(0, 0, 255, 127);
755 break;
756 case 2:
757 glColor4ub(0, 127, 0, 127);
758 break;
759 case 3:
760 glColor4ub(127, 0, 0, 127);
761 break;
762 default:
763 glColor4ub(0, 0, 0, 127);
764 break;
766 glEnable(GL_BLEND);
767 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
768 R_gl_draw_quad(i * CELW, j * CELW, CELW, CELH);
770 } else {
771 R_gl_draw_image(&walp[id], i * CELW, j * CELH, 0);
778 static void R_draw_dots (void) {
779 int i;
780 glBegin(GL_POINTS);
781 for (i = 0; i < MAXDOT; i++) {
782 if (dot[i].t != 0) {
783 R_gl_set_color(dot[i].c);
784 glVertex2i(dot[i].o.x, dot[i].o.y + 1);
787 glEnd();
790 static void R_draw_items (void) {
791 int i, s;
792 for (i = 0; i < MAXITEM; ++i) {
793 s = -1;
794 if (it[i].t && it[i].s >= 0) {
795 switch (it[i].t & 0x7FFF) {
796 case I_ARM1:
797 s = it[i].s / 9 + 18;
798 break;
799 case I_ARM2:
800 s = it[i].s / 9 + 20;
801 break;
802 case I_MEGA:
803 s = it[i].s / 2 + 22;
804 break;
805 case I_INVL:
806 s = it[i].s / 2 + 26;
807 break;
808 case I_SUPER:
809 case I_RTORCH:
810 case I_GTORCH:
811 case I_BTORCH:
812 s = it[i].s / 2 + (it[i].t - I_SUPER) * 4 + 35;
813 break;
814 case I_GOR1: case I_FCAN:
815 s = it[i].s / 2 + (it[i].t - I_GOR1) * 3 + 51;
816 break;
817 case I_AQUA:
818 s = 30;
819 break;
820 case I_SUIT:
821 s = 34;
822 break;
823 case I_KEYR:
824 case I_KEYG:
825 case I_KEYB:
826 s = (it[i].t & 0x7FFF) - I_KEYR + 31;
827 break;
828 case I_GUN2:
829 s = 57;
830 break;
831 default:
832 s = (it[i].t & 0x7FFF) - 1;
835 if (s >= 0) {
836 R_gl_draw_image(&item_spr[s], it[i].o.x, it[i].o.y, item_sprd[s]);
841 static int standspr (player_t *p) {
842 if (p->f & PLF_UP) {
843 return 'X';
844 } else if (p->f & PLF_DOWN) {
845 return 'Z';
846 } else {
847 return 'E';
851 static int wpnspr (player_t *p) {
852 if (p->f & PLF_UP) {
853 return 'C';
854 } else if(p->f & PLF_DOWN) {
855 return 'E';
856 } else {
857 return 'A';
861 static void R_draw_player (player_t *p) {
862 enum {STAND, GO, DIE, SLOP, DEAD, MESS, OUT, FALL}; // copypasted from player.c!
863 static const int wytab[] = {-1, -2, -1, 0};
864 int s = 'A';
865 int w = 0;
866 int wx = 0;
867 int wy = 0;
868 switch (p->st) {
869 case STAND:
870 if (p->f & PLF_FIRE) {
871 s = standspr(p) + 1;
872 w = wpnspr(p) + 1;
873 } else if (p->pain) {
874 s = 'G';
875 w = 'A';
876 wx = p->d ? 2 : -2;
877 wy = 1;
878 } else {
879 s = standspr(p);
880 w = wpnspr(p);
882 break;
883 case DEAD:
884 s = 'N';
885 break;
886 case MESS:
887 s = 'W';
888 break;
889 case GO:
890 if (p->pain) {
891 s = 'G';
892 w = 'A';
893 wx = p->d ? 2 : -2;
894 wy = 1;
895 } else {
896 s = plr_goanim[p->s / 8];
897 w = (p->f & PLF_FIRE) ? 'B' : 'A';
898 wx = p->d ? 2 : -2;
899 wy = 1 + wytab[s - 'A'];
901 break;
902 case DIE:
903 s = plr_dieanim[p->s];
904 break;
905 case SLOP:
906 s = plr_slopanim[p->s];
907 break;
908 case OUT:
909 s = 0;
910 break;
912 if (p->wpn == 0) {
913 w = 0;
915 if (w) {
916 R_gl_draw_image(&plr_wpn[(int)p->wpn][w -'A'], p->o.x + wx, p->o.y + wy, p->d);
918 if (s) {
919 R_gl_draw_image(&plr_spr[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
920 R_gl_set_color(p->color + PLAYER_COLOR_OFFSET);
921 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]);
925 static void R_draw_monsters (void) {
926 enum {SLEEP, GO, RUN, CLIMB, DIE, DEAD, ATTACK, SHOOT, PAIN, WAIT, REVIVE, RUNOUT}; // copypasted from monster.c!
927 int i;
928 for (i = 0; i < MAXMN; i++) {
929 if (mn[i].t != MN_NONE) {
930 int x = mn[i].o.x;
931 int y = mn[i].o.y;
932 if (mn[i].t < MN__LAST) {
933 if ((mn[i].t != MN_SOUL && mn[i].t != MN_PAIN) || mn[i].st != DEAD) {
934 int ap = mn[i].ap[mn[i].ac];
935 int d = (ap - 'A') * 2 + mn[i].d;
936 int dir = mn_sprd[mn[i].t - 1][d];
937 if (mn[i].t == MN_MAN && (ap == 'E' || ap == 'F')) {
938 R_gl_draw_image(&mn_sgun[ap - 'E'], x, y, mn[i].d);
940 R_gl_draw_image(&mn_spr[mn[i].t - 1][d], x, y, dir);
941 if (mn[i].t == MN_MAN) {
942 R_gl_set_color(MANCOLOR + PLAYER_COLOR_OFFSET);
943 R_gl_draw_image_color(&mn_man_msk[d], x, y, dir);
946 if (mn[i].t == MN_VILE && mn[i].st == SHOOT) {
947 R_gl_draw_image(&mn_fspr[mn[i].ac / 3], mn[i].tx, mn[i].ty, 0);
949 } else if (mn[i].t == MN_PL_DEAD || mn[i].t == MN_PL_MESS) {
950 int type = mn[i].t - MN_PL_DEAD;
951 R_gl_draw_image(&pl_spr[type], x, y, 0);
952 R_gl_set_color(mn[i].d);
953 R_gl_draw_image_color(&pl_msk[type], x, y, 0);
959 static void R_draw_weapons (void) {
960 enum {NONE, ROCKET, PLASMA, APLASMA, BALL1, BALL2, BALL7, BFGBALL, BFGHIT, MANF, REVF, FIRE}; // copypasted from weapons.c!
961 int i, s, d, x, y;
962 for (i = 0; i < MAXWPN; ++i) {
963 s = -1;
964 d = 0;
965 switch (wp[i].t) {
966 case REVF:
967 case ROCKET:
968 d = wp[i].s;
969 if (d < 2) {
970 d = wp[i].o.xv > 0 ? 1 : 0;
971 x = abs(wp[i].o.xv);
972 y = wp[i].o.yv;
973 s = 0;
974 if (y < 0) {
975 if (-y >= x) {
976 s = 30;
978 } else if (y > 0) {
979 if (y >= x / 2) {
980 s = 31;
983 } else {
984 s = (d - 2) / 2 + 1;
985 d = 0;
987 break;
988 case MANF:
989 s=wp[i].s;
990 if (s >= 2) {
991 s /= 2;
992 break;
994 case PLASMA:
995 case APLASMA:
996 case BALL1:
997 case BALL7:
998 case BALL2:
999 s = wp[i].s;
1000 if (s >= 2) {
1001 s = s / 2 + 1;
1003 switch (wp[i].t) {
1004 case PLASMA:
1005 s += 4;
1006 break;
1007 case APLASMA:
1008 s += 11;
1009 break;
1010 case BALL1:
1011 s += 32;
1012 break;
1013 case BALL2:
1014 s += 42;
1015 break;
1016 case BALL7:
1017 s += 37;
1018 d = wp[i].o.xv >= 0 ? 1 : 0;
1019 break;
1020 case MANF:
1021 s += 47;
1022 d= wp[i].o.xv>=0 ? 1 : 0;
1023 break;
1025 break;
1026 case BFGBALL:
1027 s = wp[i].s;
1028 if (s >= 2) {
1029 s = s / 2 + 1;
1031 s += 18;
1032 break;
1033 case BFGHIT:
1034 s = wp[i].s / 2 + 26;
1035 break;
1037 if (s >= 0) {
1038 R_gl_draw_image(&wp_spr[s * 2 + d], wp[i].o.x, wp[i].o.y, wp_sprd[s * 2 + d]);
1043 static void R_draw_smoke (void) {
1044 int i, s;
1045 for (i = 0; i < MAXSMOK; ++i) {
1046 if (sm[i].t) {
1047 switch (sm[i].s) {
1048 case 0:
1049 s = sm[i].t;
1050 if (s >= (SMSN - 1) * 3) {
1051 s = 0;
1052 } else {
1053 s = SMSN - 1 - s / 3;
1055 R_gl_draw_image(&smk_spr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1056 break;
1057 case 1:
1058 s = sm[i].t;
1059 if (s >= FLSN - 1) {
1060 s = 0;
1061 } else {
1062 s = FLSN - 1 - s;
1064 R_gl_draw_image(&smk_fspr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1065 break;
1071 static void R_draw_effects (void) {
1072 enum {NONE, TFOG, IFOG, BUBL}; // copypasted from fx.c
1073 int i, s;
1074 for (i = 0; i < MAXFX; ++i) {
1075 switch (fx[i].t) {
1076 case TFOG:
1077 s = fx[i].s / 2;
1078 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1079 break;
1080 case IFOG:
1081 s = fx[i].s / 2 + 10;
1082 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1083 break;
1084 case BUBL:
1085 glBegin(GL_POINTS);
1086 R_gl_set_color(0xC0 + fx[i].s);
1087 glVertex2i(fx[i].x >> 8, (fx[i].y >> 8) + 1);
1088 glEnd();
1089 break;
1094 static int get_pu_st (int t) {
1095 if (t >= PL_FLASH) {
1096 return 1;
1097 } else if((t / 9) & 1) {
1098 return 0;
1099 } else {
1100 return 1;
1104 static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
1105 glPushMatrix();
1106 R_gl_setclip(x, y, w, h);
1107 glTranslatef(x, y, 0);
1108 if (w_horiz && horiz.n != NULL) {
1109 R_gl_draw_image_ext(&horiz, 0, 0, w, h);
1110 if (sky_type == 2 && lt_time < 0) {
1111 image *tanderbolt = &ltn[lt_type][lt_time < -5 ? 0 : 1];
1112 if (!lt_side) {
1113 R_gl_draw_image(tanderbolt, 0, lt_ypos, 0);
1114 } else {
1115 R_gl_draw_image(tanderbolt, w - 1, lt_ypos, 1);
1118 } else {
1119 glDisable(GL_BLEND);
1120 R_gl_set_color(DEFAULT_SKY_COLOR);
1121 R_gl_draw_quad(0, 0, w, h);
1123 int maxx = min((camx + w) / CELW + 1, FLDW);
1124 int maxy = min((camy + h) / CELH + 1, FLDH);
1125 int minx = max((camx - max_wall_width) / CELW, 0);
1126 int miny = max((camy - max_wall_height) / CELH, 0);
1127 glTranslatef(-camx, -camy, 0);
1128 R_draw_fld((byte*)fldb, minx, miny, maxx, maxy, 0);
1129 R_draw_dots();
1130 R_draw_items();
1131 R_draw_player(&pl1);
1132 if (_2pl) {
1133 R_draw_player(&pl2);
1135 R_draw_monsters();
1136 R_draw_weapons();
1137 R_draw_smoke();
1138 R_draw_effects();
1139 R_draw_fld((byte*)fldf, minx, miny, maxx, maxy, 1);
1140 glTranslatef(camx, camy, 0);
1141 if (sky_type == 2 && (lt_time == -4 || lt_time == -2)) {
1142 glColor4ub(255, 255, 255, 255);
1143 glEnable(GL_BLEND);
1144 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1145 R_gl_draw_quad(0, 0, w, h);
1147 glPopMatrix();
1150 static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
1151 p->looky = min(max(p->looky, -SCRH / 4), SCRH / 4); // TODO remove writeback
1152 int st = stone.w;
1153 int cw = w - st;
1154 int cx = min(max(p->o.x, cw / 2), FLDW * CELW - cw / 2);
1155 int cy = min(max(p->o.y - 12 + p->looky, h / 2), FLDH * CELH - h / 2);
1156 int camx = max(cx - cw / 2, 0);
1157 int camy = max(cy - h / 2, 0);
1158 glPushMatrix();
1159 R_draw_view(x, y + 1, cw, h - 2, camx, camy);
1160 glTranslatef(x, y, 0);
1161 if (p->invl) {
1162 if (get_pu_st(p->invl)) {
1163 glEnable(GL_BLEND);
1164 glColor4ub(191, 191, 191, 255);
1165 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
1166 R_gl_draw_quad(0, 0, cw, h);
1168 } else {
1169 if (p->suit && get_pu_st(p->suit)) {
1170 glEnable(GL_BLEND);
1171 glColor4ub(0, 255, 0, 192);
1172 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1173 R_gl_draw_quad(0, 0, cw, h);
1175 int f = min(max(p->pain * 3, 0), 255);
1176 if (f > 0) {
1177 glEnable(GL_BLEND);
1178 glColor4ub(255, 0, 0, f);
1179 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1180 R_gl_draw_quad(0, 0, cw, h);
1183 R_gl_setclip(x, y, w, h);
1184 glTranslatef(-x + cw, 0, 0);
1185 R_gl_draw_image(&stone, 0, 0, 0);
1186 int i = stone.h;
1187 while (i < h) {
1188 R_gl_draw_image(&stone2, 0, i, 0);
1189 i += stone2.h;
1191 if (p->drawst & PL_DRAWAIR) {
1192 if (p->air < PL_AIR) {
1193 int a = min(max(p->air, 0), MAXAIR) * 100 / MAXAIR;
1194 glDisable(GL_BLEND);
1195 R_gl_set_color(0xC8);
1196 R_gl_draw_quad(10, 49, a, 2);
1199 if (p->drawst & PL_DRAWLIFE) {
1200 Z_gotoxy(10, 7);
1201 Z_printhf("%3d%%", p->life);
1203 if (p->drawst & PL_DRAWARMOR) {
1204 Z_gotoxy(10, 7 + 19);
1205 Z_printhf("%3d%%", p->armor);
1207 if (p->drawst & PL_DRAWWPN) {
1208 switch(p->wpn) {
1209 case 2:
1210 case 5:
1211 i = p->ammo;
1212 break;
1213 case 3:
1214 case 4:
1215 case 9:
1216 i = p->shel;
1217 break;
1218 case 6:
1219 i = p->rock;
1220 break;
1221 case 7:
1222 case 8:
1223 i = p->cell;
1224 break;
1225 case 10:
1226 i = p->fuel;
1227 break;
1228 default:
1229 i = -1;
1230 break;
1232 // weapon
1233 if (p->wpn >= 0) {
1234 R_gl_draw_image(&sth[12 + p->wpn], st - 88, 58 + 19, 0);
1236 // ammo
1237 if (p->wpn >= 2) {
1238 Z_gotoxy(st - 10 - 5 * 14, 58 + 2);
1239 Z_printhf("%5d", i);
1242 if (p->drawst & PL_DRAWFRAG && g_dm) {
1243 Z_gotoxy(st - 5 - 5 * 14, 77 + 5);
1244 Z_printhf("%5d", p->frag);
1246 if (p->drawst & PL_DRAWKEYS) {
1247 int x, k, n;
1248 for (k = p->keys >> 4, n = 0, x = st - 75; n < 3; n++, k >>= 1, x += 9) {
1249 if (k & 1) {
1250 R_gl_draw_image(&keys[n], x, 91, 0);
1254 if (p->drawst & PL_DRAWLIVES && !_2pl) {
1255 Z_gotoxy(st - 35, 17);
1256 Z_printhf("%d", p->lives);
1258 glPopMatrix();
1261 /* --- Game --- */
1263 static void pl_info (player_t *p, int x, int y) {
1264 dword t = p->kills * 10920 / g_time;
1265 Z_gotoxy(x + 25, y); Z_printbf("KILLS");
1266 Z_gotoxy(x + 25, y + 15); Z_printbf("KPM");
1267 Z_gotoxy(x + 25, y + 30); Z_printbf("SECRETS %u / %u", p->secrets, sw_secrets);
1268 Z_gotoxy(x + 255, y); Z_printbf("%u", p->kills);
1269 Z_gotoxy(x + 255, y + 15); Z_printbf("%u.%u", t / 10, t % 10);
1272 static void R_draw_intermission (void) {
1273 int cx = SCRW / 2;
1274 word hr, mn, sc, h;
1275 Z_gotoxy(cx - 14*12/2, 20);
1276 Z_printbf("LEVEL COMPLETE");
1277 Z_calc_time(g_time, &hr, &mn, &sc);
1278 Z_gotoxy(cx - 12*12/2, 40);
1279 Z_printbf("TIME %u:%02u:%02u", hr, mn, sc);
1280 h = 40 + SCRH / 10;
1281 if (_2pl) {
1282 Z_gotoxy(cx - 10*12/2, h);
1283 Z_printbf("PLAYER ONE");
1284 h += 20;
1286 pl_info(&pl1, cx - 160, h);
1287 if (_2pl) {
1288 h += 30 + SCRH / 10;
1289 Z_gotoxy(cx - 10*12/2, h);
1290 Z_printbf("PLAYER TWO");
1291 h += 20;
1292 pl_info(&pl2, cx - 160, h);
1296 static void W_act (void) {
1297 int i, a;
1298 if (g_time % 3 == 0) {
1299 for (i = 1; i < max_textures; i++) {
1300 a = walani[i];
1301 if (a != 0) {
1302 anic[a]++;
1303 if (anip[a][anic[a]].res == -1) {
1304 anic[a] = 0;
1306 walp[i] = anip[a][anic[a]];
1312 void R_draw (void) {
1313 W_act();
1314 glClearColor(0, 0, 0, 1);
1315 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1316 glEnable(GL_SCISSOR_TEST);
1317 R_gl_setmatrix();
1318 switch (g_st) {
1319 case GS_ENDANIM:
1320 case GS_END2ANIM:
1321 case GS_DARKEN:
1322 case GS_BVIDEO:
1323 case GS_EVIDEO:
1324 case GS_END3ANIM:
1325 break;
1326 case GS_TITLE:
1327 R_gl_draw_image_ext(&scrnh[0], 0, 0, SCRW, SCRH);
1328 break;
1329 case GS_INTER:
1330 R_gl_draw_image_ext(&scrnh[1], 0, 0, SCRW, SCRH);
1331 R_draw_intermission();
1332 break;
1333 case GS_ENDSCR:
1334 R_gl_draw_image_ext(&scrnh[2], 0, 0, SCRW, SCRH);
1335 break;
1336 case GS_GAME:
1337 if (_2pl) {
1338 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH / 2);
1339 R_draw_player_view(&pl2, 0, SCRH / 2, SCRW, SCRH / 2);
1340 } else {
1341 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH);
1343 R_gl_setclip(0, 0, SCRW, SCRH);
1344 break;
1346 GM_draw();
1347 SDL_GL_SwapBuffers();
1350 void R_alloc (void) {
1351 char s[10];
1352 int i, j, n;
1353 logo("R_alloc: load graphics\n");
1354 /* Game */
1355 scrnh[0] = R_gl_loadimage("TITLEPIC");
1356 assert(scrnh[0].n);
1357 scrnh[1] = R_gl_loadimage("INTERPIC");
1358 scrnh[2] = R_gl_loadimage("ENDPIC");
1359 for (i = 0; i < 2; i++) {
1360 sprintf(s, "LTN%c", '1' + i);
1361 for (j = 0; j < 2; j++) {
1362 ltn[i][j] = Z_getspr(s, j, 0, NULL);
1365 /* Smoke */
1366 for (i = 0; i < SMSN; i++) {
1367 smk_spr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_smoke_spr);
1369 for (i = 0; i < FLSN; i++) {
1370 smk_fspr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_flame_spr);
1372 /* Effects */
1373 for (i = 0; i < 10; i++) {
1374 fx_spr[i] = Z_getspr("TFOG", i, 0, fx_sprd + i);
1376 for (; i < 15; i++) {
1377 fx_spr[i] = Z_getspr("IFOG", i - 10, 0, fx_sprd + i);
1379 /* Weapons */
1380 for (i = 0; i < 4; i++) {
1381 wp_spr[i * 2] = Z_getspr("MISL", i, 1, wp_sprd + i * 2);
1382 wp_spr[i * 2 + 1] = Z_getspr("MISL", i, 2, wp_sprd + i * 2 + 1);
1384 for (; i < 6; i++) {
1385 wp_spr[i * 2] = Z_getspr("PLSS", i - 4, 1, wp_sprd + i * 2);
1386 wp_spr[i * 2 + 1] = Z_getspr("PLSS", i - 4, 2, wp_sprd + i * 2 + 1);
1388 for (; i < 11; i++) {
1389 wp_spr[i * 2] = Z_getspr("PLSE", i - 6, 1, wp_sprd + i * 2);
1390 wp_spr[i * 2 + 1] = Z_getspr("PLSE", i - 6, 2, wp_sprd + i * 2 + 1);
1392 for (; i < 13; i++) {
1393 wp_spr[i * 2] = Z_getspr("APLS", i - 11, 1, wp_sprd + i * 2);
1394 wp_spr[i * 2 + 1] = Z_getspr("APLS", i - 11, 2, wp_sprd + i * 2 + 1);
1396 for (; i < 18; i++) {
1397 wp_spr[i * 2] = Z_getspr("APBX", i - 13, 1, wp_sprd + i * 2);
1398 wp_spr[i * 2 + 1] = Z_getspr("APBX", i - 13, 2, wp_sprd + i * 2 + 1);
1400 for(; i < 20; i++) {
1401 wp_spr[i * 2] = Z_getspr("BFS1", i - 18, 1, wp_sprd + i * 2);
1402 wp_spr[i * 2 + 1] = Z_getspr("BFS1", i - 18, 2, wp_sprd + i * 2 + 1);
1404 for (; i < 26; i++) {
1405 wp_spr[i * 2] = Z_getspr("BFE1", i - 20, 1, wp_sprd + i * 2);
1406 wp_spr[i * 2 + 1] = Z_getspr("BFE1", i - 20, 2, wp_sprd + i * 2 + 1);
1408 for (; i < 30; i++) {
1409 wp_spr[i * 2] = Z_getspr("BFE2", i - 26, 1, wp_sprd + i * 2);
1410 wp_spr[i * 2 + 1] = Z_getspr("BFE2", i - 26, 2, wp_sprd + i * 2 + 1);
1412 for (; i < 32; i++) {
1413 wp_spr[i * 2] = Z_getspr("MISL", i - 30 + 4, 1, wp_sprd + i * 2);
1414 wp_spr[i * 2 + 1] = Z_getspr("MISL", i - 30 + 4, 2, wp_sprd + i * 2 + 1);
1416 for (; i < 37; i++) {
1417 wp_spr[i * 2] = Z_getspr("BAL1", i - 32, 1, wp_sprd + i * 2);
1418 wp_spr[i * 2 + 1] = Z_getspr("BAL1", i - 32, 2, wp_sprd + i * 2 + 1);
1420 for (; i < 42; i++) {
1421 wp_spr[i * 2] = Z_getspr("BAL7", i - 37, 1, wp_sprd + i * 2);
1422 wp_spr[i * 2 + 1] = Z_getspr("BAL7", i - 37, 2, wp_sprd + i * 2 + 1);
1424 for (; i < 47; i++) {
1425 wp_spr[i * 2] = Z_getspr("BAL2", i - 42, 1, wp_sprd + i * 2);
1426 wp_spr[i * 2 + 1] = Z_getspr("BAL2", i - 42, 2, wp_sprd + i * 2 + 1);
1428 for (; i < 49; i++) {
1429 wp_spr[i * 2] = Z_getspr("MANF", i - 47, 1, wp_sprd + i * 2);
1430 wp_spr[i * 2 + 1] = Z_getspr("MANF", i - 47, 2, wp_sprd + i * 2 + 1);
1432 /* Items */
1433 static const char snm[18][4] = {
1434 "CLIP", "SHEL", "ROCK", "CELL", "AMMO", "SBOX", "BROK", "CELP",
1435 "STIM", "MEDI", "BPAK",
1436 "CSAW", "SHOT", "SGN2", "MGUN", "LAUN", "PLAS", "BFUG"
1437 };
1438 static const char n4[4][4] = {
1439 "SOUL", "SMRT", "SMGT", "SMBT"
1440 };
1441 static const char n3[2][4] = {
1442 "GOR1", "FCAN"
1443 };
1444 for (i = 0; i < 18; i++) {
1445 item_spr[i] = Z_getspr(snm[i], 0, 0, item_sprd + i);
1447 for (; i < 20; i++) {
1448 item_spr[i] = Z_getspr("ARM1", i - 18, 0, item_sprd + i);
1449 item_spr[i + 2] = Z_getspr("ARM2", i - 18, 0, item_sprd + i);
1451 i+=2;
1452 for (; i < 26; i++) {
1453 item_spr[i] = Z_getspr("MEGA", i - 22, 0, item_sprd + i);
1455 for (; i < 30; i++) {
1456 item_spr[i] = Z_getspr("PINV", i - 26, 0, item_sprd + i);
1458 item_spr[30] = Z_getspr("AQUA", 0, 0, item_sprd + 30);
1459 item_spr[31] = Z_getspr("KEYR", 0, 0, item_sprd + 31);
1460 item_spr[32] = Z_getspr("KEYG", 0, 0, item_sprd + 32);
1461 item_spr[33] = Z_getspr("KEYB", 0, 0, item_sprd + 33);
1462 item_spr[34] = Z_getspr("SUIT", 0, 0, item_sprd + 34);
1463 for (n = 35, j = 0; j < 4; j++) {
1464 for (i = 0; i < 4; i++, n++) {
1465 item_spr[n] = Z_getspr(n4[j], i, 0, item_sprd + n);
1468 for (j = 0; j < 2; j++) {
1469 for (i = 0; i < 3; i++, n++) {
1470 item_spr[n] = Z_getspr(n3[j], i, 0, item_sprd + n);
1473 item_spr[57] = Z_getspr("GUN2", 0, 0, item_sprd + 57);
1474 /* Player */
1475 for (i = 0; i < 27; i++) {
1476 plr_spr[i * 2] = Z_getspr("PLAY", i, 1, plr_sprd + i * 2);
1477 plr_msk[i * 2] = R_gl_get_special_spr("PLAY", i, 1, &R_extract_mask_spr);
1478 plr_spr[i * 2 + 1] = Z_getspr("PLAY", i, 2, plr_sprd + i * 2 + 1);
1479 plr_msk[i * 2 + 1] = R_gl_get_special_spr("PLAY", i, 2, &R_extract_mask_spr);
1481 strncpy(s, "PWPx", 4);
1482 for (i = 1; i < 11; i++) {
1483 s[3] = (i < 10 ? '0' : 'A' - 10) + i;
1484 for (j = 0; j < 6; j++) {
1485 plr_wpn[i][j] = Z_getspr(s, j, 1, NULL);
1488 /* Monsters */
1489 static const char msn[MN_TN][4] = {
1490 "SARG", "TROO", "POSS", "SPOS", "CYBR", "CPOS", "BOSS", "BOS2", "HEAD", "SKUL",
1491 "PAIN", "SPID", "BSPI", "FATT", "SKEL", "VILE", "FISH", "BAR1", "ROBO", "PLAY"
1492 };
1493 static const int mms[MN_TN] = {
1494 14*2, 21*2, 21*2, 21*2, 16*2, 20*2, 15*2, 15*2, 12*2, 11*2,
1495 13*2, 19*2, 16*2, 20*2, 17*2, 29*2, 6*2, 2*2, 17*2, 23*2
1496 };
1497 mn_sgun[0] = Z_getspr("PWP4", 0, 1, NULL);
1498 mn_sgun[1] = Z_getspr("PWP4", 1, 1, NULL);
1499 for (j = 0; j < MN_TN; j++) {
1500 for (i = 0; i < mms[j]; i++) {
1501 mn_spr[j][i] = Z_getspr(msn[j], i / 2, (i & 1) + 1, &mn_sprd[j][i]);
1502 if (j == MN_MAN - 1) {
1503 mn_man_msk[i] = R_gl_get_special_spr(msn[j], i / 2, (i & 1) + 1, &R_extract_mask_spr);
1506 if (j == MN_BARREL - 1) {
1507 for (i = 4; i < 14; i++) {
1508 mn_spr[j][i] = Z_getspr("BEXP", i / 2 - 2, (i & 1) + 1, &mn_sprd[j][i]);
1512 for (i = 0; i < 8; i++) {
1513 mn_fspr[i] = Z_getspr("FIRE", i, 0, NULL);
1515 pl_spr[0] = Z_getspr("PLAY", 'N' - 'A', 0, NULL);
1516 pl_msk[0] = R_gl_get_special_spr("PLAY", 'N' - 'A', 0, &R_extract_mask_spr);
1517 pl_spr[1] = Z_getspr("PLAY", 'W' - 'A', 0, NULL);
1518 pl_msk[1] = R_gl_get_special_spr("PLAY", 'W' - 'A', 0, &R_extract_mask_spr);
1519 /* Misc */
1520 static const char mnm[22][8]={
1521 "STTNUM0", "STTNUM1", "STTNUM2", "STTNUM3", "STTNUM4",
1522 "STTNUM5", "STTNUM6", "STTNUM7", "STTNUM8", "STTNUM9",
1523 "STTMINUS", "STTPRCNT",
1524 "FISTA0", "CSAWA0", "PISTA0", "SHOTA0", "SGN2A0", "MGUNA0", "LAUNA0",
1525 "PLASA0", "BFUGA0", "GUN2A0"
1526 };
1527 stone = R_gl_loadimage("STONE");
1528 stone2 = R_gl_loadimage("STONE2");
1529 keys[0] = R_gl_loadimage("KEYRA0");
1530 keys[1] = R_gl_loadimage("KEYGA0");
1531 keys[2] = R_gl_loadimage("KEYBA0");
1532 for (i = 0; i < 22; i++) {
1533 sth[i] = R_gl_loadimage(mnm[i]);
1535 strcpy(s, "STBF_*");
1536 for (i = '!'; i < 160; i++) {
1537 s[5] = i;
1538 bfh[i - '!'] = R_gl_getimage(F_findres(s));
1540 for (i = '!'; i < 160; i++) {
1541 sprintf(s, "STCFN%03d", i);
1542 sfh[i - '!'] = R_gl_getimage(F_findres(s));
1544 strcpy(s, "WINUM*");
1545 for (i = '0'; i <= '9'; i++) {
1546 s[5] = i;
1547 bfh[i - '!'] = R_gl_loadimage(s);
1549 bfh[':' - '!'] = R_gl_loadimage("WICOLON");
1550 // menu
1551 msklh[0] = R_gl_loadimage("M_SKULL1");
1552 msklh[1] = R_gl_loadimage("M_SKULL2");
1553 mbarl = R_gl_loadimage("M_THERML");
1554 mbarm = R_gl_loadimage("M_THERMM");
1555 mbarr = R_gl_loadimage("M_THERMR");
1556 mbaro = R_gl_loadimage("M_THERMO");
1557 mslotl = R_gl_loadimage("M_LSLEFT");
1558 mslotm = R_gl_loadimage("M_LSCNTR");
1559 mslotr = R_gl_loadimage("M_LSRGHT");
1560 // walls
1561 for (i = 1; i < ANIT; i++) {
1562 for (j = 0; anm[i - 1][j]; j++) {
1563 anip[i][j] = R_gl_loadimage(anm[i - 1][j]);
1565 for(; j < 5; j++) {
1566 anip[i][j] = (image) {
1567 .n = NULL,
1568 .w = 8,
1569 .h = 8,
1570 .res = -1,
1571 };
1576 void R_init (void) {
1577 Uint32 flags = SDL_OPENGL;
1578 if (fullscreen) {
1579 flags = flags | SDL_FULLSCREEN;
1581 if (SCRW <= 0 || SCRH <= 0) {
1582 ERR_failinit("Invalid screen size %ix%i\n", SCRW, SCRH);
1584 if (surf == NULL) {
1585 R_init_playpal(); // only onece
1587 surf = SDL_SetVideoMode(SCRW, SCRH, 0, flags);
1588 if (surf == NULL) {
1589 ERR_failinit("Unable to set video mode: %s\n", SDL_GetError());
1591 root = R_cache_new();
1592 assert(root);
1593 R_alloc();
1596 void R_done (void) {
1597 // do nothing
1600 void R_setgamma (int g) {
1601 gamma = g < 0 ? 0 : (g > 4 ? 4 : g);
1604 int R_getgamma (void) {
1605 return gamma;
1608 void R_toggle_fullscreen (void) {
1609 fullscreen = !fullscreen;
1610 if (surf) {
1611 R_init(); // recreate window
1615 void R_get_name (int n, char s[8]) {
1616 assert(n >= 0 && n < 256);
1617 if (walp[n].res == -1) {
1618 memset(s, 0, 8);
1619 } else if (walp[n].res == -2) {
1620 memcpy(s, "_WATER_", 8);
1621 s[7] = '0' + (intptr_t)walp[n].n - 1;
1622 } else if (walani[n] > 0) {
1623 memcpy(s, anm[walani[n] - 1][0], 8);
1624 } else {
1625 F_getresname(s, walp[n].res & 0x7FFF);
1629 static short getani (char n[8]) {
1630 short i = 0;
1631 while (i < ANIT && strncasecmp(n, anm[i][0], 8) != 0) {
1632 i++;
1634 return i < ANIT ? i + 1 : 0;
1637 int R_get_special_id (int n) {
1638 assert(n >= 0 && n <= 256);
1639 return walp[n].res == -2 ? (intptr_t)walp[n].n : -1;
1642 void R_begin_load (void) {
1643 int i;
1644 for (i = 0; i < 256; i++) {
1645 if (walp[i].n != NULL && walp[i].res >= 0 && walani[i] == 0) {
1646 R_gl_free_image(&walp[i]);
1648 memset(&walp[i], 0, sizeof(image));
1649 walp[i].res = -1;
1650 walswp[i] = i;
1651 walani[i] = 0;
1653 memset(anic, 0, sizeof(anic));
1654 max_wall_width = 0;
1655 max_wall_height = 0;
1656 max_textures = 1;
1659 void R_load (char s[8], int f) {
1660 assert(max_textures < 256);
1661 if (!s[0]) {
1662 walp[max_textures] = (image) {
1663 .n = NULL,
1664 .x = 0,
1665 .y = 0,
1666 .w = 0,
1667 .h = 0,
1668 .res = -1,
1669 };
1670 } else if (strncasecmp(s, "_WATER_", 7) == 0) {
1671 walp[max_textures] = (image) {
1672 .n = (void*)(s[7] - '0' + 1),
1673 .x = 0,
1674 .y = 0,
1675 .w = 8,
1676 .h = 8,
1677 .res = -2,
1678 };
1679 } else {
1680 walp[max_textures] = R_gl_loadimage(s);
1681 if (f) {
1682 walp[max_textures].res |= 0x8000;
1684 if (s[0] == 'S' && s[1] == 'W' && s[4] == '_') {
1685 walswp[max_textures] = 0;
1687 walani[max_textures] = getani(s);
1689 max_wall_width = max(max_wall_width, walp[max_textures].w);
1690 max_wall_height = max(max_wall_height, walp[max_textures].h);
1691 max_textures++;
1694 void R_end_load (void) {
1695 int i, j, k, g;
1696 char s[8];
1697 j = max_textures;
1698 for (i = 1; i < 256 && j < 256; i++) {
1699 if (walswp[i] == 0) {
1700 R_get_name(i, s);
1701 s[5] ^= 1;
1702 g = F_getresid(s) | (walp[i].res & 0x8000);
1703 k = 1;
1704 while (k < 256 && walp[k].res != g) {
1705 k += 1;
1707 if (k >= 256) {
1708 k = j;
1709 j += 1;
1710 walp[k] = R_gl_getimage(g);
1711 walf[k] = g & 0x8000 ? 1 : 0;
1713 walswp[i] = k;
1714 walswp[k] = i;
1719 void R_loadsky (int sky) {
1720 char s[6];
1721 logo("R_loadsky(%i)\n", sky);
1722 strcpy(s, "RSKYx");
1723 s[4] = '0' + sky;
1724 R_gl_free_image(&horiz);
1725 horiz = R_gl_loadimage(s);