0,0 → 1,991 |
/* |
Copyright (C) 1996-1997 Id Software, Inc. |
|
This program is free software; you can redistribute it and/or |
modify it under the terms of the GNU General Public License |
as published by the Free Software Foundation; either version 2 |
of the License, or (at your option) any later version. |
|
This program is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
See the GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with this program; if not, write to the Free Software |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
*/ |
// screen.c -- master for refresh, status bar, console, chat, notify, etc |
|
#include "quakedef.h" |
#include "r_local.h" |
|
// only the refresh window will be updated unless these variables are flagged |
int scr_copytop; |
int scr_copyeverything; |
|
float scr_con_current; |
float scr_conlines; // lines of console to display |
|
float oldscreensize, oldfov; |
cvar_t scr_viewsize = {"viewsize","100", true}; |
cvar_t scr_fov = {"fov","90"}; // 10 - 170 |
cvar_t scr_conspeed = {"scr_conspeed","300"}; |
cvar_t scr_centertime = {"scr_centertime","2"}; |
cvar_t scr_showram = {"showram","1"}; |
cvar_t scr_showturtle = {"showturtle","0"}; |
cvar_t scr_showpause = {"showpause","1"}; |
cvar_t scr_printspeed = {"scr_printspeed","8"}; |
|
qboolean scr_initialized; // ready to draw |
|
qpic_t *scr_ram; |
qpic_t *scr_net; |
qpic_t *scr_turtle; |
|
int scr_fullupdate; |
|
int clearconsole; |
int clearnotify; |
|
viddef_t vid; // global video state |
|
vrect_t *pconupdate; |
vrect_t scr_vrect; |
|
qboolean scr_disabled_for_loading; |
qboolean scr_drawloading; |
float scr_disabled_time; |
qboolean scr_skipupdate; |
|
qboolean block_drawing; |
|
void SCR_ScreenShot_f (void); |
|
/* |
=============================================================================== |
|
CENTER PRINTING |
|
=============================================================================== |
*/ |
|
char scr_centerstring[1024]; |
float scr_centertime_start; // for slow victory printing |
float scr_centertime_off; |
int scr_center_lines; |
int scr_erase_lines; |
int scr_erase_center; |
|
/* |
============== |
SCR_CenterPrint |
|
Called for important messages that should stay in the center of the screen |
for a few moments |
============== |
*/ |
void SCR_CenterPrint (char *str) |
{ |
strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); |
scr_centertime_off = scr_centertime.value; |
scr_centertime_start = cl.time; |
|
// count the number of lines for centering |
scr_center_lines = 1; |
while (*str) |
{ |
if (*str == '\n') |
scr_center_lines++; |
str++; |
} |
} |
|
void SCR_EraseCenterString (void) |
{ |
int y; |
|
if (scr_erase_center++ > vid.numpages) |
{ |
scr_erase_lines = 0; |
return; |
} |
|
if (scr_center_lines <= 4) |
y = vid.height*0.35; |
else |
y = 48; |
|
scr_copytop = 1; |
Draw_TileClear (0, y,vid.width, 8*scr_erase_lines); |
} |
|
void SCR_DrawCenterString (void) |
{ |
char *start; |
int l; |
int j; |
int x, y; |
int remaining; |
|
// the finale prints the characters one at a time |
if (cl.intermission) |
remaining = scr_printspeed.value * (cl.time - scr_centertime_start); |
else |
remaining = 9999; |
|
scr_erase_center = 0; |
start = scr_centerstring; |
|
if (scr_center_lines <= 4) |
y = vid.height*0.35; |
else |
y = 48; |
|
do |
{ |
// scan the width of the line |
for (l=0 ; l<40 ; l++) |
if (start[l] == '\n' || !start[l]) |
break; |
x = (vid.width - l*8)/2; |
for (j=0 ; j<l ; j++, x+=8) |
{ |
Draw_Character (x, y, start[j]); |
if (!remaining--) |
return; |
} |
|
y += 8; |
|
while (*start && *start != '\n') |
start++; |
|
if (!*start) |
break; |
start++; // skip the \n |
} while (1); |
} |
|
void SCR_CheckDrawCenterString (void) |
{ |
scr_copytop = 1; |
if (scr_center_lines > scr_erase_lines) |
scr_erase_lines = scr_center_lines; |
|
scr_centertime_off -= host_frametime; |
|
if (scr_centertime_off <= 0 && !cl.intermission) |
return; |
if (key_dest != key_game) |
return; |
|
SCR_DrawCenterString (); |
} |
|
//============================================================================= |
|
/* |
==================== |
CalcFov |
==================== |
*/ |
float CalcFov (float fov_x, float width, float height) |
{ |
float a; |
float x; |
|
if (fov_x < 1 || fov_x > 179) |
Sys_Error ("Bad fov: %f", fov_x); |
|
x = width/tan(fov_x/360*M_PI); |
|
a = atan (height/x); |
|
a = a*360/M_PI; |
|
return a; |
} |
|
/* |
================= |
SCR_CalcRefdef |
|
Must be called whenever vid changes |
Internal use only |
================= |
*/ |
static void SCR_CalcRefdef (void) |
{ |
vrect_t vrect; |
float size; |
|
scr_fullupdate = 0; // force a background redraw |
vid.recalc_refdef = 0; |
|
// force the status bar to redraw |
Sbar_Changed (); |
|
//======================================== |
|
// bound viewsize |
if (scr_viewsize.value < 30) |
Cvar_Set ("viewsize","30"); |
if (scr_viewsize.value > 120) |
Cvar_Set ("viewsize","120"); |
|
// bound field of view |
if (scr_fov.value < 10) |
Cvar_Set ("fov","10"); |
if (scr_fov.value > 170) |
Cvar_Set ("fov","170"); |
|
r_refdef.fov_x = scr_fov.value; |
r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); |
|
// intermission is always full screen |
if (cl.intermission) |
size = 120; |
else |
size = scr_viewsize.value; |
|
if (size >= 120) |
sb_lines = 0; // no status bar at all |
else if (size >= 110) |
sb_lines = 24; // no inventory |
else |
sb_lines = 24+16+8; |
|
// these calculations mirror those in R_Init() for r_refdef, but take no |
// account of water warping |
vrect.x = 0; |
vrect.y = 0; |
vrect.width = vid.width; |
vrect.height = vid.height; |
|
R_SetVrect (&vrect, &scr_vrect, sb_lines); |
|
// guard against going from one mode to another that's less than half the |
// vertical resolution |
if (scr_con_current > vid.height) |
scr_con_current = vid.height; |
|
// notify the refresh of the change |
R_ViewChanged (&vrect, sb_lines, vid.aspect); |
} |
|
|
/* |
================= |
SCR_SizeUp_f |
|
Keybinding command |
================= |
*/ |
void SCR_SizeUp_f (void) |
{ |
Cvar_SetValue ("viewsize",scr_viewsize.value+10); |
vid.recalc_refdef = 1; |
} |
|
|
/* |
================= |
SCR_SizeDown_f |
|
Keybinding command |
================= |
*/ |
void SCR_SizeDown_f (void) |
{ |
Cvar_SetValue ("viewsize",scr_viewsize.value-10); |
vid.recalc_refdef = 1; |
} |
|
//============================================================================ |
|
/* |
================== |
SCR_Init |
================== |
*/ |
void SCR_Init (void) |
{ |
Cvar_RegisterVariable (&scr_fov); |
Cvar_RegisterVariable (&scr_viewsize); |
Cvar_RegisterVariable (&scr_conspeed); |
Cvar_RegisterVariable (&scr_showram); |
Cvar_RegisterVariable (&scr_showturtle); |
Cvar_RegisterVariable (&scr_showpause); |
Cvar_RegisterVariable (&scr_centertime); |
Cvar_RegisterVariable (&scr_printspeed); |
|
// |
// register our commands |
// |
Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); |
Cmd_AddCommand ("sizeup",SCR_SizeUp_f); |
Cmd_AddCommand ("sizedown",SCR_SizeDown_f); |
|
scr_ram = Draw_PicFromWad ("ram"); |
scr_net = Draw_PicFromWad ("net"); |
scr_turtle = Draw_PicFromWad ("turtle"); |
|
scr_initialized = true; |
} |
|
|
|
/* |
============== |
SCR_DrawRam |
============== |
*/ |
void SCR_DrawRam (void) |
{ |
if (!scr_showram.value) |
return; |
|
if (!r_cache_thrash) |
return; |
|
Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); |
} |
|
/* |
============== |
SCR_DrawTurtle |
============== |
*/ |
void SCR_DrawTurtle (void) |
{ |
static int count; |
|
if (!scr_showturtle.value) |
return; |
|
if (host_frametime < 0.1) |
{ |
count = 0; |
return; |
} |
|
count++; |
if (count < 3) |
return; |
|
Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); |
} |
|
/* |
============== |
SCR_DrawNet |
============== |
*/ |
void SCR_DrawNet (void) |
{ |
if (realtime - cl.last_received_message < 0.3) |
return; |
if (cls.demoplayback) |
return; |
|
Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); |
} |
|
/* |
============== |
DrawPause |
============== |
*/ |
void SCR_DrawPause (void) |
{ |
qpic_t *pic; |
|
if (!scr_showpause.value) // turn off for screenshots |
return; |
|
if (!cl.paused) |
return; |
|
pic = Draw_CachePic ("gfx/pause.lmp"); |
Draw_Pic ( (vid.width - pic->width)/2, |
(vid.height - 48 - pic->height)/2, pic); |
} |
|
|
|
/* |
============== |
SCR_DrawLoading |
============== |
*/ |
void SCR_DrawLoading (void) |
{ |
qpic_t *pic; |
|
if (!scr_drawloading) |
return; |
|
pic = Draw_CachePic ("gfx/loading.lmp"); |
Draw_Pic ( (vid.width - pic->width)/2, |
(vid.height - 48 - pic->height)/2, pic); |
} |
|
|
|
//============================================================================= |
|
|
/* |
================== |
SCR_SetUpToDrawConsole |
================== |
*/ |
void SCR_SetUpToDrawConsole (void) |
{ |
Con_CheckResize (); |
|
if (scr_drawloading) |
return; // never a console with loading plaque |
|
// decide on the height of the console |
con_forcedup = !cl.worldmodel || cls.signon != SIGNONS; |
|
if (con_forcedup) |
{ |
scr_conlines = vid.height; // full screen |
scr_con_current = scr_conlines; |
} |
else if (key_dest == key_console) |
scr_conlines = vid.height/2; // half screen |
else |
scr_conlines = 0; // none visible |
|
if (scr_conlines < scr_con_current) |
{ |
scr_con_current -= scr_conspeed.value*host_frametime; |
if (scr_conlines > scr_con_current) |
scr_con_current = scr_conlines; |
|
} |
else if (scr_conlines > scr_con_current) |
{ |
scr_con_current += scr_conspeed.value*host_frametime; |
if (scr_conlines < scr_con_current) |
scr_con_current = scr_conlines; |
} |
|
if (clearconsole++ < vid.numpages) |
{ |
scr_copytop = 1; |
Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current); |
Sbar_Changed (); |
} |
else if (clearnotify++ < vid.numpages) |
{ |
scr_copytop = 1; |
Draw_TileClear (0,0,vid.width, con_notifylines); |
} |
else |
con_notifylines = 0; |
} |
|
/* |
================== |
SCR_DrawConsole |
================== |
*/ |
void SCR_DrawConsole (void) |
{ |
if (scr_con_current) |
{ |
scr_copyeverything = 1; |
Con_DrawConsole (scr_con_current, true); |
clearconsole = 0; |
} |
else |
{ |
if (key_dest == key_game || key_dest == key_message) |
Con_DrawNotify (); // only draw notify in game |
} |
} |
|
|
/* |
============================================================================== |
|
SCREEN SHOTS |
|
============================================================================== |
*/ |
|
|
typedef struct |
{ |
char manufacturer; |
char version; |
char encoding; |
char bits_per_pixel; |
unsigned short xmin,ymin,xmax,ymax; |
unsigned short hres,vres; |
unsigned char palette[48]; |
char reserved; |
char color_planes; |
unsigned short bytes_per_line; |
unsigned short palette_type; |
char filler[58]; |
unsigned char data; // unbounded |
} pcx_t; |
|
/* |
============== |
WritePCXfile |
============== |
*/ |
void WritePCXfile (char *filename, byte *data, int width, int height, |
int rowbytes, byte *palette) |
{ |
int i, j, length; |
pcx_t *pcx; |
byte *pack; |
|
pcx = Hunk_TempAlloc (width*height*2+1000); |
if (pcx == NULL) |
{ |
Con_Printf("SCR_ScreenShot_f: not enough memory\n"); |
return; |
} |
|
pcx->manufacturer = 0x0a; // PCX id |
pcx->version = 5; // 256 color |
pcx->encoding = 1; // uncompressed |
pcx->bits_per_pixel = 8; // 256 color |
pcx->xmin = 0; |
pcx->ymin = 0; |
pcx->xmax = LittleShort((short)(width-1)); |
pcx->ymax = LittleShort((short)(height-1)); |
pcx->hres = LittleShort((short)width); |
pcx->vres = LittleShort((short)height); |
Q_memset (pcx->palette,0,sizeof(pcx->palette)); |
pcx->color_planes = 1; // chunky image |
pcx->bytes_per_line = LittleShort((short)width); |
pcx->palette_type = LittleShort(2); // not a grey scale |
Q_memset (pcx->filler,0,sizeof(pcx->filler)); |
|
// pack the image |
pack = &pcx->data; |
|
for (i=0 ; i<height ; i++) |
{ |
for (j=0 ; j<width ; j++) |
{ |
if ( (*data & 0xc0) != 0xc0) |
*pack++ = *data++; |
else |
{ |
*pack++ = 0xc1; |
*pack++ = *data++; |
} |
} |
|
data += rowbytes - width; |
} |
|
// write the palette |
*pack++ = 0x0c; // palette ID byte |
for (i=0 ; i<768 ; i++) |
*pack++ = *palette++; |
|
// write output file |
length = pack - (byte *)pcx; |
COM_WriteFile (filename, pcx, length); |
} |
|
|
|
/* |
================== |
SCR_ScreenShot_f |
================== |
*/ |
void SCR_ScreenShot_f (void) |
{ |
int i; |
char pcxname[80]; |
char checkname[MAX_OSPATH]; |
|
// |
// find a file name to save it to |
// |
strcpy(pcxname,"quake00.pcx"); |
|
for (i=0 ; i<=99 ; i++) |
{ |
pcxname[5] = i/10 + '0'; |
pcxname[6] = i%10 + '0'; |
sprintf (checkname, "%s/%s", com_gamedir, pcxname); |
if (Sys_FileTime(checkname) == -1) |
break; // file doesn't exist |
} |
if (i==100) |
{ |
Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); |
return; |
} |
|
// |
// save the pcx file |
// |
D_EnableBackBufferAccess (); // enable direct drawing of console to back |
// buffer |
|
WritePCXfile (pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes, |
host_basepal); |
|
D_DisableBackBufferAccess (); // for adapters that can't stay mapped in |
// for linear writes all the time |
|
Con_Printf ("Wrote %s\n", pcxname); |
} |
|
|
//============================================================================= |
|
|
/* |
=============== |
SCR_BeginLoadingPlaque |
|
================ |
*/ |
void SCR_BeginLoadingPlaque (void) |
{ |
S_StopAllSounds (true); |
|
if (cls.state != ca_connected) |
return; |
if (cls.signon != SIGNONS) |
return; |
|
// redraw with no console and the loading plaque |
Con_ClearNotify (); |
scr_centertime_off = 0; |
scr_con_current = 0; |
|
scr_drawloading = true; |
scr_fullupdate = 0; |
Sbar_Changed (); |
SCR_UpdateScreen (); |
scr_drawloading = false; |
|
scr_disabled_for_loading = true; |
scr_disabled_time = realtime; |
scr_fullupdate = 0; |
} |
|
/* |
=============== |
SCR_EndLoadingPlaque |
|
================ |
*/ |
void SCR_EndLoadingPlaque (void) |
{ |
scr_disabled_for_loading = false; |
scr_fullupdate = 0; |
Con_ClearNotify (); |
} |
|
//============================================================================= |
|
char *scr_notifystring; |
qboolean scr_drawdialog; |
|
void SCR_DrawNotifyString (void) |
{ |
char *start; |
int l; |
int j; |
int x, y; |
|
start = scr_notifystring; |
|
y = vid.height*0.35; |
|
do |
{ |
// scan the width of the line |
for (l=0 ; l<40 ; l++) |
if (start[l] == '\n' || !start[l]) |
break; |
x = (vid.width - l*8)/2; |
for (j=0 ; j<l ; j++, x+=8) |
Draw_Character (x, y, start[j]); |
|
y += 8; |
|
while (*start && *start != '\n') |
start++; |
|
if (!*start) |
break; |
start++; // skip the \n |
} while (1); |
} |
|
/* |
================== |
SCR_ModalMessage |
|
Displays a text string in the center of the screen and waits for a Y or N |
keypress. |
================== |
*/ |
int SCR_ModalMessage (char *text) |
{ |
if (cls.state == ca_dedicated) |
return true; |
|
scr_notifystring = text; |
|
// draw a fresh screen |
scr_fullupdate = 0; |
scr_drawdialog = true; |
SCR_UpdateScreen (); |
scr_drawdialog = false; |
|
S_ClearBuffer (); // so dma doesn't loop current sound |
|
do |
{ |
key_count = -1; // wait for a key down and up |
Sys_SendKeyEvents (); |
} while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE); |
|
scr_fullupdate = 0; |
SCR_UpdateScreen (); |
|
return key_lastpress == 'y'; |
} |
|
|
//============================================================================= |
|
/* |
=============== |
SCR_BringDownConsole |
|
Brings the console down and fades the palettes back to normal |
================ |
*/ |
void SCR_BringDownConsole (void) |
{ |
int i; |
|
scr_centertime_off = 0; |
|
for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++) |
SCR_UpdateScreen (); |
|
cl.cshifts[0].percent = 0; // no area contents palette on next frame |
VID_SetPalette (host_basepal); |
} |
|
|
/* |
================== |
SCR_UpdateScreen |
|
This is called every frame, and can also be called explicitly to flush |
text to the screen. |
|
WARNING: be very careful calling this from elsewhere, because the refresh |
needs almost the entire 256k of stack space! |
================== |
*/ |
void SCR_UpdateScreen (void) |
{ |
static float oldscr_viewsize; |
static float oldlcd_x; |
vrect_t vrect; |
|
if (scr_skipupdate || block_drawing) |
return; |
|
scr_copytop = 0; |
scr_copyeverything = 0; |
|
if (scr_disabled_for_loading) |
{ |
if (realtime - scr_disabled_time > 60) |
{ |
scr_disabled_for_loading = false; |
Con_Printf ("load failed.\n"); |
} |
else |
return; |
} |
|
if (cls.state == ca_dedicated) |
return; // stdout only |
|
if (!scr_initialized || !con_initialized) |
return; // not initialized yet |
|
if (scr_viewsize.value != oldscr_viewsize) |
{ |
oldscr_viewsize = scr_viewsize.value; |
vid.recalc_refdef = 1; |
} |
|
// |
// check for vid changes |
// |
if (oldfov != scr_fov.value) |
{ |
oldfov = scr_fov.value; |
vid.recalc_refdef = true; |
} |
|
if (oldlcd_x != lcd_x.value) |
{ |
oldlcd_x = lcd_x.value; |
vid.recalc_refdef = true; |
} |
|
if (oldscreensize != scr_viewsize.value) |
{ |
oldscreensize = scr_viewsize.value; |
vid.recalc_refdef = true; |
} |
|
if (vid.recalc_refdef) |
{ |
// something changed, so reorder the screen |
SCR_CalcRefdef (); |
} |
|
// |
// do 3D refresh drawing, and then update the screen |
// |
D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly |
|
if (scr_fullupdate++ < vid.numpages) |
{ // clear the entire screen |
scr_copyeverything = 1; |
Draw_TileClear (0,0,vid.width,vid.height); |
Sbar_Changed (); |
} |
|
pconupdate = NULL; |
|
|
SCR_SetUpToDrawConsole (); |
SCR_EraseCenterString (); |
|
D_DisableBackBufferAccess (); // for adapters that can't stay mapped in |
// for linear writes all the time |
|
VID_LockBuffer (); |
|
V_RenderView (); |
|
VID_UnlockBuffer (); |
|
D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly |
|
if (scr_drawdialog) |
{ |
Sbar_Draw (); |
Draw_FadeScreen (); |
SCR_DrawNotifyString (); |
scr_copyeverything = true; |
} |
else if (scr_drawloading) |
{ |
SCR_DrawLoading (); |
Sbar_Draw (); |
} |
else if (cl.intermission == 1 && key_dest == key_game) |
{ |
Sbar_IntermissionOverlay (); |
} |
else if (cl.intermission == 2 && key_dest == key_game) |
{ |
Sbar_FinaleOverlay (); |
SCR_CheckDrawCenterString (); |
} |
else if (cl.intermission == 3 && key_dest == key_game) |
{ |
SCR_CheckDrawCenterString (); |
} |
else |
{ |
SCR_DrawRam (); |
SCR_DrawNet (); |
SCR_DrawTurtle (); |
SCR_DrawPause (); |
SCR_CheckDrawCenterString (); |
Sbar_Draw (); |
SCR_DrawConsole (); |
M_Draw (); |
} |
|
D_DisableBackBufferAccess (); // for adapters that can't stay mapped in |
// for linear writes all the time |
if (pconupdate) |
{ |
D_UpdateRects (pconupdate); |
} |
|
V_UpdatePalette (); |
|
// |
// update one of three areas |
// |
|
if (scr_copyeverything) |
{ |
vrect.x = 0; |
vrect.y = 0; |
vrect.width = vid.width; |
vrect.height = vid.height; |
vrect.pnext = 0; |
|
VID_Update (&vrect); |
} |
else if (scr_copytop) |
{ |
vrect.x = 0; |
vrect.y = 0; |
vrect.width = vid.width; |
vrect.height = vid.height - sb_lines; |
vrect.pnext = 0; |
|
VID_Update (&vrect); |
} |
else |
{ |
vrect.x = scr_vrect.x; |
vrect.y = scr_vrect.y; |
vrect.width = scr_vrect.width; |
vrect.height = scr_vrect.height; |
vrect.pnext = 0; |
|
VID_Update (&vrect); |
} |
} |
|
|
/* |
================== |
SCR_UpdateWholeScreen |
================== |
*/ |
void SCR_UpdateWholeScreen (void) |
{ |
scr_fullupdate = 0; |
SCR_UpdateScreen (); |
} |