]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
updated to version 1.50, build 75.
[xonotic/darkplaces.git] / gl_draw.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 // draw.c -- this is the only file outside the refresh that touches the
22 // vid buffer
23
24 #include "quakedef.h"
25
26 #define GL_COLOR_INDEX8_EXT     0x80E5
27
28 extern unsigned char d_15to8table[65536];
29
30 cvar_t          qsg_version = {"qsg_version", "1"};
31 cvar_t          gl_conalpha = {"gl_conalpha", "1"};
32
33 byte            *draw_chars;                            // 8*8 graphic characters
34 qpic_t          *draw_disc;
35
36 //int                   translate_texture;
37 int                     char_texture;
38
39 typedef struct
40 {
41         int             texnum;
42         float   sl, tl, sh, th;
43 } glpic_t;
44
45 byte            conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)];
46 qpic_t          *conback = (qpic_t *)&conback_buffer;
47
48 /*
49 =============================================================================
50
51   scrap allocation
52
53   Allocate all the little status bar obejcts into a single texture
54   to crutch up stupid hardware / drivers
55
56 =============================================================================
57 */
58
59 #define MAX_SCRAPS              2
60 #define BLOCK_WIDTH             256
61 #define BLOCK_HEIGHT    256
62
63 int                     scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH];
64 byte            scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4];
65 qboolean        scrap_dirty;
66
67 // returns a texture number and the position inside it
68 int Scrap_AllocBlock (int w, int h, int *x, int *y)
69 {
70         int             i, j;
71         int             best, best2;
72         int             texnum;
73
74         for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
75         {
76                 best = BLOCK_HEIGHT;
77
78                 for (i=0 ; i<BLOCK_WIDTH-w ; i++)
79                 {
80                         best2 = 0;
81
82                         for (j=0 ; j<w ; j++)
83                         {
84                                 if (scrap_allocated[texnum][i+j] >= best)
85                                         break;
86                                 if (scrap_allocated[texnum][i+j] > best2)
87                                         best2 = scrap_allocated[texnum][i+j];
88                         }
89                         if (j == w)
90                         {       // this is a valid spot
91                                 *x = i;
92                                 *y = best = best2;
93                         }
94                 }
95
96                 if (best + h > BLOCK_HEIGHT)
97                         continue;
98
99                 for (i=0 ; i<w ; i++)
100                         scrap_allocated[texnum][*x + i] = best + h;
101
102                 return texnum;
103         }
104
105         Sys_Error ("Scrap_AllocBlock: full");
106         return 0;
107 }
108
109 int     scrap_uploads;
110 int scraptexnum[MAX_SCRAPS];
111
112 void Scrap_Upload (void)
113 {
114         int             texnum;
115
116         scrap_uploads++;
117
118         for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
119                 scraptexnum[texnum] = GL_LoadTexture (va("scrapslot%d", texnum), BLOCK_WIDTH, BLOCK_HEIGHT, scrap_texels[texnum], false, true, 1);
120         scrap_dirty = false;
121 }
122
123 //=============================================================================
124 /* Support Routines */
125
126 typedef struct cachepic_s
127 {
128         char            name[MAX_QPATH];
129         qpic_t          pic;
130         byte            padding[32];    // for appended glpic
131 } cachepic_t;
132
133 #define MAX_CACHED_PICS         128
134 cachepic_t      menu_cachepics[MAX_CACHED_PICS];
135 int                     menu_numcachepics;
136
137 byte            menuplyr_pixels[4096];
138
139 int             pic_texels;
140 int             pic_count;
141
142 int GL_LoadPicTexture (qpic_t *pic);
143
144 qpic_t *Draw_PicFromWad (char *name)
145 {
146         qpic_t  *p;
147         glpic_t *gl;
148
149         p = W_GetLumpName (name);
150         gl = (glpic_t *)p->data;
151
152         // load little ones into the scrap
153         if (p->width < 64 && p->height < 64)
154         {
155                 int             x, y;
156                 int             i, j, k;
157                 int             texnum;
158
159                 texnum = Scrap_AllocBlock (p->width, p->height, &x, &y);
160                 scrap_dirty = true;
161                 k = 0;
162                 for (i=0 ; i<p->height ; i++)
163                         for (j=0 ; j<p->width ; j++, k++)
164                                 scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k];
165                 if (!scraptexnum[texnum])
166                         scraptexnum[texnum] = GL_LoadTexture (va("scrapslot%d", texnum), BLOCK_WIDTH, BLOCK_HEIGHT, scrap_texels[texnum], false, true, 1);
167                 gl->texnum = scraptexnum[texnum];
168                 gl->sl = (x+0.01)/(float)BLOCK_WIDTH;
169                 gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH;
170                 gl->tl = (y+0.01)/(float)BLOCK_WIDTH;
171                 gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH;
172
173                 pic_count++;
174                 pic_texels += p->width*p->height;
175         }
176         else
177         {
178                 gl->texnum = GL_LoadPicTexture (p);
179                 gl->sl = 0;
180                 gl->sh = 1;
181                 gl->tl = 0;
182                 gl->th = 1;
183         }
184         return p;
185 }
186
187
188 /*
189 ================
190 Draw_CachePic
191 ================
192 */
193 qpic_t  *Draw_CachePic (char *path)
194 {
195         cachepic_t      *pic;
196         int                     i;
197         qpic_t          *dat;
198         glpic_t         *gl;
199
200         for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++)
201                 if (!strcmp (path, pic->name))
202                         return &pic->pic;
203
204         if (menu_numcachepics == MAX_CACHED_PICS)
205                 Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
206         menu_numcachepics++;
207         strcpy (pic->name, path);
208
209 //
210 // load the pic from disk
211 //
212         dat = (qpic_t *)COM_LoadTempFile (path, false);
213         if (!dat)
214                 Sys_Error ("Draw_CachePic: failed to load %s", path);
215         SwapPic (dat);
216
217         // HACK HACK HACK --- we need to keep the bytes for
218         // the translatable player picture just for the menu
219         // configuration dialog
220         if (!strcmp (path, "gfx/menuplyr.lmp"))
221                 memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
222
223         pic->pic.width = dat->width;
224         pic->pic.height = dat->height;
225
226         gl = (glpic_t *)pic->pic.data;
227         gl->texnum = GL_LoadPicTexture (dat);
228         gl->sl = 0;
229         gl->sh = 1;
230         gl->tl = 0;
231         gl->th = 1;
232
233         return &pic->pic;
234 }
235
236 /*
237 void Draw_CharToConback (int num, byte *dest)
238 {
239         int             row, col;
240         byte    *source;
241         int             drawline;
242         int             x;
243
244         row = num>>4;
245         col = num&15;
246         source = draw_chars + (row<<10) + (col<<3);
247
248         drawline = 8;
249
250         while (drawline--)
251         {
252                 for (x=0 ; x<8 ; x++)
253                         if (source[x] != 255)
254                                 dest[x] = 0x60 + source[x];
255                 source += 128;
256                 dest += 320;
257         }
258
259 }
260 */
261
262 extern void LoadSky_f(void);
263
264 extern char *QSG_EXTENSIONS;
265
266 /*
267 ===============
268 Draw_Init
269 ===============
270 */
271 void rmain_registercvars();
272 extern int buildnumber;
273
274 void gl_draw_start()
275 {
276         int             i;
277         glpic_t *gl;
278
279         // load the console background and the charset
280         // by hand, because we need to write the version
281         // string into the background before turning
282         // it into a texture
283         char_texture = loadtextureimage ("conchars", 0, 0, false, false);
284         if (!char_texture)
285         {
286                 draw_chars = W_GetLumpName ("conchars");
287                 for (i=0 ; i<256*64 ; i++)
288                         if (draw_chars[i] == 0)
289                                 draw_chars[i] = 255;    // proper transparent color
290
291                 // now turn them into textures
292                 char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1);
293         }
294
295         gl = (glpic_t *)conback->data;
296         gl->texnum = loadtextureimage("gfx/conback", 0, 0, false, false);
297         gl->sl = 0;
298         gl->sh = 1;
299         gl->tl = 0;
300         gl->th = 1;
301         conback->width = vid.width;
302         conback->height = vid.height;
303
304         memset(scraptexnum, 0, sizeof(scraptexnum));
305
306         // get the other pics we need
307         draw_disc = Draw_PicFromWad ("disc");
308 }
309
310 void gl_draw_shutdown()
311 {
312 }
313
314 char engineversion[40];
315 int engineversionx, engineversiony;
316
317 extern void GL_Textures_Init();
318 void GL_Draw_Init (void)
319 {
320         int i;
321         Cvar_RegisterVariable (&qsg_version);
322         Cvar_RegisterVariable (&gl_conalpha);
323
324         Cmd_AddCommand ("loadsky", &LoadSky_f);
325
326 #ifdef NEHAHRA
327 #if defined(__linux__)
328         sprintf (engineversion, "DPNehahra Linux   GL %.2f build %3i", (float) VERSION, buildnumber);
329 #elif defined(WIN32)
330         sprintf (engineversion, "DPNehahra Windows GL %.2f build %3i", (float) VERSION, buildnumber);
331 #else
332         sprintf (engineversion, "DPNehahra Unknown GL %.2f build %3i", (float) VERSION, buildnumber);
333 #endif
334 #else
335 #if defined(__linux__)
336         sprintf (engineversion, "DarkPlaces Linux   GL %.2f build %3i", (float) VERSION, buildnumber);
337 #elif defined(WIN32)
338         sprintf (engineversion, "DarkPlaces Windows GL %.2f build %3i", (float) VERSION, buildnumber);
339 #else
340         sprintf (engineversion, "DarkPlaces Unknown GL %.2f build %3i", (float) VERSION, buildnumber);
341 #endif
342 #endif
343         for (i = 0;i < 40 && engineversion[i];i++)
344                 engineversion[i] += 0x80; // shift to orange
345         engineversionx = vid.width - strlen(engineversion) * 8 - 8;
346         engineversiony = vid.height - 8;
347
348         GL_Textures_Init();
349         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown);
350 }
351
352 /*
353 ================
354 Draw_Character
355
356 Draws one 8*8 graphics character with 0 being transparent.
357 It can be clipped to the top of the screen to allow the console to be
358 smoothly scrolled off.
359 ================
360 */
361 void Draw_Character (int x, int y, int num)
362 {
363         int                             row, col;
364         float                   frow, fcol, size;
365
366         if (num == 32)
367                 return;         // space
368
369         num &= 255;
370         
371         if (y <= -8)
372                 return;                 // totally off screen
373
374         row = num>>4;
375         col = num&15;
376
377         frow = row*0.0625;
378         fcol = col*0.0625;
379         size = 0.0625;
380
381         glBindTexture(GL_TEXTURE_2D, char_texture);
382         // LordHavoc: NEAREST mode on text if not scaling up
383         if ((int) vid.width < glwidth)
384         {
385                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
386                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
387         }
388
389         glColor3f(1,1,1);
390         glBegin (GL_QUADS);
391         glTexCoord2f (fcol, frow);
392         glVertex2f (x, y);
393         glTexCoord2f (fcol + size, frow);
394         glVertex2f (x+8, y);
395         glTexCoord2f (fcol + size, frow + size);
396         glVertex2f (x+8, y+8);
397         glTexCoord2f (fcol, frow + size);
398         glVertex2f (x, y+8);
399         glEnd ();
400
401         // LordHavoc: revert to LINEAR mode
402         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
403         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
404 }
405
406 /*
407 ================
408 Draw_String
409 ================
410 */
411 // LordHavoc: sped this up a lot, and added maxlen
412 void Draw_String (int x, int y, char *str, int maxlen)
413 {
414         int num;
415         float frow, fcol;
416         if (y <= -8 || y >= (int) vid.height || x >= (int) vid.width || *str == 0) // completely offscreen or no text to print
417                 return;
418         if (maxlen < 1)
419                 maxlen = strlen(str);
420         else if (maxlen > (int) strlen(str))
421                 maxlen = strlen(str);
422         glBindTexture(GL_TEXTURE_2D, char_texture);
423
424         // LordHavoc: NEAREST mode on text if not scaling up
425         if ((int) vid.width < glwidth)
426         {
427                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
428                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
429         }
430
431         glColor3f(1,1,1);
432         glBegin (GL_QUADS);
433         while (maxlen-- && x < (int) vid.width) // stop rendering when out of characters or room
434         {
435                 if ((num = *str++) != 32) // skip spaces
436                 {
437                         frow = (float) ((int) num >> 4)*0.0625;
438                         fcol = (float) ((int) num & 15)*0.0625;
439                         glTexCoord2f (fcol, frow);
440                         glVertex2f (x, y);
441                         glTexCoord2f (fcol + 0.0625, frow);
442                         glVertex2f (x+8, y);
443                         glTexCoord2f (fcol + 0.0625, frow + 0.0625);
444                         glVertex2f (x+8, y+8);
445                         glTexCoord2f (fcol, frow + 0.0625);
446                         glVertex2f (x, y+8);
447                 }
448                 x += 8;
449         }
450         glEnd ();
451
452         // LordHavoc: revert to LINEAR mode
453         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
454         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
455 }
456
457 void Draw_GenericPic (int texnum, float red, float green, float blue, float alpha, float x, float y, float width, float height)
458 {
459         glDisable(GL_ALPHA_TEST);
460 //      glEnable (GL_BLEND);
461         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
462 //      glCullFace(GL_FRONT);
463         glColor4f(red,green,blue,alpha);
464         glBindTexture(GL_TEXTURE_2D, texnum);
465         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
466         glBegin (GL_QUADS);
467         glTexCoord2f (0, 0);
468         glVertex2f (x, y);
469         glTexCoord2f (1, 0);
470         glVertex2f (x+width, y);
471         glTexCoord2f (1, 1);
472         glVertex2f (x+width, y+height);
473         glTexCoord2f (0, 1);
474         glVertex2f (x, y+height);
475         glEnd ();
476         glColor3f(1,1,1);
477 //      glEnable(GL_ALPHA_TEST);
478 //      glDisable (GL_BLEND);
479 }
480
481 /*
482 =============
483 Draw_AlphaPic
484 =============
485 */
486 void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
487 {
488         glpic_t                 *gl;
489
490         if (scrap_dirty)
491                 Scrap_Upload ();
492         gl = (glpic_t *)pic->data;
493 //      glDisable(GL_ALPHA_TEST);
494 //      glEnable (GL_BLEND);
495 //      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
496 //      glCullFace(GL_FRONT);
497         glColor4f(0.8,0.8,0.8,alpha);
498         glBindTexture(GL_TEXTURE_2D, gl->texnum);
499 //      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
500         glBegin (GL_QUADS);
501         glTexCoord2f (gl->sl, gl->tl);
502         glVertex2f (x, y);
503         glTexCoord2f (gl->sh, gl->tl);
504         glVertex2f (x+pic->width, y);
505         glTexCoord2f (gl->sh, gl->th);
506         glVertex2f (x+pic->width, y+pic->height);
507         glTexCoord2f (gl->sl, gl->th);
508         glVertex2f (x, y+pic->height);
509         glEnd ();
510         glColor3f(1,1,1);
511 //      glEnable(GL_ALPHA_TEST);
512 //      glDisable (GL_BLEND);
513 }
514
515
516 /*
517 =============
518 Draw_Pic
519 =============
520 */
521 void Draw_Pic (int x, int y, qpic_t *pic)
522 {
523         glpic_t                 *gl;
524
525         if (scrap_dirty)
526                 Scrap_Upload ();
527         gl = (glpic_t *)pic->data;
528         glColor3f(0.8,0.8,0.8);
529         glBindTexture(GL_TEXTURE_2D, gl->texnum);
530         glBegin (GL_QUADS);
531         glTexCoord2f (gl->sl, gl->tl);
532         glVertex2f (x, y);
533         glTexCoord2f (gl->sh, gl->tl);
534         glVertex2f (x+pic->width, y);
535         glTexCoord2f (gl->sh, gl->th);
536         glVertex2f (x+pic->width, y+pic->height);
537         glTexCoord2f (gl->sl, gl->th);
538         glVertex2f (x, y+pic->height);
539         glEnd ();
540 }
541
542
543 /*
544 =============
545 Draw_TransPic
546 =============
547 */
548 void Draw_TransPic (int x, int y, qpic_t *pic)
549 {
550         if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || (unsigned)(y + pic->height) > vid.height)
551                 Sys_Error ("Draw_TransPic: bad coordinates");
552
553 //      glEnable(GL_BLEND);
554 //      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
555 //      glDisable(GL_ALPHA_TEST);
556         Draw_Pic (x, y, pic);
557 //      glDisable(GL_BLEND);
558 }
559
560
561 /*
562 =============
563 Draw_TransPicTranslate
564
565 Only used for the player color selection menu
566 =============
567 */
568 void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation)
569 {
570         int                             v, u, c;
571         unsigned                trans[64*64], *dest;
572         byte                    *src;
573         int                             p;
574
575         c = pic->width * pic->height;
576
577         dest = trans;
578         for (v=0 ; v<64 ; v++, dest += 64)
579         {
580                 src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width];
581                 for (u=0 ; u<64 ; u++)
582                 {
583                         p = src[(u*pic->width)>>6];
584                         if (p == 255)
585                                 dest[u] = p;
586                         else
587                                 dest[u] =  d_8to24table[translation[p]];
588                 }
589         }
590
591         glBindTexture(GL_TEXTURE_2D, GL_LoadTexture ("translatedplayerpic", 64, 64, (void *) trans, false, true, 1));
592
593         glColor3f(0.8,0.8,0.8);
594         glBegin (GL_QUADS);
595         glTexCoord2f (0, 0);
596         glVertex2f (x, y);
597         glTexCoord2f (1, 0);
598         glVertex2f (x+pic->width, y);
599         glTexCoord2f (1, 1);
600         glVertex2f (x+pic->width, y+pic->height);
601         glTexCoord2f (0, 1);
602         glVertex2f (x, y+pic->height);
603         glEnd ();
604 }
605
606
607 /*
608 ================
609 Draw_ConsoleBackground
610
611 ================
612 */
613 void Draw_ConsoleBackground (int lines)
614 {
615         // LordHavoc: changed alpha
616         //int y = (vid.height >> 1);
617
618         if (lines >= (int) vid.height)
619                 Draw_Pic(0, lines - vid.height, conback);
620         else
621                 Draw_AlphaPic (0, lines - vid.height, conback, gl_conalpha.value*lines/vid.height);
622         //      Draw_AlphaPic (0, lines - vid.height, conback, (float)(1.2 * lines)/y);
623         // LordHavoc: draw version
624         Draw_String(engineversionx, lines - vid.height + engineversiony, engineversion, 9999);
625 }
626
627 /*
628 =============
629 Draw_Fill
630
631 Fills a box of pixels with a single color
632 =============
633 */
634 void Draw_Fill (int x, int y, int w, int h, int c)
635 {
636         glDisable (GL_TEXTURE_2D);
637         glColor3f (host_basepal[c*3]/255.0, host_basepal[c*3+1]/255.0, host_basepal[c*3+2]/255.0);
638
639         glBegin (GL_QUADS);
640
641         glVertex2f (x,y);
642         glVertex2f (x+w, y);
643         glVertex2f (x+w, y+h);
644         glVertex2f (x, y+h);
645
646         glEnd ();
647         glColor3f(1,1,1);
648         glEnable (GL_TEXTURE_2D);
649 }
650 //=============================================================================
651
652 //=============================================================================
653
654 /*
655 ================
656 GL_Set2D
657
658 Setup as if the screen was 320*200
659 ================
660 */
661 void GL_Set2D (void)
662 {
663         glViewport (glx, gly, glwidth, glheight);
664
665         glMatrixMode(GL_PROJECTION);
666     glLoadIdentity ();
667         glOrtho  (0, vid.width, vid.height, 0, -99999, 99999);
668
669         glMatrixMode(GL_MODELVIEW);
670     glLoadIdentity ();
671
672         glDisable (GL_DEPTH_TEST);
673         glDisable (GL_CULL_FACE);
674         glEnable (GL_BLEND); // was Disable
675 //      glEnable (GL_ALPHA_TEST);
676         glDisable (GL_ALPHA_TEST);
677         glEnable(GL_TEXTURE_2D);
678
679         // LordHavoc: added this
680         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
681         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
682
683         glColor3f(1,1,1);
684 }
685
686 // LordHavoc: SHOWLMP stuff
687 #define SHOWLMP_MAXLABELS 256
688 typedef struct showlmp_s
689 {
690         qboolean        isactive;
691         float           x;
692         float           y;
693         char            label[32];
694         char            pic[128];
695 } showlmp_t;
696
697 showlmp_t showlmp[SHOWLMP_MAXLABELS];
698
699 void SHOWLMP_decodehide()
700 {
701         int i;
702         byte *lmplabel;
703         lmplabel = MSG_ReadString();
704         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
705                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
706                 {
707                         showlmp[i].isactive = false;
708                         return;
709                 }
710 }
711
712 void SHOWLMP_decodeshow()
713 {
714         int i, k;
715         byte lmplabel[256], picname[256];
716         float x, y;
717         strcpy(lmplabel,MSG_ReadString());
718         strcpy(picname, MSG_ReadString());
719         x = MSG_ReadByte();
720         y = MSG_ReadByte();
721         k = -1;
722         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
723                 if (showlmp[i].isactive)
724                 {
725                         if (strcmp(showlmp[i].label, lmplabel) == 0)
726                         {
727                                 k = i;
728                                 break; // drop out to replace it
729                         }
730                 }
731                 else if (k < 0) // find first empty one to replace
732                         k = i;
733         if (k < 0)
734                 return; // none found to replace
735         // change existing one
736         showlmp[k].isactive = true;
737         strcpy(showlmp[k].label, lmplabel);
738         strcpy(showlmp[k].pic, picname);
739         showlmp[k].x = x;
740         showlmp[k].y = y;
741 }
742
743 void SHOWLMP_drawall()
744 {
745         int i;
746         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
747                 if (showlmp[i].isactive)
748                         Draw_TransPic(showlmp[i].x, showlmp[i].y, Draw_CachePic(showlmp[i].pic));
749 }
750
751 void SHOWLMP_clear()
752 {
753         int i;
754         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
755                 showlmp[i].isactive = false;
756 }