***map loader generates portals for the map*** (can you tell this is a big deal? :)
[divverent/darkplaces.git] / console.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 // console.c
21
22 #ifdef NeXT
23 #include <libc.h>
24 #endif
25 #ifndef _MSC_VER
26 #ifndef __BORLANDC__
27 #include <unistd.h>
28 #endif
29 #endif
30 #ifdef WIN32
31 #include <io.h>
32 #endif
33 #include <fcntl.h>
34 #include "quakedef.h"
35
36 int             con_linewidth;
37
38 float           con_cursorspeed = 4;
39
40 #define         CON_TEXTSIZE    16384
41
42 qboolean        con_forcedup;           // because no entities to refresh
43
44 int                     con_totallines;         // total lines in console scrollback
45 int                     con_backscroll;         // lines up from bottom to display
46 int                     con_current;            // where next message will be printed
47 int                     con_x;                          // offset in current line for next print
48 char            *con_text=0;
49
50 cvar_t          con_notifytime = {"con_notifytime","3"};                //seconds
51 cvar_t          logfile = {"logfile","0"};
52
53 #define NUM_CON_TIMES 4
54 float           con_times[NUM_CON_TIMES];       // realtime time the line was generated
55                                                                 // for transparent notify lines
56
57 int                     con_vislines;
58
59 qboolean        con_debuglog;
60
61 #define         MAXCMDLINE      256
62 extern  char    key_lines[32][MAXCMDLINE];
63 extern  int             edit_line;
64 extern  int             key_linepos;
65                 
66
67 qboolean        con_initialized;
68
69 int                     con_notifylines;                // scan lines to clear for notify lines
70
71 extern void M_Menu_Main_f (void);
72
73 /*
74 ================
75 Con_ToggleConsole_f
76 ================
77 */
78 void Con_ToggleConsole_f (void)
79 {
80         if (key_dest == key_console)
81         {
82                 if (cls.state == ca_connected)
83                 {
84                         key_dest = key_game;
85                         key_lines[edit_line][1] = 0;    // clear any typing
86                         key_linepos = 1;
87                 }
88                 else
89                 {
90                         M_Menu_Main_f ();
91                 }
92         }
93         else
94                 key_dest = key_console;
95         
96 //      SCR_EndLoadingPlaque ();
97         memset (con_times, 0, sizeof(con_times));
98 }
99
100 /*
101 ================
102 Con_Clear_f
103 ================
104 */
105 void Con_Clear_f (void)
106 {
107         if (con_text)
108                 memset (con_text, ' ', CON_TEXTSIZE);
109 }
110
111                                                 
112 /*
113 ================
114 Con_ClearNotify
115 ================
116 */
117 void Con_ClearNotify (void)
118 {
119         int             i;
120         
121         for (i=0 ; i<NUM_CON_TIMES ; i++)
122                 con_times[i] = 0;
123 }
124
125                                                 
126 /*
127 ================
128 Con_MessageMode_f
129 ================
130 */
131 extern qboolean team_message;
132
133 void Con_MessageMode_f (void)
134 {
135         key_dest = key_message;
136         team_message = false;
137 }
138
139                                                 
140 /*
141 ================
142 Con_MessageMode2_f
143 ================
144 */
145 void Con_MessageMode2_f (void)
146 {
147         key_dest = key_message;
148         team_message = true;
149 }
150
151                                                 
152 /*
153 ================
154 Con_CheckResize
155
156 If the line width has changed, reformat the buffer.
157 ================
158 */
159 void Con_CheckResize (void)
160 {
161         int             i, j, width, oldwidth, oldtotallines, numlines, numchars;
162         char    tbuf[CON_TEXTSIZE];
163
164         width = (vid.width >> 3) - 2;
165
166         if (width == con_linewidth)
167                 return;
168
169         if (width < 1)                  // video hasn't been initialized yet
170         {
171                 width = 78; // LordHavoc: changed from 38 to 78 (320 -> 640 conversion)
172                 con_linewidth = width;
173                 con_totallines = CON_TEXTSIZE / con_linewidth;
174                 memset (con_text, ' ', CON_TEXTSIZE);
175         }
176         else
177         {
178                 oldwidth = con_linewidth;
179                 con_linewidth = width;
180                 oldtotallines = con_totallines;
181                 con_totallines = CON_TEXTSIZE / con_linewidth;
182                 numlines = oldtotallines;
183
184                 if (con_totallines < numlines)
185                         numlines = con_totallines;
186
187                 numchars = oldwidth;
188         
189                 if (con_linewidth < numchars)
190                         numchars = con_linewidth;
191
192                 memcpy (tbuf, con_text, CON_TEXTSIZE);
193                 memset (con_text, ' ', CON_TEXTSIZE);
194
195                 for (i=0 ; i<numlines ; i++)
196                 {
197                         for (j=0 ; j<numchars ; j++)
198                         {
199                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
200                                                 tbuf[((con_current - i + oldtotallines) %
201                                                           oldtotallines) * oldwidth + j];
202                         }
203                 }
204
205                 Con_ClearNotify ();
206         }
207
208         con_backscroll = 0;
209         con_current = con_totallines - 1;
210 }
211
212
213 /*
214 ================
215 Con_Init
216 ================
217 */
218 void Con_Init (void)
219 {
220 #define MAXGAMEDIRLEN   1000
221         char    temp[MAXGAMEDIRLEN+1];
222         char    *t2 = "/qconsole.log";
223
224         Cvar_RegisterVariable(&logfile);
225         con_debuglog = COM_CheckParm("-condebug");
226
227         if (con_debuglog)
228         {
229                 if (strlen (com_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
230                 {
231                         sprintf (temp, "%s%s", com_gamedir, t2);
232                         unlink (temp);
233                 }
234                 logfile.value = 1;
235         }
236
237         con_text = Hunk_AllocName (CON_TEXTSIZE, "context");
238         memset (con_text, ' ', CON_TEXTSIZE);
239         con_linewidth = -1;
240         Con_CheckResize ();
241         
242         Con_Printf ("Console initialized.\n");
243
244 //
245 // register our commands
246 //
247         Cvar_RegisterVariable (&con_notifytime);
248
249         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
250         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
251         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
252         Cmd_AddCommand ("clear", Con_Clear_f);
253         con_initialized = true;
254 }
255
256
257 /*
258 ===============
259 Con_Linefeed
260 ===============
261 */
262 void Con_Linefeed (void)
263 {
264         con_x = 0;
265         con_current++;
266         memset (&con_text[(con_current%con_totallines)*con_linewidth]
267         , ' ', con_linewidth);
268 }
269
270 /*
271 ================
272 Con_Print
273
274 Handles cursor positioning, line wrapping, etc
275 All console printing must go through this in order to be logged to disk
276 If no console is visible, the notify window will pop up.
277 ================
278 */
279 void Con_Print (char *txt)
280 {
281         int             y;
282         int             c, l;
283         static int      cr;
284         int             mask;
285         
286         con_backscroll = 0;
287
288         if (txt[0] == 1)
289         {
290                 mask = 128;             // go to colored text
291                 S_LocalSound ("misc/talk.wav");
292         // play talk wav
293                 txt++;
294         }
295         else if (txt[0] == 2)
296         {
297                 mask = 128;             // go to colored text
298                 txt++;
299         }
300         else
301                 mask = 0;
302
303
304         while ( (c = *txt) )
305         {
306         // count word length
307                 for (l=0 ; l< con_linewidth ; l++)
308                         if ( txt[l] <= ' ')
309                                 break;
310
311         // word wrap
312                 if (l != con_linewidth && (con_x + l > con_linewidth) )
313                         con_x = 0;
314
315                 txt++;
316
317                 if (cr)
318                 {
319                         con_current--;
320                         cr = false;
321                 }
322
323                 
324                 if (!con_x)
325                 {
326                         Con_Linefeed ();
327                 // mark time for transparent overlay
328                         if (con_current >= 0)
329                                 con_times[con_current % NUM_CON_TIMES] = realtime;
330                 }
331
332                 switch (c)
333                 {
334                 case '\n':
335                         con_x = 0;
336                         break;
337
338                 case '\r':
339                         con_x = 0;
340                         cr = 1;
341                         break;
342
343                 default:        // display character and advance
344                         y = con_current % con_totallines;
345                         con_text[y*con_linewidth+con_x] = c | mask;
346                         con_x++;
347                         if (con_x >= con_linewidth)
348                                 con_x = 0;
349                         break;
350                 }
351                 
352         }
353 }
354
355
356 /*
357 ================
358 Con_DebugLog
359 ================
360 */
361 void Con_DebugLog(char *file, char *fmt, ...)
362 {
363     va_list argptr; 
364     static char data[1024];
365     int fd;
366     
367     va_start(argptr, fmt);
368     vsprintf(data, fmt, argptr);
369     va_end(argptr);
370     fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
371     write(fd, data, strlen(data));
372     close(fd);
373 }
374
375
376 /*
377 ================
378 Con_Printf
379
380 Handles cursor positioning, line wrapping, etc
381 ================
382 */
383 // LordHavoc: increased from 4096 to 16384
384 #define MAXPRINTMSG     16384
385 // FIXME: make a buffer size safe vsprintf?
386 void Con_Printf (char *fmt, ...)
387 {
388         va_list         argptr;
389         char            msg[MAXPRINTMSG];
390 //      static qboolean inupdate;
391         
392         va_start (argptr,fmt);
393         vsprintf (msg,fmt,argptr);
394         va_end (argptr);
395         
396 // also echo to debugging console
397         Sys_Printf ("%s", msg); // also echo to debugging console
398
399 // log all messages to file
400         if (con_debuglog)
401                 Con_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg);
402
403         if (!con_initialized)
404                 return;
405                 
406         if (cls.state == ca_dedicated)
407                 return;         // no graphics mode
408
409 // write it to the scrollable buffer
410         Con_Print (msg);
411         
412 // update the screen if the console is displayed
413         // LordHavoc: I don't think there's a real need for this
414         /*
415         // LordHavoc: don't print text while loading scripts
416         if (cls.state != ca_disconnected)
417         if (cls.signon != SIGNONS && !scr_disabled_for_loading )
418         {
419         // protect against infinite loop if something in SCR_UpdateScreen calls
420         // Con_Printf
421                 if (!inupdate)
422                 {
423                         inupdate = true;
424                         SCR_UpdateScreen ();
425                         inupdate = false;
426                 }
427         }
428         */
429 }
430
431 /*
432 ================
433 Con_DPrintf
434
435 A Con_Printf that only shows up if the "developer" cvar is set
436 ================
437 */
438 void Con_DPrintf (char *fmt, ...)
439 {
440         va_list         argptr;
441         char            msg[MAXPRINTMSG];
442                 
443         if (!developer.value)
444                 return;                 // don't confuse non-developers with techie stuff...
445
446         va_start (argptr,fmt);
447         vsprintf (msg,fmt,argptr);
448         va_end (argptr);
449         
450         Con_Printf ("%s", msg);
451 }
452
453
454 /*
455 ==================
456 Con_SafePrintf
457
458 Okay to call even when the screen can't be updated
459 ==================
460 */
461 void Con_SafePrintf (char *fmt, ...)
462 {
463         va_list         argptr;
464         char            msg[1024];
465         int                     temp;
466                 
467         va_start (argptr,fmt);
468         vsprintf (msg,fmt,argptr);
469         va_end (argptr);
470
471         temp = scr_disabled_for_loading;
472         scr_disabled_for_loading = true;
473         Con_Printf ("%s", msg);
474         scr_disabled_for_loading = temp;
475 }
476
477
478 /*
479 ==============================================================================
480
481 DRAWING
482
483 ==============================================================================
484 */
485
486
487 /*
488 ================
489 Con_DrawInput
490
491 The input line scrolls horizontally if typing goes beyond the right edge
492 ================
493 */
494 void Con_DrawInput (void)
495 {
496         int             y;
497         char    *text;
498
499         if (key_dest != key_console && !con_forcedup)
500                 return;         // don't draw anything
501
502         text = key_lines[edit_line];
503         
504 // add the cursor frame
505         text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1);
506         
507         text[key_linepos+1] = 0; // LordHavoc: null terminate, rather than padding with spaces
508 // fill out remainder with spaces
509 //      for (i=key_linepos+1 ; i< con_linewidth ; i++)
510 //              text[i] = ' ';
511                 
512 //      prestep if horizontally scrolling
513         if (key_linepos >= con_linewidth)
514                 text += 1 + key_linepos - con_linewidth;
515                 
516 // draw it
517         y = con_vislines-16;
518
519 //      for (i=0 ; i<con_linewidth ; i++)
520 //              Draw_Character ( (i+1)<<3, con_vislines - 16, text[i]);
521         // LordHavoc: speedup
522         Draw_String(8, con_vislines - 16, text, con_linewidth);
523
524 // remove cursor
525         key_lines[edit_line][key_linepos] = 0;
526 }
527
528
529 /*
530 ================
531 Con_DrawNotify
532
533 Draws the last few lines of output transparently over the game top
534 ================
535 */
536 void Con_DrawNotify (void)
537 {
538         int             x, v;
539         char    *text;
540         int             i;
541         float   time;
542         extern char chat_buffer[];
543         char    temptext[256];
544
545         v = 0;
546         for (i= con_current-NUM_CON_TIMES+1 ; i<=con_current ; i++)
547         {
548                 if (i < 0)
549                         continue;
550                 time = con_times[i % NUM_CON_TIMES];
551                 if (time == 0)
552                         continue;
553                 time = realtime - time;
554                 if (time > con_notifytime.value)
555                         continue;
556                 text = con_text + (i % con_totallines)*con_linewidth;
557                 
558                 clearnotify = 0;
559
560 //              for (x = 0 ; x < con_linewidth ; x++)
561 //                      Draw_Character ( (x+1)<<3, v, text[x]);
562                 // LordHavoc: speedup
563                 Draw_String(8, v, text, con_linewidth);
564
565                 v += 8;
566         }
567
568
569         if (key_dest == key_message)
570         {
571                 clearnotify = 0;
572         
573                 x = 0;
574                 
575                 // LordHavoc: speedup, and other improvements
576                 if (team_message)
577                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
578                 else
579                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
580                 while (strlen(temptext) >= con_linewidth)
581                 {
582                         Draw_String (8, v, temptext, con_linewidth);
583                         strcpy(temptext, &temptext[con_linewidth]);
584                         v += 8;
585                 }
586                 if (strlen(temptext) > 0)
587                 {
588                         Draw_String (8, v, temptext, 0);
589                         v += 8;
590                 }
591 //              Draw_String (8, v, "say:", 0);
592 //              while(chat_buffer[x])
593 //              {
594 //                      Draw_Character ( (x+5)<<3, v, chat_buffer[x]);
595 //                      x++;
596 //              }
597 //              Draw_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1));
598 //              v += 8;
599         }
600         
601         if (v > con_notifylines)
602                 con_notifylines = v;
603 }
604
605 /*
606 ================
607 Con_DrawConsole
608
609 Draws the console with the solid background
610 The typing input line at the bottom should only be drawn if typing is allowed
611 ================
612 */
613 void Con_DrawConsole (int lines, qboolean drawinput)
614 {
615         int                             i, y;
616         int                             rows;
617         char                    *text;
618         int                             j;
619         
620         if (lines <= 0)
621                 return;
622
623 // draw the background
624         Draw_ConsoleBackground (lines);
625
626 // draw the text
627         con_vislines = lines;
628
629         rows = (lines-16)>>3;           // rows of text to draw
630         y = lines - 16 - (rows<<3);     // may start slightly negative
631
632         for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 )
633         {
634                 j = i - con_backscroll;
635                 if (j<0)
636                         j = 0;
637                 text = con_text + (j % con_totallines)*con_linewidth;
638
639 //              for (x=0 ; x<con_linewidth ; x++)
640 //                      Draw_Character ( (x+1)<<3, y, text[x]);
641                 // LordHavoc: speedup
642                 Draw_String(8, y, text, con_linewidth);
643         }
644
645 // draw the input prompt, user text, and cursor if desired
646         if (drawinput)
647                 Con_DrawInput ();
648 }