This commit is contained in:
Literally A Penguin
2024-09-25 15:37:26 -05:00
committed by GitHub
parent e58d83b4a8
commit b585e51a81
100 changed files with 65768 additions and 0 deletions

1058
client/adivtab.h Normal file

File diff suppressed because it is too large Load Diff

181
client/anorms.h Normal file
View File

@ -0,0 +1,181 @@
/*
Copyright (C) 1997-2001 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.
*/
{-0.525731, 0.000000, 0.850651},
{-0.442863, 0.238856, 0.864188},
{-0.295242, 0.000000, 0.955423},
{-0.309017, 0.500000, 0.809017},
{-0.162460, 0.262866, 0.951056},
{0.000000, 0.000000, 1.000000},
{0.000000, 0.850651, 0.525731},
{-0.147621, 0.716567, 0.681718},
{0.147621, 0.716567, 0.681718},
{0.000000, 0.525731, 0.850651},
{0.309017, 0.500000, 0.809017},
{0.525731, 0.000000, 0.850651},
{0.295242, 0.000000, 0.955423},
{0.442863, 0.238856, 0.864188},
{0.162460, 0.262866, 0.951056},
{-0.681718, 0.147621, 0.716567},
{-0.809017, 0.309017, 0.500000},
{-0.587785, 0.425325, 0.688191},
{-0.850651, 0.525731, 0.000000},
{-0.864188, 0.442863, 0.238856},
{-0.716567, 0.681718, 0.147621},
{-0.688191, 0.587785, 0.425325},
{-0.500000, 0.809017, 0.309017},
{-0.238856, 0.864188, 0.442863},
{-0.425325, 0.688191, 0.587785},
{-0.716567, 0.681718, -0.147621},
{-0.500000, 0.809017, -0.309017},
{-0.525731, 0.850651, 0.000000},
{0.000000, 0.850651, -0.525731},
{-0.238856, 0.864188, -0.442863},
{0.000000, 0.955423, -0.295242},
{-0.262866, 0.951056, -0.162460},
{0.000000, 1.000000, 0.000000},
{0.000000, 0.955423, 0.295242},
{-0.262866, 0.951056, 0.162460},
{0.238856, 0.864188, 0.442863},
{0.262866, 0.951056, 0.162460},
{0.500000, 0.809017, 0.309017},
{0.238856, 0.864188, -0.442863},
{0.262866, 0.951056, -0.162460},
{0.500000, 0.809017, -0.309017},
{0.850651, 0.525731, 0.000000},
{0.716567, 0.681718, 0.147621},
{0.716567, 0.681718, -0.147621},
{0.525731, 0.850651, 0.000000},
{0.425325, 0.688191, 0.587785},
{0.864188, 0.442863, 0.238856},
{0.688191, 0.587785, 0.425325},
{0.809017, 0.309017, 0.500000},
{0.681718, 0.147621, 0.716567},
{0.587785, 0.425325, 0.688191},
{0.955423, 0.295242, 0.000000},
{1.000000, 0.000000, 0.000000},
{0.951056, 0.162460, 0.262866},
{0.850651, -0.525731, 0.000000},
{0.955423, -0.295242, 0.000000},
{0.864188, -0.442863, 0.238856},
{0.951056, -0.162460, 0.262866},
{0.809017, -0.309017, 0.500000},
{0.681718, -0.147621, 0.716567},
{0.850651, 0.000000, 0.525731},
{0.864188, 0.442863, -0.238856},
{0.809017, 0.309017, -0.500000},
{0.951056, 0.162460, -0.262866},
{0.525731, 0.000000, -0.850651},
{0.681718, 0.147621, -0.716567},
{0.681718, -0.147621, -0.716567},
{0.850651, 0.000000, -0.525731},
{0.809017, -0.309017, -0.500000},
{0.864188, -0.442863, -0.238856},
{0.951056, -0.162460, -0.262866},
{0.147621, 0.716567, -0.681718},
{0.309017, 0.500000, -0.809017},
{0.425325, 0.688191, -0.587785},
{0.442863, 0.238856, -0.864188},
{0.587785, 0.425325, -0.688191},
{0.688191, 0.587785, -0.425325},
{-0.147621, 0.716567, -0.681718},
{-0.309017, 0.500000, -0.809017},
{0.000000, 0.525731, -0.850651},
{-0.525731, 0.000000, -0.850651},
{-0.442863, 0.238856, -0.864188},
{-0.295242, 0.000000, -0.955423},
{-0.162460, 0.262866, -0.951056},
{0.000000, 0.000000, -1.000000},
{0.295242, 0.000000, -0.955423},
{0.162460, 0.262866, -0.951056},
{-0.442863, -0.238856, -0.864188},
{-0.309017, -0.500000, -0.809017},
{-0.162460, -0.262866, -0.951056},
{0.000000, -0.850651, -0.525731},
{-0.147621, -0.716567, -0.681718},
{0.147621, -0.716567, -0.681718},
{0.000000, -0.525731, -0.850651},
{0.309017, -0.500000, -0.809017},
{0.442863, -0.238856, -0.864188},
{0.162460, -0.262866, -0.951056},
{0.238856, -0.864188, -0.442863},
{0.500000, -0.809017, -0.309017},
{0.425325, -0.688191, -0.587785},
{0.716567, -0.681718, -0.147621},
{0.688191, -0.587785, -0.425325},
{0.587785, -0.425325, -0.688191},
{0.000000, -0.955423, -0.295242},
{0.000000, -1.000000, 0.000000},
{0.262866, -0.951056, -0.162460},
{0.000000, -0.850651, 0.525731},
{0.000000, -0.955423, 0.295242},
{0.238856, -0.864188, 0.442863},
{0.262866, -0.951056, 0.162460},
{0.500000, -0.809017, 0.309017},
{0.716567, -0.681718, 0.147621},
{0.525731, -0.850651, 0.000000},
{-0.238856, -0.864188, -0.442863},
{-0.500000, -0.809017, -0.309017},
{-0.262866, -0.951056, -0.162460},
{-0.850651, -0.525731, 0.000000},
{-0.716567, -0.681718, -0.147621},
{-0.716567, -0.681718, 0.147621},
{-0.525731, -0.850651, 0.000000},
{-0.500000, -0.809017, 0.309017},
{-0.238856, -0.864188, 0.442863},
{-0.262866, -0.951056, 0.162460},
{-0.864188, -0.442863, 0.238856},
{-0.809017, -0.309017, 0.500000},
{-0.688191, -0.587785, 0.425325},
{-0.681718, -0.147621, 0.716567},
{-0.442863, -0.238856, 0.864188},
{-0.587785, -0.425325, 0.688191},
{-0.309017, -0.500000, 0.809017},
{-0.147621, -0.716567, 0.681718},
{-0.425325, -0.688191, 0.587785},
{-0.162460, -0.262866, 0.951056},
{0.442863, -0.238856, 0.864188},
{0.162460, -0.262866, 0.951056},
{0.309017, -0.500000, 0.809017},
{0.147621, -0.716567, 0.681718},
{0.000000, -0.525731, 0.850651},
{0.425325, -0.688191, 0.587785},
{0.587785, -0.425325, 0.688191},
{0.688191, -0.587785, 0.425325},
{-0.955423, 0.295242, 0.000000},
{-0.951056, 0.162460, 0.262866},
{-1.000000, 0.000000, 0.000000},
{-0.850651, 0.000000, 0.525731},
{-0.955423, -0.295242, 0.000000},
{-0.951056, -0.162460, 0.262866},
{-0.864188, 0.442863, -0.238856},
{-0.951056, 0.162460, -0.262866},
{-0.809017, 0.309017, -0.500000},
{-0.864188, -0.442863, -0.238856},
{-0.951056, -0.162460, -0.262866},
{-0.809017, -0.309017, -0.500000},
{-0.681718, 0.147621, -0.716567},
{-0.681718, -0.147621, -0.716567},
{-0.850651, 0.000000, -0.525731},
{-0.688191, 0.587785, -0.425325},
{-0.587785, 0.425325, -0.688191},
{-0.425325, 0.688191, -0.587785},
{-0.425325, -0.688191, -0.587785},
{-0.587785, -0.425325, -0.688191},
{-0.688191, -0.587785, -0.425325},

81
client/asm_i386.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef __ASM_I386__
#define __ASM_I386__
#ifdef ELF
#define C(label) label
#else
#define C(label) _##label
#endif
//
// !!! note that this file must match the corresponding C structures at all
// times !!!
//
// plane_t structure
// !!! if this is changed, it must be changed in model.h too !!!
// !!! if the size of this is changed, the array lookup in SV_HullPointContents
// must be changed too !!!
#define pl_normal 0
#define pl_dist 12
#define pl_type 16
#define pl_signbits 17
#define pl_pad 18
#define pl_size 20
// hull_t structure
// !!! if this is changed, it must be changed in model.h too !!!
#define hu_clipnodes 0
#define hu_planes 4
#define hu_firstclipnode 8
#define hu_lastclipnode 12
#define hu_clip_mins 16
#define hu_clip_maxs 28
#define hu_size 40
// dnode_t structure
// !!! if this is changed, it must be changed in bspfile.h too !!!
#define nd_planenum 0
#define nd_children 4
#define nd_mins 8
#define nd_maxs 20
#define nd_firstface 32
#define nd_numfaces 36
#define nd_size 40
// sfxcache_t structure
// !!! if this is changed, it much be changed in sound.h too !!!
#define sfxc_length 0
#define sfxc_loopstart 4
#define sfxc_speed 8
#define sfxc_width 12
#define sfxc_stereo 16
#define sfxc_data 20
// channel_t structure
// !!! if this is changed, it much be changed in sound.h too !!!
#define ch_sfx 0
#define ch_leftvol 4
#define ch_rightvol 8
#define ch_end 12
#define ch_pos 16
#define ch_looping 20
#define ch_entnum 24
#define ch_entchannel 28
#define ch_origin 32
#define ch_dist_mult 44
#define ch_master_vol 48
#define ch_size 52
// portable_samplepair_t structure
// !!! if this is changed, it much be changed in sound.h too !!!
#define psp_left 0
#define psp_right 4
#define psp_size 8
// !!! must be kept the same as in d_iface.h !!!
#define TRANSPARENT_COLOR 255
#endif

123
client/block16.h Normal file
View File

@ -0,0 +1,123 @@
LEnter16_16:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch0:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch1:
movw %cx,2(%edi)
addl $0x4,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch2:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch3:
movw %cx,2(%edi)
addl $0x4,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch4:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch5:
movw %cx,2(%edi)
addl $0x4,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch6:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch7:
movw %cx,2(%edi)
addl $0x4,%edi
LEnter8_16:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch8:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch9:
movw %cx,2(%edi)
addl $0x4,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch10:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch11:
movw %cx,2(%edi)
addl $0x4,%edi
LEnter4_16:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch12:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch13:
movw %cx,2(%edi)
addl $0x4,%edi
LEnter2_16:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movw 0x12345678(,%eax,2),%ax
LBPatch14:
addl %ebp,%edx
movw %ax,(%edi)
movw 0x12345678(,%ecx,2),%cx
LBPatch15:
movw %cx,2(%edi)
addl $0x4,%edi

124
client/block8.h Normal file
View File

@ -0,0 +1,124 @@
LEnter16_8:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch0:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch1:
movb %cl,1(%edi)
addl $0x2,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch2:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch3:
movb %cl,1(%edi)
addl $0x2,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch4:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch5:
movb %cl,1(%edi)
addl $0x2,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch6:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch7:
movb %cl,1(%edi)
addl $0x2,%edi
LEnter8_8:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch8:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch9:
movb %cl,1(%edi)
addl $0x2,%edi
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch10:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch11:
movb %cl,1(%edi)
addl $0x2,%edi
LEnter4_8:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch12:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch13:
movb %cl,1(%edi)
addl $0x2,%edi
LEnter2_8:
movb (%esi),%al
movb (%esi,%ebx,),%cl
movb %dh,%ah
addl %ebp,%edx
movb %dh,%ch
leal (%esi,%ebx,2),%esi
movb 0x12345678(%eax),%al
LBPatch14:
addl %ebp,%edx
movb %al,(%edi)
movb 0x12345678(%ecx),%cl
LBPatch15:
movb %cl,1(%edi)
addl $0x2,%edi

26
client/cdaudio.h Normal file
View File

@ -0,0 +1,26 @@
/*
Copyright (C) 1997-2001 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.
*/
int CDAudio_Init(void);
void CDAudio_Shutdown(void);
void CDAudio_Play(int track, qboolean looping);
void CDAudio_Stop(void);
void CDAudio_Update(void);
void CDAudio_Activate (qboolean active);

650
client/cl_cin.c Normal file
View File

@ -0,0 +1,650 @@
/*
Copyright (C) 1997-2001 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.
*/
#include "client.h"
typedef struct
{
byte *data;
int count;
} cblock_t;
typedef struct
{
qboolean restart_sound;
int s_rate;
int s_width;
int s_channels;
int width;
int height;
byte *pic;
byte *pic_pending;
// order 1 huffman stuff
int *hnodes1; // [256][256][2];
int numhnodes1[256];
int h_used[512];
int h_count[512];
} cinematics_t;
cinematics_t cin;
/*
=================================================================
PCX LOADING
=================================================================
*/
/*
==============
SCR_LoadPCX
==============
*/
void SCR_LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
{
byte *raw;
pcx_t *pcx;
int x, y;
int len;
int dataByte, runLength;
byte *out, *pix;
*pic = NULL;
//
// load the file
//
len = FS_LoadFile (filename, (void **)&raw);
if (!raw)
return; // Com_Printf ("Bad pcx file %s\n", filename);
//
// parse the PCX file
//
pcx = (pcx_t *)raw;
raw = &pcx->data;
if (pcx->manufacturer != 0x0a
|| pcx->version != 5
|| pcx->encoding != 1
|| pcx->bits_per_pixel != 8
|| pcx->xmax >= 640
|| pcx->ymax >= 480)
{
Com_Printf ("Bad pcx file %s\n", filename);
return;
}
out = Z_Malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
*pic = out;
pix = out;
if (palette)
{
*palette = Z_Malloc(768);
memcpy (*palette, (byte *)pcx + len - 768, 768);
}
if (width)
*width = pcx->xmax+1;
if (height)
*height = pcx->ymax+1;
for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
{
for (x=0 ; x<=pcx->xmax ; )
{
dataByte = *raw++;
if((dataByte & 0xC0) == 0xC0)
{
runLength = dataByte & 0x3F;
dataByte = *raw++;
}
else
runLength = 1;
while(runLength-- > 0)
pix[x++] = dataByte;
}
}
if ( raw - (byte *)pcx > len)
{
Com_Printf ("PCX file %s was malformed", filename);
Z_Free (*pic);
*pic = NULL;
}
FS_FreeFile (pcx);
}
//=============================================================
/*
==================
SCR_StopCinematic
==================
*/
void SCR_StopCinematic (void)
{
cl.cinematictime = 0; // done
if (cin.pic)
{
Z_Free (cin.pic);
cin.pic = NULL;
}
if (cin.pic_pending)
{
Z_Free (cin.pic_pending);
cin.pic_pending = NULL;
}
if (cl.cinematicpalette_active)
{
re.CinematicSetPalette(NULL);
cl.cinematicpalette_active = false;
}
if (cl.cinematic_file)
{
fclose (cl.cinematic_file);
cl.cinematic_file = NULL;
}
if (cin.hnodes1)
{
Z_Free (cin.hnodes1);
cin.hnodes1 = NULL;
}
// switch back down to 11 khz sound if necessary
if (cin.restart_sound)
{
cin.restart_sound = false;
CL_Snd_Restart_f ();
}
}
/*
====================
SCR_FinishCinematic
Called when either the cinematic completes, or it is aborted
====================
*/
void SCR_FinishCinematic (void)
{
// tell the server to advance to the next map / cinematic
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
SZ_Print (&cls.netchan.message, va("nextserver %i\n", cl.servercount));
}
//==========================================================================
/*
==================
SmallestNode1
==================
*/
int SmallestNode1 (int numhnodes)
{
int i;
int best, bestnode;
best = 99999999;
bestnode = -1;
for (i=0 ; i<numhnodes ; i++)
{
if (cin.h_used[i])
continue;
if (!cin.h_count[i])
continue;
if (cin.h_count[i] < best)
{
best = cin.h_count[i];
bestnode = i;
}
}
if (bestnode == -1)
return -1;
cin.h_used[bestnode] = true;
return bestnode;
}
/*
==================
Huff1TableInit
Reads the 64k counts table and initializes the node trees
==================
*/
void Huff1TableInit (void)
{
int prev;
int j;
int *node, *nodebase;
byte counts[256];
int numhnodes;
cin.hnodes1 = Z_Malloc (256*256*2*4);
memset (cin.hnodes1, 0, 256*256*2*4);
for (prev=0 ; prev<256 ; prev++)
{
memset (cin.h_count,0,sizeof(cin.h_count));
memset (cin.h_used,0,sizeof(cin.h_used));
// read a row of counts
FS_Read (counts, sizeof(counts), cl.cinematic_file);
for (j=0 ; j<256 ; j++)
cin.h_count[j] = counts[j];
// build the nodes
numhnodes = 256;
nodebase = cin.hnodes1 + prev*256*2;
while (numhnodes != 511)
{
node = nodebase + (numhnodes-256)*2;
// pick two lowest counts
node[0] = SmallestNode1 (numhnodes);
if (node[0] == -1)
break; // no more
node[1] = SmallestNode1 (numhnodes);
if (node[1] == -1)
break;
cin.h_count[numhnodes] = cin.h_count[node[0]] + cin.h_count[node[1]];
numhnodes++;
}
cin.numhnodes1[prev] = numhnodes-1;
}
}
/*
==================
Huff1Decompress
==================
*/
cblock_t Huff1Decompress (cblock_t in)
{
byte *input;
byte *out_p;
int nodenum;
int count;
cblock_t out;
int inbyte;
int *hnodes, *hnodesbase;
//int i;
// get decompressed count
count = in.data[0] + (in.data[1]<<8) + (in.data[2]<<16) + (in.data[3]<<24);
input = in.data + 4;
out_p = out.data = Z_Malloc (count);
// read bits
hnodesbase = cin.hnodes1 - 256*2; // nodes 0-255 aren't stored
hnodes = hnodesbase;
nodenum = cin.numhnodes1[0];
while (count)
{
inbyte = *input++;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
//-----------
if (nodenum < 256)
{
hnodes = hnodesbase + (nodenum<<9);
*out_p++ = nodenum;
if (!--count)
break;
nodenum = cin.numhnodes1[nodenum];
}
nodenum = hnodes[nodenum*2 + (inbyte&1)];
inbyte >>=1;
}
if (input - in.data != in.count && input - in.data != in.count+1)
{
Com_Printf ("Decompression overread by %i", (input - in.data) - in.count);
}
out.count = out_p - out.data;
return out;
}
/*
==================
SCR_ReadNextFrame
==================
*/
byte *SCR_ReadNextFrame (void)
{
int r;
int command;
byte samples[22050/14*4];
byte compressed[0x20000];
int size;
byte *pic;
cblock_t in, huf1;
int start, end, count;
// read the next frame
r = fread (&command, 4, 1, cl.cinematic_file);
if (r == 0) // we'll give it one more chance
r = fread (&command, 4, 1, cl.cinematic_file);
if (r != 1)
return NULL;
command = LittleLong(command);
if (command == 2)
return NULL; // last frame marker
if (command == 1)
{ // read palette
FS_Read (cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file);
cl.cinematicpalette_active=0; // dubious.... exposes an edge case
}
// decompress the next frame
FS_Read (&size, 4, cl.cinematic_file);
size = LittleLong(size);
if (size > sizeof(compressed) || size < 1)
Com_Error (ERR_DROP, "Bad compressed frame size");
FS_Read (compressed, size, cl.cinematic_file);
// read sound
start = cl.cinematicframe*cin.s_rate/14;
end = (cl.cinematicframe+1)*cin.s_rate/14;
count = end - start;
FS_Read (samples, count*cin.s_width*cin.s_channels, cl.cinematic_file);
S_RawSamples (count, cin.s_rate, cin.s_width, cin.s_channels, samples);
in.data = compressed;
in.count = size;
huf1 = Huff1Decompress (in);
pic = huf1.data;
cl.cinematicframe++;
return pic;
}
/*
==================
SCR_RunCinematic
==================
*/
void SCR_RunCinematic (void)
{
int frame;
if (cl.cinematictime <= 0)
{
SCR_StopCinematic ();
return;
}
if (cl.cinematicframe == -1)
return; // static image
if (cls.key_dest != key_game)
{ // pause if menu or console is up
cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14;
return;
}
frame = (cls.realtime - cl.cinematictime)*14.0/1000;
if (frame <= cl.cinematicframe)
return;
if (frame > cl.cinematicframe+1)
{
Com_Printf ("Dropped frame: %i > %i\n", frame, cl.cinematicframe+1);
cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14;
}
if (cin.pic)
Z_Free (cin.pic);
cin.pic = cin.pic_pending;
cin.pic_pending = NULL;
cin.pic_pending = SCR_ReadNextFrame ();
if (!cin.pic_pending)
{
SCR_StopCinematic ();
SCR_FinishCinematic ();
cl.cinematictime = 1; // hack to get the black screen behind loading
SCR_BeginLoadingPlaque ();
cl.cinematictime = 0;
return;
}
}
/*
==================
SCR_DrawCinematic
Returns true if a cinematic is active, meaning the view rendering
should be skipped
==================
*/
qboolean SCR_DrawCinematic (void)
{
if (cl.cinematictime <= 0)
{
return false;
}
if (cls.key_dest == key_menu)
{ // blank screen and pause if menu is up
re.CinematicSetPalette(NULL);
cl.cinematicpalette_active = false;
return true;
}
if (!cl.cinematicpalette_active)
{
re.CinematicSetPalette(cl.cinematicpalette);
cl.cinematicpalette_active = true;
}
if (!cin.pic)
return true;
re.DrawStretchRaw (0, 0, viddef.width, viddef.height,
cin.width, cin.height, cin.pic);
return true;
}
/*
==================
SCR_PlayCinematic
==================
*/
void SCR_PlayCinematic (char *arg)
{
int width, height;
byte *palette;
char name[MAX_OSPATH], *dot;
int old_khz;
// make sure CD isn't playing music
CDAudio_Stop();
cl.cinematicframe = 0;
dot = strstr (arg, ".");
if (dot && !strcmp (dot, ".pcx"))
{ // static pcx image
Com_sprintf (name, sizeof(name), "pics/%s", arg);
SCR_LoadPCX (name, &cin.pic, &palette, &cin.width, &cin.height);
cl.cinematicframe = -1;
cl.cinematictime = 1;
SCR_EndLoadingPlaque ();
cls.state = ca_active;
if (!cin.pic)
{
Com_Printf ("%s not found.\n", name);
cl.cinematictime = 0;
}
else
{
memcpy (cl.cinematicpalette, palette, sizeof(cl.cinematicpalette));
Z_Free (palette);
}
return;
}
Com_sprintf (name, sizeof(name), "video/%s", arg);
FS_FOpenFile (name, &cl.cinematic_file);
if (!cl.cinematic_file)
{
// Com_Error (ERR_DROP, "Cinematic %s not found.\n", name);
SCR_FinishCinematic ();
cl.cinematictime = 0; // done
return;
}
SCR_EndLoadingPlaque ();
cls.state = ca_active;
FS_Read (&width, 4, cl.cinematic_file);
FS_Read (&height, 4, cl.cinematic_file);
cin.width = LittleLong(width);
cin.height = LittleLong(height);
FS_Read (&cin.s_rate, 4, cl.cinematic_file);
cin.s_rate = LittleLong(cin.s_rate);
FS_Read (&cin.s_width, 4, cl.cinematic_file);
cin.s_width = LittleLong(cin.s_width);
FS_Read (&cin.s_channels, 4, cl.cinematic_file);
cin.s_channels = LittleLong(cin.s_channels);
Huff1TableInit ();
// switch up to 22 khz sound if necessary
old_khz = Cvar_VariableValue ("s_khz");
if (old_khz != cin.s_rate/1000)
{
cin.restart_sound = true;
Cvar_SetValue ("s_khz", cin.s_rate/1000);
CL_Snd_Restart_f ();
Cvar_SetValue ("s_khz", old_khz);
}
cl.cinematicframe = 0;
cin.pic = SCR_ReadNextFrame ();
cl.cinematictime = Sys_Milliseconds ();
}

1500
client/cl_ents.c Normal file

File diff suppressed because it is too large Load Diff

2298
client/cl_fx.c Normal file

File diff suppressed because it is too large Load Diff

542
client/cl_input.c Normal file
View File

@ -0,0 +1,542 @@
/*
Copyright (C) 1997-2001 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.
*/
// cl.input.c -- builds an intended movement command to send to the server
#include "client.h"
cvar_t *cl_nodelta;
extern unsigned sys_frame_time;
unsigned frame_msec;
unsigned old_sys_frame_time;
/*
===============================================================================
KEY BUTTONS
Continuous button event tracking is complicated by the fact that two different
input sources (say, mouse button 1 and the control key) can both press the
same button, but the button should only be released when both of the
pressing key have been released.
When a key event issues a button command (+forward, +attack, etc), it appends
its key number as a parameter to the command so it can be matched up with
the release.
state bit 0 is the current state of the key
state bit 1 is edge triggered on the up to down transition
state bit 2 is edge triggered on the down to up transition
Key_Event (int key, qboolean down, unsigned time);
+mlook src time
===============================================================================
*/
kbutton_t in_klook;
kbutton_t in_left, in_right, in_forward, in_back;
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
kbutton_t in_strafe, in_speed, in_use, in_attack;
kbutton_t in_up, in_down;
int in_impulse;
void KeyDown (kbutton_t *b)
{
int k;
char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
k = -1; // typed manually at the console for continuous down
if (k == b->down[0] || k == b->down[1])
return; // repeating key
if (!b->down[0])
b->down[0] = k;
else if (!b->down[1])
b->down[1] = k;
else
{
Com_Printf ("Three keys down for a button!\n");
return;
}
if (b->state & 1)
return; // still down
// save timestamp
c = Cmd_Argv(2);
b->downtime = atoi(c);
if (!b->downtime)
b->downtime = sys_frame_time - 100;
b->state |= 1 + 2; // down + impulse down
}
void KeyUp (kbutton_t *b)
{
int k;
char *c;
unsigned uptime;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
{ // typed manually at the console, assume for unsticking, so clear all
b->down[0] = b->down[1] = 0;
b->state = 4; // impulse up
return;
}
if (b->down[0] == k)
b->down[0] = 0;
else if (b->down[1] == k)
b->down[1] = 0;
else
return; // key up without coresponding down (menu pass through)
if (b->down[0] || b->down[1])
return; // some other key is still holding it down
if (!(b->state & 1))
return; // still up (this should not happen)
// save timestamp
c = Cmd_Argv(2);
uptime = atoi(c);
if (uptime)
b->msec += uptime - b->downtime;
else
b->msec += 10;
b->state &= ~1; // now up
b->state |= 4; // impulse up
}
void IN_KLookDown (void) {KeyDown(&in_klook);}
void IN_KLookUp (void) {KeyUp(&in_klook);}
void IN_UpDown(void) {KeyDown(&in_up);}
void IN_UpUp(void) {KeyUp(&in_up);}
void IN_DownDown(void) {KeyDown(&in_down);}
void IN_DownUp(void) {KeyUp(&in_down);}
void IN_LeftDown(void) {KeyDown(&in_left);}
void IN_LeftUp(void) {KeyUp(&in_left);}
void IN_RightDown(void) {KeyDown(&in_right);}
void IN_RightUp(void) {KeyUp(&in_right);}
void IN_ForwardDown(void) {KeyDown(&in_forward);}
void IN_ForwardUp(void) {KeyUp(&in_forward);}
void IN_BackDown(void) {KeyDown(&in_back);}
void IN_BackUp(void) {KeyUp(&in_back);}
void IN_LookupDown(void) {KeyDown(&in_lookup);}
void IN_LookupUp(void) {KeyUp(&in_lookup);}
void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
void IN_MoverightDown(void) {KeyDown(&in_moveright);}
void IN_MoverightUp(void) {KeyUp(&in_moveright);}
void IN_SpeedDown(void) {KeyDown(&in_speed);}
void IN_SpeedUp(void) {KeyUp(&in_speed);}
void IN_StrafeDown(void) {KeyDown(&in_strafe);}
void IN_StrafeUp(void) {KeyUp(&in_strafe);}
void IN_AttackDown(void) {KeyDown(&in_attack);}
void IN_AttackUp(void) {KeyUp(&in_attack);}
void IN_UseDown (void) {KeyDown(&in_use);}
void IN_UseUp (void) {KeyUp(&in_use);}
void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
/*
===============
CL_KeyState
Returns the fraction of the frame that the key was down
===============
*/
float CL_KeyState (kbutton_t *key)
{
float val;
int msec;
key->state &= 1; // clear impulses
msec = key->msec;
key->msec = 0;
if (key->state)
{ // still down
msec += sys_frame_time - key->downtime;
key->downtime = sys_frame_time;
}
#if 0
if (msec)
{
Com_Printf ("%i ", msec);
}
#endif
val = (float)msec / frame_msec;
if (val < 0)
val = 0;
if (val > 1)
val = 1;
return val;
}
//==========================================================================
cvar_t *cl_upspeed;
cvar_t *cl_forwardspeed;
cvar_t *cl_sidespeed;
cvar_t *cl_yawspeed;
cvar_t *cl_pitchspeed;
cvar_t *cl_run;
cvar_t *cl_anglespeedkey;
/*
================
CL_AdjustAngles
Moves the local angle positions
================
*/
void CL_AdjustAngles (void)
{
float speed;
float up, down;
if (in_speed.state & 1)
speed = cls.frametime * cl_anglespeedkey->value;
else
speed = cls.frametime;
if (!(in_strafe.state & 1))
{
cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
}
if (in_klook.state & 1)
{
cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_forward);
cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_back);
}
up = CL_KeyState (&in_lookup);
down = CL_KeyState(&in_lookdown);
cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * up;
cl.viewangles[PITCH] += speed*cl_pitchspeed->value * down;
}
/*
================
CL_BaseMove
Send the intended movement message to the server
================
*/
void CL_BaseMove (usercmd_t *cmd)
{
CL_AdjustAngles ();
memset (cmd, 0, sizeof(*cmd));
VectorCopy (cl.viewangles, cmd->angles);
if (in_strafe.state & 1)
{
cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_right);
cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_left);
}
cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright);
cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft);
cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up);
cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down);
if (! (in_klook.state & 1) )
{
cmd->forwardmove += cl_forwardspeed->value * CL_KeyState (&in_forward);
cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState (&in_back);
}
//
// adjust for speed key / running
//
if ( (in_speed.state & 1) ^ (int)(cl_run->value) )
{
cmd->forwardmove *= 2;
cmd->sidemove *= 2;
cmd->upmove *= 2;
}
}
void CL_ClampPitch (void)
{
float pitch;
pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
if (pitch > 180)
pitch -= 360;
if (cl.viewangles[PITCH] + pitch > 89)
cl.viewangles[PITCH] = 89 - pitch;
if (cl.viewangles[PITCH] + pitch < -89)
cl.viewangles[PITCH] = -89 - pitch;
}
/*
==============
CL_FinishMove
==============
*/
void CL_FinishMove (usercmd_t *cmd)
{
int ms;
int i;
//
// figure button bits
//
if ( in_attack.state & 3 )
cmd->buttons |= BUTTON_ATTACK;
in_attack.state &= ~2;
if (in_use.state & 3)
cmd->buttons |= BUTTON_USE;
in_use.state &= ~2;
if (anykeydown && cls.key_dest == key_game)
cmd->buttons |= BUTTON_ANY;
// send milliseconds of time to apply the move
ms = cls.frametime * 1000;
if (ms > 250)
ms = 100; // time was unreasonable
cmd->msec = ms;
CL_ClampPitch ();
for (i=0 ; i<3 ; i++)
cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
cmd->impulse = in_impulse;
in_impulse = 0;
// send the ambient light level at the player's current position
cmd->lightlevel = (byte)cl_lightlevel->value;
}
/*
=================
CL_CreateCmd
=================
*/
usercmd_t CL_CreateCmd (void)
{
usercmd_t cmd;
frame_msec = sys_frame_time - old_sys_frame_time;
if (frame_msec < 1)
frame_msec = 1;
if (frame_msec > 200)
frame_msec = 200;
// get basic movement from keyboard
CL_BaseMove (&cmd);
// allow mice or other external controllers to add to the move
IN_Move (&cmd);
CL_FinishMove (&cmd);
old_sys_frame_time = sys_frame_time;
//cmd.impulse = cls.framecount;
return cmd;
}
void IN_CenterView (void)
{
cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
}
/*
============
CL_InitInput
============
*/
void CL_InitInput (void)
{
Cmd_AddCommand ("centerview",IN_CenterView);
Cmd_AddCommand ("+moveup",IN_UpDown);
Cmd_AddCommand ("-moveup",IN_UpUp);
Cmd_AddCommand ("+movedown",IN_DownDown);
Cmd_AddCommand ("-movedown",IN_DownUp);
Cmd_AddCommand ("+left",IN_LeftDown);
Cmd_AddCommand ("-left",IN_LeftUp);
Cmd_AddCommand ("+right",IN_RightDown);
Cmd_AddCommand ("-right",IN_RightUp);
Cmd_AddCommand ("+forward",IN_ForwardDown);
Cmd_AddCommand ("-forward",IN_ForwardUp);
Cmd_AddCommand ("+back",IN_BackDown);
Cmd_AddCommand ("-back",IN_BackUp);
Cmd_AddCommand ("+lookup", IN_LookupDown);
Cmd_AddCommand ("-lookup", IN_LookupUp);
Cmd_AddCommand ("+lookdown", IN_LookdownDown);
Cmd_AddCommand ("-lookdown", IN_LookdownUp);
Cmd_AddCommand ("+strafe", IN_StrafeDown);
Cmd_AddCommand ("-strafe", IN_StrafeUp);
Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
Cmd_AddCommand ("+moveright", IN_MoverightDown);
Cmd_AddCommand ("-moveright", IN_MoverightUp);
Cmd_AddCommand ("+speed", IN_SpeedDown);
Cmd_AddCommand ("-speed", IN_SpeedUp);
Cmd_AddCommand ("+attack", IN_AttackDown);
Cmd_AddCommand ("-attack", IN_AttackUp);
Cmd_AddCommand ("+use", IN_UseDown);
Cmd_AddCommand ("-use", IN_UseUp);
Cmd_AddCommand ("impulse", IN_Impulse);
Cmd_AddCommand ("+klook", IN_KLookDown);
Cmd_AddCommand ("-klook", IN_KLookUp);
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
}
/*
=================
CL_SendCmd
=================
*/
void CL_SendCmd (void)
{
sizebuf_t buf;
byte data[128];
int i;
usercmd_t *cmd, *oldcmd;
usercmd_t nullcmd;
int checksumIndex;
// build a command even if not connected
// save this command off for prediction
i = cls.netchan.outgoing_sequence & (CMD_BACKUP-1);
cmd = &cl.cmds[i];
cl.cmd_time[i] = cls.realtime; // for netgraph ping calculation
*cmd = CL_CreateCmd ();
cl.cmd = *cmd;
if (cls.state == ca_disconnected || cls.state == ca_connecting)
return;
if ( cls.state == ca_connected)
{
if (cls.netchan.message.cursize || curtime - cls.netchan.last_sent > 1000 )
Netchan_Transmit (&cls.netchan, 0, buf.data);
return;
}
// send a userinfo update if needed
if (userinfo_modified)
{
CL_FixUpGender();
userinfo_modified = false;
MSG_WriteByte (&cls.netchan.message, clc_userinfo);
MSG_WriteString (&cls.netchan.message, Cvar_Userinfo() );
}
SZ_Init (&buf, data, sizeof(data));
if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop
&& cls.realtime - cl.cinematictime > 1000)
{ // skip the rest of the cinematic
SCR_FinishCinematic ();
}
// begin a client move command
MSG_WriteByte (&buf, clc_move);
// save the position for a checksum byte
checksumIndex = buf.cursize;
MSG_WriteByte (&buf, 0);
// let the server know what the last frame we
// got was, so the next message can be delta compressed
if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
MSG_WriteLong (&buf, -1); // no compression
else
MSG_WriteLong (&buf, cl.frame.serverframe);
// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
i = (cls.netchan.outgoing_sequence-2) & (CMD_BACKUP-1);
cmd = &cl.cmds[i];
memset (&nullcmd, 0, sizeof(nullcmd));
MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd);
oldcmd = cmd;
i = (cls.netchan.outgoing_sequence-1) & (CMD_BACKUP-1);
cmd = &cl.cmds[i];
MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
oldcmd = cmd;
i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP-1);
cmd = &cl.cmds[i];
MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
// calculate a checksum over the move commands
buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
cls.netchan.outgoing_sequence);
//
// deliver the message
//
Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);
}

142
client/cl_inv.c Normal file
View File

@ -0,0 +1,142 @@
/*
Copyright (C) 1997-2001 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.
*/
// cl_inv.c -- client inventory screen
#include "client.h"
/*
================
CL_ParseInventory
================
*/
void CL_ParseInventory (void)
{
int i;
for (i=0 ; i<MAX_ITEMS ; i++)
cl.inventory[i] = MSG_ReadShort (&net_message);
}
/*
================
Inv_DrawString
================
*/
void Inv_DrawString (int x, int y, char *string)
{
while (*string)
{
re.DrawChar (x, y, *string);
x+=8;
string++;
}
}
void SetStringHighBit (char *s)
{
while (*s)
*s++ |= 128;
}
/*
================
CL_DrawInventory
================
*/
#define DISPLAY_ITEMS 17
void CL_DrawInventory (void)
{
int i, j;
int num, selected_num, item;
int index[MAX_ITEMS];
char string[1024];
int x, y;
char binding[1024];
char *bind;
int selected;
int top;
selected = cl.frame.playerstate.stats[STAT_SELECTED_ITEM];
num = 0;
selected_num = 0;
for (i=0 ; i<MAX_ITEMS ; i++)
{
if (i==selected)
selected_num = num;
if (cl.inventory[i])
{
index[num] = i;
num++;
}
}
// determine scroll point
top = selected_num - DISPLAY_ITEMS/2;
if (num - top < DISPLAY_ITEMS)
top = num - DISPLAY_ITEMS;
if (top < 0)
top = 0;
x = (viddef.width-256)/2;
y = (viddef.height-240)/2;
// repaint everything next frame
SCR_DirtyScreen ();
re.DrawPic (x, y+8, "inventory");
y += 24;
x += 24;
Inv_DrawString (x, y, "hotkey ### item");
Inv_DrawString (x, y+8, "------ --- ----");
y += 16;
for (i=top ; i<num && i < top+DISPLAY_ITEMS ; i++)
{
item = index[i];
// search for a binding
Com_sprintf (binding, sizeof(binding), "use %s", cl.configstrings[CS_ITEMS+item]);
bind = "";
for (j=0 ; j<256 ; j++)
if (keybindings[j] && !Q_stricmp (keybindings[j], binding))
{
bind = Key_KeynumToString(j);
break;
}
Com_sprintf (string, sizeof(string), "%6s %3i %s", bind, cl.inventory[item],
cl.configstrings[CS_ITEMS+item] );
if (item != selected)
SetStringHighBit (string);
else // draw a blinky cursor by the selected item
{
if ( (int)(cls.realtime*10) & 1)
re.DrawChar (x-8, y, 15);
}
Inv_DrawString (x, y, string);
y += 8;
}
}

1844
client/cl_main.c Normal file

File diff suppressed because it is too large Load Diff

1323
client/cl_newfx.c Normal file

File diff suppressed because it is too large Load Diff

806
client/cl_parse.c Normal file
View File

@ -0,0 +1,806 @@
/*
Copyright (C) 1997-2001 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.
*/
// cl_parse.c -- parse a message received from the server
#include "client.h"
char *svc_strings[256] =
{
"svc_bad",
"svc_muzzleflash",
"svc_muzzlflash2",
"svc_temp_entity",
"svc_layout",
"svc_inventory",
"svc_nop",
"svc_disconnect",
"svc_reconnect",
"svc_sound",
"svc_print",
"svc_stufftext",
"svc_serverdata",
"svc_configstring",
"svc_spawnbaseline",
"svc_centerprint",
"svc_download",
"svc_playerinfo",
"svc_packetentities",
"svc_deltapacketentities",
"svc_frame"
};
//=============================================================================
void CL_DownloadFileName(char *dest, int destlen, char *fn)
{
if (strncmp(fn, "players", 7) == 0)
Com_sprintf (dest, destlen, "%s/%s", BASEDIRNAME, fn);
else
Com_sprintf (dest, destlen, "%s/%s", FS_Gamedir(), fn);
}
/*
===============
CL_CheckOrDownloadFile
Returns true if the file exists, otherwise it attempts
to start a download from the server.
===============
*/
qboolean CL_CheckOrDownloadFile (char *filename)
{
FILE *fp;
char name[MAX_OSPATH];
if (strstr (filename, ".."))
{
Com_Printf ("Refusing to download a path with ..\n");
return true;
}
if (FS_LoadFile (filename, NULL) != -1)
{ // it exists, no need to download
return true;
}
strcpy (cls.downloadname, filename);
// download to a temp name, and only rename
// to the real name when done, so if interrupted
// a runt file wont be left
COM_StripExtension (cls.downloadname, cls.downloadtempname);
strcat (cls.downloadtempname, ".tmp");
//ZOID
// check to see if we already have a tmp for this file, if so, try to resume
// open the file if not opened yet
CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
// FS_CreatePath (name);
fp = fopen (name, "r+b");
if (fp) { // it exists
int len;
fseek(fp, 0, SEEK_END);
len = ftell(fp);
cls.download = fp;
// give the server an offset to start the download
Com_Printf ("Resuming %s\n", cls.downloadname);
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
MSG_WriteString (&cls.netchan.message,
va("download %s %i", cls.downloadname, len));
} else {
Com_Printf ("Downloading %s\n", cls.downloadname);
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
MSG_WriteString (&cls.netchan.message,
va("download %s", cls.downloadname));
}
cls.downloadnumber++;
return false;
}
/*
===============
CL_Download_f
Request a download from the server
===============
*/
void CL_Download_f (void)
{
char filename[MAX_OSPATH];
if (Cmd_Argc() != 2) {
Com_Printf("Usage: download <filename>\n");
return;
}
Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1));
if (strstr (filename, ".."))
{
Com_Printf ("Refusing to download a path with ..\n");
return;
}
if (FS_LoadFile (filename, NULL) != -1)
{ // it exists, no need to download
Com_Printf("File already exists.\n");
return;
}
strcpy (cls.downloadname, filename);
Com_Printf ("Downloading %s\n", cls.downloadname);
// download to a temp name, and only rename
// to the real name when done, so if interrupted
// a runt file wont be left
COM_StripExtension (cls.downloadname, cls.downloadtempname);
strcat (cls.downloadtempname, ".tmp");
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
MSG_WriteString (&cls.netchan.message,
va("download %s", cls.downloadname));
cls.downloadnumber++;
}
/*
======================
CL_RegisterSounds
======================
*/
void CL_RegisterSounds (void)
{
int i;
S_BeginRegistration ();
CL_RegisterTEntSounds ();
for (i=1 ; i<MAX_SOUNDS ; i++)
{
if (!cl.configstrings[CS_SOUNDS+i][0])
break;
cl.sound_precache[i] = S_RegisterSound (cl.configstrings[CS_SOUNDS+i]);
Sys_SendKeyEvents (); // pump message loop
}
S_EndRegistration ();
}
/*
=====================
CL_ParseDownload
A download message has been received from the server
=====================
*/
void CL_ParseDownload (void)
{
int size, percent;
char name[MAX_OSPATH];
int r;
// read the data
size = MSG_ReadShort (&net_message);
percent = MSG_ReadByte (&net_message);
if (size == -1)
{
Com_Printf ("Server does not have this file.\n");
if (cls.download)
{
// if here, we tried to resume a file but the server said no
fclose (cls.download);
cls.download = NULL;
}
CL_RequestNextDownload ();
return;
}
// open the file if not opened yet
if (!cls.download)
{
CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
FS_CreatePath (name);
cls.download = fopen (name, "wb");
if (!cls.download)
{
net_message.readcount += size;
Com_Printf ("Failed to open %s\n", cls.downloadtempname);
CL_RequestNextDownload ();
return;
}
}
fwrite (net_message.data + net_message.readcount, 1, size, cls.download);
net_message.readcount += size;
if (percent != 100)
{
// request next block
// change display routines by zoid
#if 0
Com_Printf (".");
if (10*(percent/10) != cls.downloadpercent)
{
cls.downloadpercent = 10*(percent/10);
Com_Printf ("%i%%", cls.downloadpercent);
}
#endif
cls.downloadpercent = percent;
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
SZ_Print (&cls.netchan.message, "nextdl");
}
else
{
char oldn[MAX_OSPATH];
char newn[MAX_OSPATH];
// Com_Printf ("100%%\n");
fclose (cls.download);
// rename the temp file to it's final name
CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
r = rename (oldn, newn);
if (r)
Com_Printf ("failed to rename.\n");
cls.download = NULL;
cls.downloadpercent = 0;
// get another file if needed
CL_RequestNextDownload ();
}
}
/*
=====================================================================
SERVER CONNECTING MESSAGES
=====================================================================
*/
/*
==================
CL_ParseServerData
==================
*/
void CL_ParseServerData (void)
{
extern cvar_t *fs_gamedirvar;
char *str;
int i;
Com_DPrintf ("Serverdata packet received.\n");
//
// wipe the client_state_t struct
//
CL_ClearState ();
cls.state = ca_connected;
// parse protocol version number
i = MSG_ReadLong (&net_message);
cls.serverProtocol = i;
// BIG HACK to let demos from release work with the 3.0x patch!!!
if (Com_ServerState() && PROTOCOL_VERSION == 34)
{
}
else if (i != PROTOCOL_VERSION)
Com_Error (ERR_DROP,"Server returned version %i, not %i", i, PROTOCOL_VERSION);
cl.servercount = MSG_ReadLong (&net_message);
cl.attractloop = MSG_ReadByte (&net_message);
// game directory
str = MSG_ReadString (&net_message);
strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1);
// set gamedir
if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
Cvar_Set("game", str);
// parse player entity number
cl.playernum = MSG_ReadShort (&net_message);
// get the full level name
str = MSG_ReadString (&net_message);
if (cl.playernum == -1)
{ // playing a cinematic or showing a pic, not a level
SCR_PlayCinematic (str);
}
else
{
// seperate the printfs so the server message can have a color
Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
Com_Printf ("%c%s\n", 2, str);
// need to prep refresh at next oportunity
cl.refresh_prepped = false;
}
}
/*
==================
CL_ParseBaseline
==================
*/
void CL_ParseBaseline (void)
{
entity_state_t *es;
int bits;
int newnum;
entity_state_t nullstate;
memset (&nullstate, 0, sizeof(nullstate));
newnum = CL_ParseEntityBits (&bits);
es = &cl_entities[newnum].baseline;
CL_ParseDelta (&nullstate, es, newnum, bits);
}
/*
================
CL_LoadClientinfo
================
*/
void CL_LoadClientinfo (clientinfo_t *ci, char *s)
{
int i;
char *t;
char model_name[MAX_QPATH];
char skin_name[MAX_QPATH];
char model_filename[MAX_QPATH];
char skin_filename[MAX_QPATH];
char weapon_filename[MAX_QPATH];
strncpy(ci->cinfo, s, sizeof(ci->cinfo));
ci->cinfo[sizeof(ci->cinfo)-1] = 0;
// isolate the player's name
strncpy(ci->name, s, sizeof(ci->name));
ci->name[sizeof(ci->name)-1] = 0;
t = strstr (s, "\\");
if (t)
{
ci->name[t-s] = 0;
s = t+1;
}
if (cl_noskins->value || *s == 0)
{
Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/weapon.md2");
Com_sprintf (skin_filename, sizeof(skin_filename), "players/male/grunt.pcx");
Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/male/grunt_i.pcx");
ci->model = re.RegisterModel (model_filename);
memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
ci->weaponmodel[0] = re.RegisterModel (weapon_filename);
ci->skin = re.RegisterSkin (skin_filename);
ci->icon = re.RegisterPic (ci->iconname);
}
else
{
// isolate the model name
strcpy (model_name, s);
t = strstr(model_name, "/");
if (!t)
t = strstr(model_name, "\\");
if (!t)
t = model_name;
*t = 0;
// isolate the skin name
strcpy (skin_name, s + strlen(model_name) + 1);
// model file
Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
ci->model = re.RegisterModel (model_filename);
if (!ci->model)
{
strcpy(model_name, "male");
Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
ci->model = re.RegisterModel (model_filename);
}
// skin file
Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
ci->skin = re.RegisterSkin (skin_filename);
// if we don't have the skin and the model wasn't male,
// see if the male has it (this is for CTF's skins)
if (!ci->skin && Q_stricmp(model_name, "male"))
{
// change model to male
strcpy(model_name, "male");
Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
ci->model = re.RegisterModel (model_filename);
// see if the skin exists for the male model
Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
ci->skin = re.RegisterSkin (skin_filename);
}
// if we still don't have a skin, it means that the male model didn't have
// it, so default to grunt
if (!ci->skin) {
// see if the skin exists for the male model
Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name);
ci->skin = re.RegisterSkin (skin_filename);
}
// weapon file
for (i = 0; i < num_cl_weaponmodels; i++) {
Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]);
ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0) {
// try male
Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]);
ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
}
if (!cl_vwep->value)
break; // only one when vwep is off
}
// icon file
Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
ci->icon = re.RegisterPic (ci->iconname);
}
// must have loaded all data types to be valud
if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
{
ci->skin = NULL;
ci->icon = NULL;
ci->model = NULL;
ci->weaponmodel[0] = NULL;
return;
}
}
/*
================
CL_ParseClientinfo
Load the skin, icon, and model for a client
================
*/
void CL_ParseClientinfo (int player)
{
char *s;
clientinfo_t *ci;
s = cl.configstrings[player+CS_PLAYERSKINS];
ci = &cl.clientinfo[player];
CL_LoadClientinfo (ci, s);
}
/*
================
CL_ParseConfigString
================
*/
void CL_ParseConfigString (void)
{
int i;
char *s;
i = MSG_ReadShort (&net_message);
if (i < 0 || i >= MAX_CONFIGSTRINGS)
Com_Error (ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
s = MSG_ReadString(&net_message);
strcpy (cl.configstrings[i], s);
// do something apropriate
if (i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES)
CL_SetLightstyle (i - CS_LIGHTS);
else if (i == CS_CDTRACK)
{
if (cl.refresh_prepped)
CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
}
else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS)
{
if (cl.refresh_prepped)
{
cl.model_draw[i-CS_MODELS] = re.RegisterModel (cl.configstrings[i]);
if (cl.configstrings[i][0] == '*')
cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]);
else
cl.model_clip[i-CS_MODELS] = NULL;
}
}
else if (i >= CS_SOUNDS && i < CS_SOUNDS+MAX_MODELS)
{
if (cl.refresh_prepped)
cl.sound_precache[i-CS_SOUNDS] = S_RegisterSound (cl.configstrings[i]);
}
else if (i >= CS_IMAGES && i < CS_IMAGES+MAX_MODELS)
{
if (cl.refresh_prepped)
cl.image_precache[i-CS_IMAGES] = re.RegisterPic (cl.configstrings[i]);
}
else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS+MAX_CLIENTS)
{
if (cl.refresh_prepped)
CL_ParseClientinfo (i-CS_PLAYERSKINS);
}
}
/*
=====================================================================
ACTION MESSAGES
=====================================================================
*/
/*
==================
CL_ParseStartSoundPacket
==================
*/
void CL_ParseStartSoundPacket(void)
{
vec3_t pos_v;
float *pos;
int channel, ent;
int sound_num;
float volume;
float attenuation;
int flags;
float ofs;
flags = MSG_ReadByte (&net_message);
sound_num = MSG_ReadByte (&net_message);
if (flags & SND_VOLUME)
volume = MSG_ReadByte (&net_message) / 255.0;
else
volume = DEFAULT_SOUND_PACKET_VOLUME;
if (flags & SND_ATTENUATION)
attenuation = MSG_ReadByte (&net_message) / 64.0;
else
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
if (flags & SND_OFFSET)
ofs = MSG_ReadByte (&net_message) / 1000.0;
else
ofs = 0;
if (flags & SND_ENT)
{ // entity reletive
channel = MSG_ReadShort(&net_message);
ent = channel>>3;
if (ent > MAX_EDICTS)
Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent);
channel &= 7;
}
else
{
ent = 0;
channel = 0;
}
if (flags & SND_POS)
{ // positioned in space
MSG_ReadPos (&net_message, pos_v);
pos = pos_v;
}
else // use entity number
pos = NULL;
if (!cl.sound_precache[sound_num])
return;
S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
}
void SHOWNET(char *s)
{
if (cl_shownet->value>=2)
Com_Printf ("%3i:%s\n", net_message.readcount-1, s);
}
/*
=====================
CL_ParseServerMessage
=====================
*/
void CL_ParseServerMessage (void)
{
int cmd;
char *s;
int i;
//
// if recording demos, copy the message out
//
if (cl_shownet->value == 1)
Com_Printf ("%i ",net_message.cursize);
else if (cl_shownet->value >= 2)
Com_Printf ("------------------\n");
//
// parse the message
//
while (1)
{
if (net_message.readcount > net_message.cursize)
{
Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message");
break;
}
cmd = MSG_ReadByte (&net_message);
if (cmd == -1)
{
SHOWNET("END OF MESSAGE");
break;
}
if (cl_shownet->value>=2)
{
if (!svc_strings[cmd])
Com_Printf ("%3i:BAD CMD %i\n", net_message.readcount-1,cmd);
else
SHOWNET(svc_strings[cmd]);
}
// other commands
switch (cmd)
{
default:
Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
break;
case svc_nop:
// Com_Printf ("svc_nop\n");
break;
case svc_disconnect:
Com_Error (ERR_DISCONNECT,"Server disconnected\n");
break;
case svc_reconnect:
Com_Printf ("Server disconnected, reconnecting\n");
if (cls.download) {
//ZOID, close download
fclose (cls.download);
cls.download = NULL;
}
cls.state = ca_connecting;
cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
break;
case svc_print:
i = MSG_ReadByte (&net_message);
if (i == PRINT_CHAT)
{
S_StartLocalSound ("misc/talk.wav");
con.ormask = 128;
}
Com_Printf ("%s", MSG_ReadString (&net_message));
con.ormask = 0;
break;
case svc_centerprint:
SCR_CenterPrint (MSG_ReadString (&net_message));
break;
case svc_stufftext:
s = MSG_ReadString (&net_message);
Com_DPrintf ("stufftext: %s\n", s);
Cbuf_AddText (s);
break;
case svc_serverdata:
Cbuf_Execute (); // make sure any stuffed commands are done
CL_ParseServerData ();
break;
case svc_configstring:
CL_ParseConfigString ();
break;
case svc_sound:
CL_ParseStartSoundPacket();
break;
case svc_spawnbaseline:
CL_ParseBaseline ();
break;
case svc_temp_entity:
CL_ParseTEnt ();
break;
case svc_muzzleflash:
CL_ParseMuzzleFlash ();
break;
case svc_muzzleflash2:
CL_ParseMuzzleFlash2 ();
break;
case svc_download:
CL_ParseDownload ();
break;
case svc_frame:
CL_ParseFrame ();
break;
case svc_inventory:
CL_ParseInventory ();
break;
case svc_layout:
s = MSG_ReadString (&net_message);
strncpy (cl.layout, s, sizeof(cl.layout)-1);
break;
case svc_playerinfo:
case svc_packetentities:
case svc_deltapacketentities:
Com_Error (ERR_DROP, "Out of place frame data");
break;
}
}
CL_AddNetgraph ();
//
// we don't know if it is ok to save a demo message until
// after we have parsed the frame
//
if (cls.demorecording && !cls.demowaiting)
CL_WriteDemoMessage ();
}

278
client/cl_pred.c Normal file
View File

@ -0,0 +1,278 @@
/*
Copyright (C) 1997-2001 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.
*/
#include "client.h"
/*
===================
CL_CheckPredictionError
===================
*/
void CL_CheckPredictionError (void)
{
int frame;
int delta[3];
int i;
int len;
if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
return;
// calculate the last usercmd_t we sent that the server has processed
frame = cls.netchan.incoming_acknowledged;
frame &= (CMD_BACKUP-1);
// compare what the server returned with what we had predicted it to be
VectorSubtract (cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame], delta);
// save the prediction error for interpolation
len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);
if (len > 640) // 80 world units
{ // a teleport or something
VectorClear (cl.prediction_error);
}
else
{
if (cl_showmiss->value && (delta[0] || delta[1] || delta[2]) )
Com_Printf ("prediction miss on %i: %i\n", cl.frame.serverframe,
delta[0] + delta[1] + delta[2]);
VectorCopy (cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame]);
// save for error itnerpolation
for (i=0 ; i<3 ; i++)
cl.prediction_error[i] = delta[i]*0.125;
}
}
/*
====================
CL_ClipMoveToEntities
====================
*/
void CL_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, trace_t *tr )
{
int i, x, zd, zu;
trace_t trace;
int headnode;
float *angles;
entity_state_t *ent;
int num;
cmodel_t *cmodel;
vec3_t bmins, bmaxs;
for (i=0 ; i<cl.frame.num_entities ; i++)
{
num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
ent = &cl_parse_entities[num];
if (!ent->solid)
continue;
if (ent->number == cl.playernum+1)
continue;
if (ent->solid == 31)
{ // special value for bmodel
cmodel = cl.model_clip[ent->modelindex];
if (!cmodel)
continue;
headnode = cmodel->headnode;
angles = ent->angles;
}
else
{ // encoded bbox
x = 8*(ent->solid & 31);
zd = 8*((ent->solid>>5) & 31);
zu = 8*((ent->solid>>10) & 63) - 32;
bmins[0] = bmins[1] = -x;
bmaxs[0] = bmaxs[1] = x;
bmins[2] = -zd;
bmaxs[2] = zu;
headnode = CM_HeadnodeForBox (bmins, bmaxs);
angles = vec3_origin; // boxes don't rotate
}
if (tr->allsolid)
return;
trace = CM_TransformedBoxTrace (start, end,
mins, maxs, headnode, MASK_PLAYERSOLID,
ent->origin, angles);
if (trace.allsolid || trace.startsolid ||
trace.fraction < tr->fraction)
{
trace.ent = (struct edict_s *)ent;
if (tr->startsolid)
{
*tr = trace;
tr->startsolid = true;
}
else
*tr = trace;
}
else if (trace.startsolid)
tr->startsolid = true;
}
}
/*
================
CL_PMTrace
================
*/
trace_t CL_PMTrace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
{
trace_t t;
// check against world
t = CM_BoxTrace (start, end, mins, maxs, 0, MASK_PLAYERSOLID);
if (t.fraction < 1.0)
t.ent = (struct edict_s *)1;
// check all other solid models
CL_ClipMoveToEntities (start, mins, maxs, end, &t);
return t;
}
int CL_PMpointcontents (vec3_t point)
{
int i;
entity_state_t *ent;
int num;
cmodel_t *cmodel;
int contents;
contents = CM_PointContents (point, 0);
for (i=0 ; i<cl.frame.num_entities ; i++)
{
num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
ent = &cl_parse_entities[num];
if (ent->solid != 31) // special value for bmodel
continue;
cmodel = cl.model_clip[ent->modelindex];
if (!cmodel)
continue;
contents |= CM_TransformedPointContents (point, cmodel->headnode, ent->origin, ent->angles);
}
return contents;
}
/*
=================
CL_PredictMovement
Sets cl.predicted_origin and cl.predicted_angles
=================
*/
void CL_PredictMovement (void)
{
int ack, current;
int frame;
int oldframe;
usercmd_t *cmd;
pmove_t pm;
int i;
int step;
int oldz;
if (cls.state != ca_active)
return;
if (cl_paused->value)
return;
if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
{ // just set angles
for (i=0 ; i<3 ; i++)
{
cl.predicted_angles[i] = cl.viewangles[i] + SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[i]);
}
return;
}
ack = cls.netchan.incoming_acknowledged;
current = cls.netchan.outgoing_sequence;
// if we are too far out of date, just freeze
if (current - ack >= CMD_BACKUP)
{
if (cl_showmiss->value)
Com_Printf ("exceeded CMD_BACKUP\n");
return;
}
// copy current state to pmove
memset (&pm, 0, sizeof(pm));
pm.trace = CL_PMTrace;
pm.pointcontents = CL_PMpointcontents;
pm_airaccelerate = atof(cl.configstrings[CS_AIRACCEL]);
pm.s = cl.frame.playerstate.pmove;
// SCR_DebugGraph (current - ack - 1, 0);
frame = 0;
// run frames
while (++ack < current)
{
frame = ack & (CMD_BACKUP-1);
cmd = &cl.cmds[frame];
pm.cmd = *cmd;
Pmove (&pm);
// save for debug checking
VectorCopy (pm.s.origin, cl.predicted_origins[frame]);
}
oldframe = (ack-2) & (CMD_BACKUP-1);
oldz = cl.predicted_origins[oldframe][2];
step = pm.s.origin[2] - oldz;
if (step > 63 && step < 160 && (pm.s.pm_flags & PMF_ON_GROUND) )
{
cl.predicted_step = step * 0.125;
cl.predicted_step_time = cls.realtime - cls.frametime * 500;
}
// copy results out for rendering
cl.predicted_origin[0] = pm.s.origin[0]*0.125;
cl.predicted_origin[1] = pm.s.origin[1]*0.125;
cl.predicted_origin[2] = pm.s.origin[2]*0.125;
VectorCopy (pm.viewangles, cl.predicted_angles);
}

1401
client/cl_scrn.c Normal file

File diff suppressed because it is too large Load Diff

1745
client/cl_tent.c Normal file

File diff suppressed because it is too large Load Diff

584
client/cl_view.c Normal file
View File

@ -0,0 +1,584 @@
/*
Copyright (C) 1997-2001 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.
*/
// cl_view.c -- player rendering positioning
#include "client.h"
//=============
//
// development tools for weapons
//
int gun_frame;
struct model_s *gun_model;
//=============
cvar_t *crosshair;
cvar_t *cl_testparticles;
cvar_t *cl_testentities;
cvar_t *cl_testlights;
cvar_t *cl_testblend;
cvar_t *cl_stats;
int r_numdlights;
dlight_t r_dlights[MAX_DLIGHTS];
int r_numentities;
entity_t r_entities[MAX_ENTITIES];
int r_numparticles;
particle_t r_particles[MAX_PARTICLES];
lightstyle_t r_lightstyles[MAX_LIGHTSTYLES];
char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
int num_cl_weaponmodels;
/*
====================
V_ClearScene
Specifies the model that will be used as the world
====================
*/
void V_ClearScene (void)
{
r_numdlights = 0;
r_numentities = 0;
r_numparticles = 0;
}
/*
=====================
V_AddEntity
=====================
*/
void V_AddEntity (entity_t *ent)
{
if (r_numentities >= MAX_ENTITIES)
return;
r_entities[r_numentities++] = *ent;
}
/*
=====================
V_AddParticle
=====================
*/
void V_AddParticle (vec3_t org, int color, float alpha)
{
particle_t *p;
if (r_numparticles >= MAX_PARTICLES)
return;
p = &r_particles[r_numparticles++];
VectorCopy (org, p->origin);
p->color = color;
p->alpha = alpha;
}
/*
=====================
V_AddLight
=====================
*/
void V_AddLight (vec3_t org, float intensity, float r, float g, float b)
{
dlight_t *dl;
if (r_numdlights >= MAX_DLIGHTS)
return;
dl = &r_dlights[r_numdlights++];
VectorCopy (org, dl->origin);
dl->intensity = intensity;
dl->color[0] = r;
dl->color[1] = g;
dl->color[2] = b;
}
/*
=====================
V_AddLightStyle
=====================
*/
void V_AddLightStyle (int style, float r, float g, float b)
{
lightstyle_t *ls;
if (style < 0 || style > MAX_LIGHTSTYLES)
Com_Error (ERR_DROP, "Bad light style %i", style);
ls = &r_lightstyles[style];
ls->white = r+g+b;
ls->rgb[0] = r;
ls->rgb[1] = g;
ls->rgb[2] = b;
}
/*
================
V_TestParticles
If cl_testparticles is set, create 4096 particles in the view
================
*/
void V_TestParticles (void)
{
particle_t *p;
int i, j;
float d, r, u;
r_numparticles = MAX_PARTICLES;
for (i=0 ; i<r_numparticles ; i++)
{
d = i*0.25;
r = 4*((i&7)-3.5);
u = 4*(((i>>3)&7)-3.5);
p = &r_particles[i];
for (j=0 ; j<3 ; j++)
p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*d +
cl.v_right[j]*r + cl.v_up[j]*u;
p->color = 8;
p->alpha = cl_testparticles->value;
}
}
/*
================
V_TestEntities
If cl_testentities is set, create 32 player models
================
*/
void V_TestEntities (void)
{
int i, j;
float f, r;
entity_t *ent;
r_numentities = 32;
memset (r_entities, 0, sizeof(r_entities));
for (i=0 ; i<r_numentities ; i++)
{
ent = &r_entities[i];
r = 64 * ( (i%4) - 1.5 );
f = 64 * (i/4) + 128;
for (j=0 ; j<3 ; j++)
ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
cl.v_right[j]*r;
ent->model = cl.baseclientinfo.model;
ent->skin = cl.baseclientinfo.skin;
}
}
/*
================
V_TestLights
If cl_testlights is set, create 32 lights models
================
*/
void V_TestLights (void)
{
int i, j;
float f, r;
dlight_t *dl;
r_numdlights = 32;
memset (r_dlights, 0, sizeof(r_dlights));
for (i=0 ; i<r_numdlights ; i++)
{
dl = &r_dlights[i];
r = 64 * ( (i%4) - 1.5 );
f = 64 * (i/4) + 128;
for (j=0 ; j<3 ; j++)
dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
cl.v_right[j]*r;
dl->color[0] = ((i%6)+1) & 1;
dl->color[1] = (((i%6)+1) & 2)>>1;
dl->color[2] = (((i%6)+1) & 4)>>2;
dl->intensity = 200;
}
}
//===================================================================
/*
=================
CL_PrepRefresh
Call before entering a new level, or after changing dlls
=================
*/
void CL_PrepRefresh (void)
{
char mapname[32];
int i;
char name[MAX_QPATH];
float rotate;
vec3_t axis;
if (!cl.configstrings[CS_MODELS+1][0])
return; // no map loaded
SCR_AddDirtyPoint (0, 0);
SCR_AddDirtyPoint (viddef.width-1, viddef.height-1);
// let the render dll load the map
strcpy (mapname, cl.configstrings[CS_MODELS+1] + 5); // skip "maps/"
mapname[strlen(mapname)-4] = 0; // cut off ".bsp"
// register models, pics, and skins
Com_Printf ("Map: %s\r", mapname);
SCR_UpdateScreen ();
re.BeginRegistration (mapname);
Com_Printf (" \r");
// precache status bar pics
Com_Printf ("pics\r");
SCR_UpdateScreen ();
SCR_TouchPics ();
Com_Printf (" \r");
CL_RegisterTEntModels ();
num_cl_weaponmodels = 1;
strcpy(cl_weaponmodels[0], "weapon.md2");
for (i=1 ; i<MAX_MODELS && cl.configstrings[CS_MODELS+i][0] ; i++)
{
strcpy (name, cl.configstrings[CS_MODELS+i]);
name[37] = 0; // never go beyond one line
if (name[0] != '*')
Com_Printf ("%s\r", name);
SCR_UpdateScreen ();
Sys_SendKeyEvents (); // pump message loop
if (name[0] == '#')
{
// special player weapon model
if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS)
{
strncpy(cl_weaponmodels[num_cl_weaponmodels], cl.configstrings[CS_MODELS+i]+1,
sizeof(cl_weaponmodels[num_cl_weaponmodels]) - 1);
num_cl_weaponmodels++;
}
}
else
{
cl.model_draw[i] = re.RegisterModel (cl.configstrings[CS_MODELS+i]);
if (name[0] == '*')
cl.model_clip[i] = CM_InlineModel (cl.configstrings[CS_MODELS+i]);
else
cl.model_clip[i] = NULL;
}
if (name[0] != '*')
Com_Printf (" \r");
}
Com_Printf ("images\r", i);
SCR_UpdateScreen ();
for (i=1 ; i<MAX_IMAGES && cl.configstrings[CS_IMAGES+i][0] ; i++)
{
cl.image_precache[i] = re.RegisterPic (cl.configstrings[CS_IMAGES+i]);
Sys_SendKeyEvents (); // pump message loop
}
Com_Printf (" \r");
for (i=0 ; i<MAX_CLIENTS ; i++)
{
if (!cl.configstrings[CS_PLAYERSKINS+i][0])
continue;
Com_Printf ("client %i\r", i);
SCR_UpdateScreen ();
Sys_SendKeyEvents (); // pump message loop
CL_ParseClientinfo (i);
Com_Printf (" \r");
}
CL_LoadClientinfo (&cl.baseclientinfo, "unnamed\\male/grunt");
// set sky textures and speed
Com_Printf ("sky\r", i);
SCR_UpdateScreen ();
rotate = atof (cl.configstrings[CS_SKYROTATE]);
sscanf (cl.configstrings[CS_SKYAXIS], "%f %f %f",
&axis[0], &axis[1], &axis[2]);
re.SetSky (cl.configstrings[CS_SKY], rotate, axis);
Com_Printf (" \r");
// the renderer can now free unneeded stuff
re.EndRegistration ();
// clear any lines of console text
Con_ClearNotify ();
SCR_UpdateScreen ();
cl.refresh_prepped = true;
cl.force_refdef = true; // make sure we have a valid refdef
// start the cd track
CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
}
/*
====================
CalcFov
====================
*/
float CalcFov (float fov_x, float width, float height)
{
float a;
float x;
if (fov_x < 1 || fov_x > 179)
Com_Error (ERR_DROP, "Bad fov: %f", fov_x);
x = width/tan(fov_x/360*M_PI);
a = atan (height/x);
a = a*360/M_PI;
return a;
}
//============================================================================
// gun frame debugging functions
void V_Gun_Next_f (void)
{
gun_frame++;
Com_Printf ("frame %i\n", gun_frame);
}
void V_Gun_Prev_f (void)
{
gun_frame--;
if (gun_frame < 0)
gun_frame = 0;
Com_Printf ("frame %i\n", gun_frame);
}
void V_Gun_Model_f (void)
{
char name[MAX_QPATH];
if (Cmd_Argc() != 2)
{
gun_model = NULL;
return;
}
Com_sprintf (name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1));
gun_model = re.RegisterModel (name);
}
//============================================================================
/*
=================
SCR_DrawCrosshair
=================
*/
void SCR_DrawCrosshair (void)
{
if (!crosshair->value)
return;
if (crosshair->modified)
{
crosshair->modified = false;
SCR_TouchPics ();
}
if (!crosshair_pic[0])
return;
re.DrawPic (scr_vrect.x + ((scr_vrect.width - crosshair_width)>>1)
, scr_vrect.y + ((scr_vrect.height - crosshair_height)>>1), crosshair_pic);
}
/*
==================
V_RenderView
==================
*/
void V_RenderView( float stereo_separation )
{
extern int entitycmpfnc( const entity_t *, const entity_t * );
if (cls.state != ca_active)
return;
if (!cl.refresh_prepped)
return; // still loading
if (cl_timedemo->value)
{
if (!cl.timedemo_start)
cl.timedemo_start = Sys_Milliseconds ();
cl.timedemo_frames++;
}
// an invalid frame will just use the exact previous refdef
// we can't use the old frame if the video mode has changed, though...
if ( cl.frame.valid && (cl.force_refdef || !cl_paused->value) )
{
cl.force_refdef = false;
V_ClearScene ();
// build a refresh entity list and calc cl.sim*
// this also calls CL_CalcViewValues which loads
// v_forward, etc.
CL_AddEntities ();
if (cl_testparticles->value)
V_TestParticles ();
if (cl_testentities->value)
V_TestEntities ();
if (cl_testlights->value)
V_TestLights ();
if (cl_testblend->value)
{
cl.refdef.blend[0] = 1;
cl.refdef.blend[1] = 0.5;
cl.refdef.blend[2] = 0.25;
cl.refdef.blend[3] = 0.5;
}
// offset vieworg appropriately if we're doing stereo separation
if ( stereo_separation != 0 )
{
vec3_t tmp;
VectorScale( cl.v_right, stereo_separation, tmp );
VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg );
}
// never let it sit exactly on a node line, because a water plane can
// dissapear when viewed with the eye exactly on it.
// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
cl.refdef.vieworg[0] += 1.0/16;
cl.refdef.vieworg[1] += 1.0/16;
cl.refdef.vieworg[2] += 1.0/16;
cl.refdef.x = scr_vrect.x;
cl.refdef.y = scr_vrect.y;
cl.refdef.width = scr_vrect.width;
cl.refdef.height = scr_vrect.height;
cl.refdef.fov_y = CalcFov (cl.refdef.fov_x, cl.refdef.width, cl.refdef.height);
cl.refdef.time = cl.time*0.001;
cl.refdef.areabits = cl.frame.areabits;
if (!cl_add_entities->value)
r_numentities = 0;
if (!cl_add_particles->value)
r_numparticles = 0;
if (!cl_add_lights->value)
r_numdlights = 0;
if (!cl_add_blend->value)
{
VectorClear (cl.refdef.blend);
}
cl.refdef.num_entities = r_numentities;
cl.refdef.entities = r_entities;
cl.refdef.num_particles = r_numparticles;
cl.refdef.particles = r_particles;
cl.refdef.num_dlights = r_numdlights;
cl.refdef.dlights = r_dlights;
cl.refdef.lightstyles = r_lightstyles;
cl.refdef.rdflags = cl.frame.playerstate.rdflags;
// sort entities for better cache locality
qsort( cl.refdef.entities, cl.refdef.num_entities, sizeof( cl.refdef.entities[0] ), (int (*)(const void *, const void *))entitycmpfnc );
}
re.RenderFrame (&cl.refdef);
if (cl_stats->value)
Com_Printf ("ent:%i lt:%i part:%i\n", r_numentities, r_numdlights, r_numparticles);
if ( log_stats->value && ( log_stats_file != 0 ) )
fprintf( log_stats_file, "%i,%i,%i,",r_numentities, r_numdlights, r_numparticles);
SCR_AddDirtyPoint (scr_vrect.x, scr_vrect.y);
SCR_AddDirtyPoint (scr_vrect.x+scr_vrect.width-1,
scr_vrect.y+scr_vrect.height-1);
SCR_DrawCrosshair ();
}
/*
=============
V_Viewpos_f
=============
*/
void V_Viewpos_f (void)
{
Com_Printf ("(%i %i %i) : %i\n", (int)cl.refdef.vieworg[0],
(int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2],
(int)cl.refdef.viewangles[YAW]);
}
/*
=============
V_Init
=============
*/
void V_Init (void)
{
Cmd_AddCommand ("gun_next", V_Gun_Next_f);
Cmd_AddCommand ("gun_prev", V_Gun_Prev_f);
Cmd_AddCommand ("gun_model", V_Gun_Model_f);
Cmd_AddCommand ("viewpos", V_Viewpos_f);
crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE);
cl_testblend = Cvar_Get ("cl_testblend", "0", 0);
cl_testparticles = Cvar_Get ("cl_testparticles", "0", 0);
cl_testentities = Cvar_Get ("cl_testentities", "0", 0);
cl_testlights = Cvar_Get ("cl_testlights", "0", 0);
cl_stats = Cvar_Get ("cl_stats", "0", 0);
}

584
client/client.h Normal file
View File

@ -0,0 +1,584 @@
/*
Copyright (C) 1997-2001 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.
*/
// client.h -- primary header for client
//define PARANOID // speed sapping error checking
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "ref.h"
#include "vid.h"
#include "screen.h"
#include "sound.h"
#include "input.h"
#include "keys.h"
#include "console.h"
#include "cdaudio.h"
//=============================================================================
typedef struct
{
qboolean valid; // cleared if delta parsing was invalid
int serverframe;
int servertime; // server time the message is valid for (in msec)
int deltaframe;
byte areabits[MAX_MAP_AREAS/8]; // portalarea visibility bits
player_state_t playerstate;
int num_entities;
int parse_entities; // non-masked index into cl_parse_entities array
} frame_t;
typedef struct
{
entity_state_t baseline; // delta from this if not from a previous frame
entity_state_t current;
entity_state_t prev; // will always be valid, but might just be a copy of current
int serverframe; // if not current, this ent isn't in the frame
int trailcount; // for diminishing grenade trails
vec3_t lerp_origin; // for trails (variable hz)
int fly_stoptime;
} centity_t;
#define MAX_CLIENTWEAPONMODELS 20 // PGM -- upped from 16 to fit the chainfist vwep
typedef struct
{
char name[MAX_QPATH];
char cinfo[MAX_QPATH];
struct image_s *skin;
struct image_s *icon;
char iconname[MAX_QPATH];
struct model_s *model;
struct model_s *weaponmodel[MAX_CLIENTWEAPONMODELS];
} clientinfo_t;
extern char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
extern int num_cl_weaponmodels;
#define CMD_BACKUP 64 // allow a lot of command backups for very fast systems
//
// the client_state_t structure is wiped completely at every
// server map change
//
typedef struct
{
int timeoutcount;
int timedemo_frames;
int timedemo_start;
qboolean refresh_prepped; // false if on new level or new ref dll
qboolean sound_prepped; // ambient sounds can start
qboolean force_refdef; // vid has changed, so we can't use a paused refdef
int parse_entities; // index (not anded off) into cl_parse_entities[]
usercmd_t cmd;
usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds
int cmd_time[CMD_BACKUP]; // time sent, for calculating pings
short predicted_origins[CMD_BACKUP][3]; // for debug comparing against server
float predicted_step; // for stair up smoothing
unsigned predicted_step_time;
vec3_t predicted_origin; // generated by CL_PredictMovement
vec3_t predicted_angles;
vec3_t prediction_error;
frame_t frame; // received from server
int surpressCount; // number of messages rate supressed
frame_t frames[UPDATE_BACKUP];
// the client maintains its own idea of view angles, which are
// sent to the server each frame. It is cleared to 0 upon entering each level.
// the server sends a delta each frame which is added to the locally
// tracked view angles to account for standing on rotating objects,
// and teleport direction changes
vec3_t viewangles;
int time; // this is the time value that the client
// is rendering at. always <= cls.realtime
float lerpfrac; // between oldframe and frame
refdef_t refdef;
vec3_t v_forward, v_right, v_up; // set when refdef.angles is set
//
// transient data from server
//
char layout[1024]; // general 2D overlay
int inventory[MAX_ITEMS];
//
// non-gameserver infornamtion
// FIXME: move this cinematic stuff into the cin_t structure
FILE *cinematic_file;
int cinematictime; // cls.realtime for first cinematic frame
int cinematicframe;
char cinematicpalette[768];
qboolean cinematicpalette_active;
//
// server state information
//
qboolean attractloop; // running the attract loop, any key will menu
int servercount; // server identification for prespawns
char gamedir[MAX_QPATH];
int playernum;
char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH];
//
// locally derived information from server state
//
struct model_s *model_draw[MAX_MODELS];
struct cmodel_s *model_clip[MAX_MODELS];
struct sfx_s *sound_precache[MAX_SOUNDS];
struct image_s *image_precache[MAX_IMAGES];
clientinfo_t clientinfo[MAX_CLIENTS];
clientinfo_t baseclientinfo;
} client_state_t;
extern client_state_t cl;
/*
==================================================================
the client_static_t structure is persistant through an arbitrary number
of server connections
==================================================================
*/
typedef enum {
ca_uninitialized,
ca_disconnected, // not talking to a server
ca_connecting, // sending request packets to the server
ca_connected, // netchan_t established, waiting for svc_serverdata
ca_active // game views should be displayed
} connstate_t;
typedef enum {
dl_none,
dl_model,
dl_sound,
dl_skin,
dl_single
} dltype_t; // download type
typedef enum {key_game, key_console, key_message, key_menu} keydest_t;
typedef struct
{
connstate_t state;
keydest_t key_dest;
int framecount;
int realtime; // always increasing, no clamping, etc
float frametime; // seconds since last frame
// screen rendering information
float disable_screen; // showing loading plaque between levels
// or changing rendering dlls
// if time gets > 30 seconds ahead, break it
int disable_servercount; // when we receive a frame and cl.servercount
// > cls.disable_servercount, clear disable_screen
// connection information
char servername[MAX_OSPATH]; // name of server from original connect
float connect_time; // for connection retransmits
int quakePort; // a 16 bit value that allows quake servers
// to work around address translating routers
netchan_t netchan;
int serverProtocol; // in case we are doing some kind of version hack
int challenge; // from the server to use for connecting
FILE *download; // file transfer from server
char downloadtempname[MAX_OSPATH];
char downloadname[MAX_OSPATH];
int downloadnumber;
dltype_t downloadtype;
int downloadpercent;
// demo recording info must be here, so it isn't cleared on level change
qboolean demorecording;
qboolean demowaiting; // don't record until a non-delta message is received
FILE *demofile;
} client_static_t;
extern client_static_t cls;
//=============================================================================
//
// cvars
//
extern cvar_t *cl_stereo_separation;
extern cvar_t *cl_stereo;
extern cvar_t *cl_gun;
extern cvar_t *cl_add_blend;
extern cvar_t *cl_add_lights;
extern cvar_t *cl_add_particles;
extern cvar_t *cl_add_entities;
extern cvar_t *cl_predict;
extern cvar_t *cl_footsteps;
extern cvar_t *cl_noskins;
extern cvar_t *cl_autoskins;
extern cvar_t *cl_upspeed;
extern cvar_t *cl_forwardspeed;
extern cvar_t *cl_sidespeed;
extern cvar_t *cl_yawspeed;
extern cvar_t *cl_pitchspeed;
extern cvar_t *cl_run;
extern cvar_t *cl_anglespeedkey;
extern cvar_t *cl_shownet;
extern cvar_t *cl_showmiss;
extern cvar_t *cl_showclamp;
extern cvar_t *lookspring;
extern cvar_t *lookstrafe;
extern cvar_t *sensitivity;
extern cvar_t *m_pitch;
extern cvar_t *m_yaw;
extern cvar_t *m_forward;
extern cvar_t *m_side;
extern cvar_t *freelook;
extern cvar_t *cl_lightlevel; // FIXME HACK
extern cvar_t *cl_paused;
extern cvar_t *cl_timedemo;
extern cvar_t *cl_vwep;
typedef struct
{
int key; // so entities can reuse same entry
vec3_t color;
vec3_t origin;
float radius;
float die; // stop lighting after this time
float decay; // drop this each second
float minlight; // don't add when contributing less
} cdlight_t;
extern centity_t cl_entities[MAX_EDICTS];
extern cdlight_t cl_dlights[MAX_DLIGHTS];
// the cl_parse_entities must be large enough to hold UPDATE_BACKUP frames of
// entities, so that when a delta compressed message arives from the server
// it can be un-deltad from the original
#define MAX_PARSE_ENTITIES 1024
extern entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
//=============================================================================
extern netadr_t net_from;
extern sizebuf_t net_message;
void DrawString (int x, int y, char *s);
void DrawAltString (int x, int y, char *s); // toggle high bit
qboolean CL_CheckOrDownloadFile (char *filename);
void CL_AddNetgraph (void);
//ROGUE
typedef struct cl_sustain
{
int id;
int type;
int endtime;
int nextthink;
int thinkinterval;
vec3_t org;
vec3_t dir;
int color;
int count;
int magnitude;
void (*think)(struct cl_sustain *self);
} cl_sustain_t;
#define MAX_SUSTAINS 32
void CL_ParticleSteamEffect2(cl_sustain_t *self);
void CL_TeleporterParticles (entity_state_t *ent);
void CL_ParticleEffect (vec3_t org, vec3_t dir, int color, int count);
void CL_ParticleEffect2 (vec3_t org, vec3_t dir, int color, int count);
// RAFAEL
void CL_ParticleEffect3 (vec3_t org, vec3_t dir, int color, int count);
//=================================================
// ========
// PGM
typedef struct particle_s
{
struct particle_s *next;
float time;
vec3_t org;
vec3_t vel;
vec3_t accel;
float color;
float colorvel;
float alpha;
float alphavel;
} cparticle_t;
#define PARTICLE_GRAVITY 40
#define BLASTER_PARTICLE_COLOR 0xe0
// PMM
#define INSTANT_PARTICLE -10000.0
// PGM
// ========
void CL_ClearEffects (void);
void CL_ClearTEnts (void);
void CL_BlasterTrail (vec3_t start, vec3_t end);
void CL_QuadTrail (vec3_t start, vec3_t end);
void CL_RailTrail (vec3_t start, vec3_t end);
void CL_BubbleTrail (vec3_t start, vec3_t end);
void CL_FlagTrail (vec3_t start, vec3_t end, float color);
// RAFAEL
void CL_IonripperTrail (vec3_t start, vec3_t end);
// ========
// PGM
void CL_BlasterParticles2 (vec3_t org, vec3_t dir, unsigned int color);
void CL_BlasterTrail2 (vec3_t start, vec3_t end);
void CL_DebugTrail (vec3_t start, vec3_t end);
void CL_SmokeTrail (vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing);
void CL_Flashlight (int ent, vec3_t pos);
void CL_ForceWall (vec3_t start, vec3_t end, int color);
void CL_FlameEffects (centity_t *ent, vec3_t origin);
void CL_GenericParticleEffect (vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread, float alphavel);
void CL_BubbleTrail2 (vec3_t start, vec3_t end, int dist);
void CL_Heatbeam (vec3_t start, vec3_t end);
void CL_ParticleSteamEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude);
void CL_TrackerTrail (vec3_t start, vec3_t end, int particleColor);
void CL_Tracker_Explode(vec3_t origin);
void CL_TagTrail (vec3_t start, vec3_t end, float color);
void CL_ColorFlash (vec3_t pos, int ent, int intensity, float r, float g, float b);
void CL_Tracker_Shell(vec3_t origin);
void CL_MonsterPlasma_Shell(vec3_t origin);
void CL_ColorExplosionParticles (vec3_t org, int color, int run);
void CL_ParticleSmokeEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude);
void CL_Widowbeamout (cl_sustain_t *self);
void CL_Nukeblast (cl_sustain_t *self);
void CL_WidowSplash (vec3_t org);
// PGM
// ========
int CL_ParseEntityBits (unsigned *bits);
void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int bits);
void CL_ParseFrame (void);
void CL_ParseTEnt (void);
void CL_ParseConfigString (void);
void CL_ParseMuzzleFlash (void);
void CL_ParseMuzzleFlash2 (void);
void SmokeAndFlash(vec3_t origin);
void CL_SetLightstyle (int i);
void CL_RunParticles (void);
void CL_RunDLights (void);
void CL_RunLightStyles (void);
void CL_AddEntities (void);
void CL_AddDLights (void);
void CL_AddTEnts (void);
void CL_AddLightStyles (void);
//=================================================
void CL_PrepRefresh (void);
void CL_RegisterSounds (void);
void CL_Quit_f (void);
void IN_Accumulate (void);
void CL_ParseLayout (void);
//
// cl_main
//
extern refexport_t re; // interface to refresh .dll
void CL_Init (void);
void CL_FixUpGender(void);
void CL_Disconnect (void);
void CL_Disconnect_f (void);
void CL_GetChallengePacket (void);
void CL_PingServers_f (void);
void CL_Snd_Restart_f (void);
void CL_RequestNextDownload (void);
//
// cl_input
//
typedef struct
{
int down[2]; // key nums holding it down
unsigned downtime; // msec timestamp
unsigned msec; // msec down this frame
int state;
} kbutton_t;
extern kbutton_t in_mlook, in_klook;
extern kbutton_t in_strafe;
extern kbutton_t in_speed;
void CL_InitInput (void);
void CL_SendCmd (void);
void CL_SendMove (usercmd_t *cmd);
void CL_ClearState (void);
void CL_ReadPackets (void);
int CL_ReadFromServer (void);
void CL_WriteToServer (usercmd_t *cmd);
void CL_BaseMove (usercmd_t *cmd);
void IN_CenterView (void);
float CL_KeyState (kbutton_t *key);
char *Key_KeynumToString (int keynum);
//
// cl_demo.c
//
void CL_WriteDemoMessage (void);
void CL_Stop_f (void);
void CL_Record_f (void);
//
// cl_parse.c
//
extern char *svc_strings[256];
void CL_ParseServerMessage (void);
void CL_LoadClientinfo (clientinfo_t *ci, char *s);
void SHOWNET(char *s);
void CL_ParseClientinfo (int player);
void CL_Download_f (void);
//
// cl_view.c
//
extern int gun_frame;
extern struct model_s *gun_model;
void V_Init (void);
void V_RenderView( float stereo_separation );
void V_AddEntity (entity_t *ent);
void V_AddParticle (vec3_t org, int color, float alpha);
void V_AddLight (vec3_t org, float intensity, float r, float g, float b);
void V_AddLightStyle (int style, float r, float g, float b);
//
// cl_tent.c
//
void CL_RegisterTEntSounds (void);
void CL_RegisterTEntModels (void);
void CL_SmokeAndFlash(vec3_t origin);
//
// cl_pred.c
//
void CL_InitPrediction (void);
void CL_PredictMove (void);
void CL_CheckPredictionError (void);
//
// cl_fx.c
//
cdlight_t *CL_AllocDlight (int key);
void CL_BigTeleportParticles (vec3_t org);
void CL_RocketTrail (vec3_t start, vec3_t end, centity_t *old);
void CL_DiminishingTrail (vec3_t start, vec3_t end, centity_t *old, int flags);
void CL_FlyEffect (centity_t *ent, vec3_t origin);
void CL_BfgParticles (entity_t *ent);
void CL_AddParticles (void);
void CL_EntityEvent (entity_state_t *ent);
// RAFAEL
void CL_TrapParticles (entity_t *ent);
//
// menus
//
void M_Init (void);
void M_Keydown (int key);
void M_Draw (void);
void M_Menu_Main_f (void);
void M_ForceMenuOff (void);
void M_AddToServerList (netadr_t adr, char *info);
//
// cl_inv.c
//
void CL_ParseInventory (void);
void CL_KeyInventory (int key);
void CL_DrawInventory (void);
//
// cl_pred.c
//
void CL_PredictMovement (void);
#if id386
void x86_TimerStart( void );
void x86_TimerStop( void );
void x86_TimerInit( unsigned long smallest, unsigned longest );
unsigned long *x86_TimerGetHistogram( void );
#endif

682
client/console.c Normal file
View File

@ -0,0 +1,682 @@
/*
Copyright (C) 1997-2001 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.
*/
// console.c
#include "client.h"
console_t con;
cvar_t *con_notifytime;
#define MAXCMDLINE 256
extern char key_lines[32][MAXCMDLINE];
extern int edit_line;
extern int key_linepos;
void DrawString (int x, int y, char *s)
{
while (*s)
{
re.DrawChar (x, y, *s);
x+=8;
s++;
}
}
void DrawAltString (int x, int y, char *s)
{
while (*s)
{
re.DrawChar (x, y, *s ^ 0x80);
x+=8;
s++;
}
}
void Key_ClearTyping (void)
{
key_lines[edit_line][1] = 0; // clear any typing
key_linepos = 1;
}
/*
================
Con_ToggleConsole_f
================
*/
void Con_ToggleConsole_f (void)
{
SCR_EndLoadingPlaque (); // get rid of loading plaque
if (cl.attractloop)
{
Cbuf_AddText ("killserver\n");
return;
}
if (cls.state == ca_disconnected)
{ // start the demo loop again
Cbuf_AddText ("d1\n");
return;
}
Key_ClearTyping ();
Con_ClearNotify ();
if (cls.key_dest == key_console)
{
M_ForceMenuOff ();
Cvar_Set ("paused", "0");
}
else
{
M_ForceMenuOff ();
cls.key_dest = key_console;
if (Cvar_VariableValue ("maxclients") == 1
&& Com_ServerState ())
Cvar_Set ("paused", "1");
}
}
/*
================
Con_ToggleChat_f
================
*/
void Con_ToggleChat_f (void)
{
Key_ClearTyping ();
if (cls.key_dest == key_console)
{
if (cls.state == ca_active)
{
M_ForceMenuOff ();
cls.key_dest = key_game;
}
}
else
cls.key_dest = key_console;
Con_ClearNotify ();
}
/*
================
Con_Clear_f
================
*/
void Con_Clear_f (void)
{
memset (con.text, ' ', CON_TEXTSIZE);
}
/*
================
Con_Dump_f
Save the console contents out to a file
================
*/
void Con_Dump_f (void)
{
int l, x;
char *line;
FILE *f;
char buffer[1024];
char name[MAX_OSPATH];
if (Cmd_Argc() != 2)
{
Com_Printf ("usage: condump <filename>\n");
return;
}
Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1));
Com_Printf ("Dumped console text to %s.\n", name);
FS_CreatePath (name);
f = fopen (name, "w");
if (!f)
{
Com_Printf ("ERROR: couldn't open.\n");
return;
}
// skip empty lines
for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
{
line = con.text + (l%con.totallines)*con.linewidth;
for (x=0 ; x<con.linewidth ; x++)
if (line[x] != ' ')
break;
if (x != con.linewidth)
break;
}
// write the remaining lines
buffer[con.linewidth] = 0;
for ( ; l <= con.current ; l++)
{
line = con.text + (l%con.totallines)*con.linewidth;
strncpy (buffer, line, con.linewidth);
for (x=con.linewidth-1 ; x>=0 ; x--)
{
if (buffer[x] == ' ')
buffer[x] = 0;
else
break;
}
for (x=0; buffer[x]; x++)
buffer[x] &= 0x7f;
fprintf (f, "%s\n", buffer);
}
fclose (f);
}
/*
================
Con_ClearNotify
================
*/
void Con_ClearNotify (void)
{
int i;
for (i=0 ; i<NUM_CON_TIMES ; i++)
con.times[i] = 0;
}
/*
================
Con_MessageMode_f
================
*/
void Con_MessageMode_f (void)
{
chat_team = false;
cls.key_dest = key_message;
}
/*
================
Con_MessageMode2_f
================
*/
void Con_MessageMode2_f (void)
{
chat_team = true;
cls.key_dest = key_message;
}
/*
================
Con_CheckResize
If the line width has changed, reformat the buffer.
================
*/
void Con_CheckResize (void)
{
int i, j, width, oldwidth, oldtotallines, numlines, numchars;
char tbuf[CON_TEXTSIZE];
width = (viddef.width >> 3) - 2;
if (width == con.linewidth)
return;
if (width < 1) // video hasn't been initialized yet
{
width = 38;
con.linewidth = width;
con.totallines = CON_TEXTSIZE / con.linewidth;
memset (con.text, ' ', CON_TEXTSIZE);
}
else
{
oldwidth = con.linewidth;
con.linewidth = width;
oldtotallines = con.totallines;
con.totallines = CON_TEXTSIZE / con.linewidth;
numlines = oldtotallines;
if (con.totallines < numlines)
numlines = con.totallines;
numchars = oldwidth;
if (con.linewidth < numchars)
numchars = con.linewidth;
memcpy (tbuf, con.text, CON_TEXTSIZE);
memset (con.text, ' ', CON_TEXTSIZE);
for (i=0 ; i<numlines ; i++)
{
for (j=0 ; j<numchars ; j++)
{
con.text[(con.totallines - 1 - i) * con.linewidth + j] =
tbuf[((con.current - i + oldtotallines) %
oldtotallines) * oldwidth + j];
}
}
Con_ClearNotify ();
}
con.current = con.totallines - 1;
con.display = con.current;
}
/*
================
Con_Init
================
*/
void Con_Init (void)
{
con.linewidth = -1;
Con_CheckResize ();
Com_Printf ("Console initialized.\n");
//
// register our commands
//
con_notifytime = Cvar_Get ("con_notifytime", "3", 0);
Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
Cmd_AddCommand ("togglechat", Con_ToggleChat_f);
Cmd_AddCommand ("messagemode", Con_MessageMode_f);
Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
Cmd_AddCommand ("clear", Con_Clear_f);
Cmd_AddCommand ("condump", Con_Dump_f);
con.initialized = true;
}
/*
===============
Con_Linefeed
===============
*/
void Con_Linefeed (void)
{
con.x = 0;
if (con.display == con.current)
con.display++;
con.current++;
memset (&con.text[(con.current%con.totallines)*con.linewidth]
, ' ', con.linewidth);
}
/*
================
Con_Print
Handles cursor positioning, line wrapping, etc
All console printing must go through this in order to be logged to disk
If no console is visible, the text will appear at the top of the game window
================
*/
void Con_Print (char *txt)
{
int y;
int c, l;
static int cr;
int mask;
if (!con.initialized)
return;
if (txt[0] == 1 || txt[0] == 2)
{
mask = 128; // go to colored text
txt++;
}
else
mask = 0;
while ( (c = *txt) )
{
// count word length
for (l=0 ; l< con.linewidth ; l++)
if ( txt[l] <= ' ')
break;
// word wrap
if (l != con.linewidth && (con.x + l > con.linewidth) )
con.x = 0;
txt++;
if (cr)
{
con.current--;
cr = false;
}
if (!con.x)
{
Con_Linefeed ();
// mark time for transparent overlay
if (con.current >= 0)
con.times[con.current % NUM_CON_TIMES] = cls.realtime;
}
switch (c)
{
case '\n':
con.x = 0;
break;
case '\r':
con.x = 0;
cr = 1;
break;
default: // display character and advance
y = con.current % con.totallines;
con.text[y*con.linewidth+con.x] = c | mask | con.ormask;
con.x++;
if (con.x >= con.linewidth)
con.x = 0;
break;
}
}
}
/*
==============
Con_CenteredPrint
==============
*/
void Con_CenteredPrint (char *text)
{
int l;
char buffer[1024];
l = strlen(text);
l = (con.linewidth-l)/2;
if (l < 0)
l = 0;
memset (buffer, ' ', l);
strcpy (buffer+l, text);
strcat (buffer, "\n");
Con_Print (buffer);
}
/*
==============================================================================
DRAWING
==============================================================================
*/
/*
================
Con_DrawInput
The input line scrolls horizontally if typing goes beyond the right edge
================
*/
void Con_DrawInput (void)
{
int y;
int i;
char *text;
if (cls.key_dest == key_menu)
return;
if (cls.key_dest != key_console && cls.state == ca_active)
return; // don't draw anything (always draw if not active)
text = key_lines[edit_line];
// add the cursor frame
text[key_linepos] = 10+((int)(cls.realtime>>8)&1);
// fill out remainder with spaces
for (i=key_linepos+1 ; i< con.linewidth ; i++)
text[i] = ' ';
// prestep if horizontally scrolling
if (key_linepos >= con.linewidth)
text += 1 + key_linepos - con.linewidth;
// draw it
y = con.vislines-16;
for (i=0 ; i<con.linewidth ; i++)
re.DrawChar ( (i+1)<<3, con.vislines - 22, text[i]);
// remove cursor
key_lines[edit_line][key_linepos] = 0;
}
/*
================
Con_DrawNotify
Draws the last few lines of output transparently over the game top
================
*/
void Con_DrawNotify (void)
{
int x, v;
char *text;
int i;
int time;
char *s;
int skip;
v = 0;
for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
{
if (i < 0)
continue;
time = con.times[i % NUM_CON_TIMES];
if (time == 0)
continue;
time = cls.realtime - time;
if (time > con_notifytime->value*1000)
continue;
text = con.text + (i % con.totallines)*con.linewidth;
for (x = 0 ; x < con.linewidth ; x++)
re.DrawChar ( (x+1)<<3, v, text[x]);
v += 8;
}
if (cls.key_dest == key_message)
{
if (chat_team)
{
DrawString (8, v, "say_team:");
skip = 11;
}
else
{
DrawString (8, v, "say:");
skip = 5;
}
s = chat_buffer;
if (chat_bufferlen > (viddef.width>>3)-(skip+1))
s += chat_bufferlen - ((viddef.width>>3)-(skip+1));
x = 0;
while(s[x])
{
re.DrawChar ( (x+skip)<<3, v, s[x]);
x++;
}
re.DrawChar ( (x+skip)<<3, v, 10+((cls.realtime>>8)&1));
v += 8;
}
if (v)
{
SCR_AddDirtyPoint (0,0);
SCR_AddDirtyPoint (viddef.width-1, v);
}
}
/*
================
Con_DrawConsole
Draws the console with the solid background
================
*/
void Con_DrawConsole (float frac)
{
int i, j, x, y, n;
int rows;
char *text;
int row;
int lines;
char version[64];
char dlbar[1024];
lines = viddef.height * frac;
if (lines <= 0)
return;
if (lines > viddef.height)
lines = viddef.height;
// draw the background
re.DrawStretchPic (0, -viddef.height+lines, viddef.width, viddef.height, "conback");
SCR_AddDirtyPoint (0,0);
SCR_AddDirtyPoint (viddef.width-1,lines-1);
Com_sprintf (version, sizeof(version), "v%4.2f", VERSION);
for (x=0 ; x<5 ; x++)
re.DrawChar (viddef.width-44+x*8, lines-12, 128 + version[x] );
// draw the text
con.vislines = lines;
#if 0
rows = (lines-8)>>3; // rows of text to draw
y = lines - 24;
#else
rows = (lines-22)>>3; // rows of text to draw
y = lines - 30;
#endif
// draw from the bottom up
if (con.display != con.current)
{
// draw arrows to show the buffer is backscrolled
for (x=0 ; x<con.linewidth ; x+=4)
re.DrawChar ( (x+1)<<3, y, '^');
y -= 8;
rows--;
}
row = con.display;
for (i=0 ; i<rows ; i++, y-=8, row--)
{
if (row < 0)
break;
if (con.current - row >= con.totallines)
break; // past scrollback wrap point
text = con.text + (row % con.totallines)*con.linewidth;
for (x=0 ; x<con.linewidth ; x++)
re.DrawChar ( (x+1)<<3, y, text[x]);
}
//ZOID
// draw the download bar
// figure out width
if (cls.download) {
if ((text = strrchr(cls.downloadname, '/')) != NULL)
text++;
else
text = cls.downloadname;
x = con.linewidth - ((con.linewidth * 7) / 40);
y = x - strlen(text) - 8;
i = con.linewidth/3;
if (strlen(text) > i) {
y = x - i - 11;
strncpy(dlbar, text, i);
dlbar[i] = 0;
strcat(dlbar, "...");
} else
strcpy(dlbar, text);
strcat(dlbar, ": ");
i = strlen(dlbar);
dlbar[i++] = '\x80';
// where's the dot go?
if (cls.downloadpercent == 0)
n = 0;
else
n = y * cls.downloadpercent / 100;
for (j = 0; j < y; j++)
if (j == n)
dlbar[i++] = '\x83';
else
dlbar[i++] = '\x81';
dlbar[i++] = '\x82';
dlbar[i] = 0;
sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
// draw it
y = con.vislines-12;
for (i = 0; i < strlen(dlbar); i++)
re.DrawChar ( (i+1)<<3, y, dlbar[i]);
}
//ZOID
// draw the input prompt, user text, and cursor if desired
Con_DrawInput ();
}

62
client/console.h Normal file
View File

@ -0,0 +1,62 @@
/*
Copyright (C) 1997-2001 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.
*/
//
// console
//
#define NUM_CON_TIMES 4
#define CON_TEXTSIZE 32768
typedef struct
{
qboolean initialized;
char text[CON_TEXTSIZE];
int current; // line where next message will be printed
int x; // offset in current line for next print
int display; // bottom of console displays this line
int ormask; // high bit mask for colored characters
int linewidth; // characters across screen
int totallines; // total lines in console scrollback
float cursorspeed;
int vislines;
float times[NUM_CON_TIMES]; // cls.realtime time the line was generated
// for transparent notify lines
} console_t;
extern console_t con;
void Con_DrawCharacter (int cx, int line, int num);
void Con_CheckResize (void);
void Con_Init (void);
void Con_DrawConsole (float frac);
void Con_Print (char *txt);
void Con_CenteredPrint (char *text);
void Con_Clear_f (void);
void Con_DrawNotify (void);
void Con_ClearNotify (void);
void Con_ToggleConsole_f (void);

34
client/input.h Normal file
View File

@ -0,0 +1,34 @@
/*
Copyright (C) 1997-2001 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.
*/
// input.h -- external (non-keyboard) input devices
void IN_Init (void);
void IN_Shutdown (void);
void IN_Commands (void);
// oportunity for devices to stick commands on the script buffer
void IN_Frame (void);
void IN_Move (usercmd_t *cmd);
// add additional movement on top of the keyboard move cmd
void IN_Activate (qboolean active);

943
client/keys.c Normal file
View File

@ -0,0 +1,943 @@
/*
Copyright (C) 1997-2001 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.
*/
#include "client.h"
/*
key up events are sent even if in console mode
*/
#define MAXCMDLINE 256
char key_lines[32][MAXCMDLINE];
int key_linepos;
int shift_down=false;
int anykeydown;
int edit_line=0;
int history_line=0;
int key_waiting;
char *keybindings[256];
qboolean consolekeys[256]; // if true, can't be rebound while in console
qboolean menubound[256]; // if true, can't be rebound while in menu
int keyshift[256]; // key to map to if shift held down in console
int key_repeats[256]; // if > 1, it is autorepeating
qboolean keydown[256];
typedef struct
{
char *name;
int keynum;
} keyname_t;
keyname_t keynames[] =
{
{"TAB", K_TAB},
{"ENTER", K_ENTER},
{"ESCAPE", K_ESCAPE},
{"SPACE", K_SPACE},
{"BACKSPACE", K_BACKSPACE},
{"UPARROW", K_UPARROW},
{"DOWNARROW", K_DOWNARROW},
{"LEFTARROW", K_LEFTARROW},
{"RIGHTARROW", K_RIGHTARROW},
{"ALT", K_ALT},
{"CTRL", K_CTRL},
{"SHIFT", K_SHIFT},
{"F1", K_F1},
{"F2", K_F2},
{"F3", K_F3},
{"F4", K_F4},
{"F5", K_F5},
{"F6", K_F6},
{"F7", K_F7},
{"F8", K_F8},
{"F9", K_F9},
{"F10", K_F10},
{"F11", K_F11},
{"F12", K_F12},
{"INS", K_INS},
{"DEL", K_DEL},
{"PGDN", K_PGDN},
{"PGUP", K_PGUP},
{"HOME", K_HOME},
{"END", K_END},
{"MOUSE1", K_MOUSE1},
{"MOUSE2", K_MOUSE2},
{"MOUSE3", K_MOUSE3},
{"JOY1", K_JOY1},
{"JOY2", K_JOY2},
{"JOY3", K_JOY3},
{"JOY4", K_JOY4},
{"AUX1", K_AUX1},
{"AUX2", K_AUX2},
{"AUX3", K_AUX3},
{"AUX4", K_AUX4},
{"AUX5", K_AUX5},
{"AUX6", K_AUX6},
{"AUX7", K_AUX7},
{"AUX8", K_AUX8},
{"AUX9", K_AUX9},
{"AUX10", K_AUX10},
{"AUX11", K_AUX11},
{"AUX12", K_AUX12},
{"AUX13", K_AUX13},
{"AUX14", K_AUX14},
{"AUX15", K_AUX15},
{"AUX16", K_AUX16},
{"AUX17", K_AUX17},
{"AUX18", K_AUX18},
{"AUX19", K_AUX19},
{"AUX20", K_AUX20},
{"AUX21", K_AUX21},
{"AUX22", K_AUX22},
{"AUX23", K_AUX23},
{"AUX24", K_AUX24},
{"AUX25", K_AUX25},
{"AUX26", K_AUX26},
{"AUX27", K_AUX27},
{"AUX28", K_AUX28},
{"AUX29", K_AUX29},
{"AUX30", K_AUX30},
{"AUX31", K_AUX31},
{"AUX32", K_AUX32},
{"KP_HOME", K_KP_HOME },
{"KP_UPARROW", K_KP_UPARROW },
{"KP_PGUP", K_KP_PGUP },
{"KP_LEFTARROW", K_KP_LEFTARROW },
{"KP_5", K_KP_5 },
{"KP_RIGHTARROW", K_KP_RIGHTARROW },
{"KP_END", K_KP_END },
{"KP_DOWNARROW", K_KP_DOWNARROW },
{"KP_PGDN", K_KP_PGDN },
{"KP_ENTER", K_KP_ENTER },
{"KP_INS", K_KP_INS },
{"KP_DEL", K_KP_DEL },
{"KP_SLASH", K_KP_SLASH },
{"KP_MINUS", K_KP_MINUS },
{"KP_PLUS", K_KP_PLUS },
{"MWHEELUP", K_MWHEELUP },
{"MWHEELDOWN", K_MWHEELDOWN },
{"PAUSE", K_PAUSE},
{"SEMICOLON", ';'}, // because a raw semicolon seperates commands
{NULL,0}
};
/*
==============================================================================
LINE TYPING INTO THE CONSOLE
==============================================================================
*/
void CompleteCommand (void)
{
char *cmd, *s;
s = key_lines[edit_line]+1;
if (*s == '\\' || *s == '/')
s++;
cmd = Cmd_CompleteCommand (s);
if (!cmd)
cmd = Cvar_CompleteVariable (s);
if (cmd)
{
key_lines[edit_line][1] = '/';
strcpy (key_lines[edit_line]+2, cmd);
key_linepos = strlen(cmd)+2;
key_lines[edit_line][key_linepos] = ' ';
key_linepos++;
key_lines[edit_line][key_linepos] = 0;
return;
}
}
/*
====================
Key_Console
Interactive line editing and console scrollback
====================
*/
void Key_Console (int key)
{
switch ( key )
{
case K_KP_SLASH:
key = '/';
break;
case K_KP_MINUS:
key = '-';
break;
case K_KP_PLUS:
key = '+';
break;
case K_KP_HOME:
key = '7';
break;
case K_KP_UPARROW:
key = '8';
break;
case K_KP_PGUP:
key = '9';
break;
case K_KP_LEFTARROW:
key = '4';
break;
case K_KP_5:
key = '5';
break;
case K_KP_RIGHTARROW:
key = '6';
break;
case K_KP_END:
key = '1';
break;
case K_KP_DOWNARROW:
key = '2';
break;
case K_KP_PGDN:
key = '3';
break;
case K_KP_INS:
key = '0';
break;
case K_KP_DEL:
key = '.';
break;
}
if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) ||
( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) )
{
char *cbd;
if ( ( cbd = Sys_GetClipboardData() ) != 0 )
{
int i;
strtok( cbd, "\n\r\b" );
i = strlen( cbd );
if ( i + key_linepos >= MAXCMDLINE)
i= MAXCMDLINE - key_linepos;
if ( i > 0 )
{
cbd[i]=0;
strcat( key_lines[edit_line], cbd );
key_linepos += i;
}
free( cbd );
}
return;
}
if ( key == 'l' )
{
if ( keydown[K_CTRL] )
{
Cbuf_AddText ("clear\n");
return;
}
}
if ( key == K_ENTER || key == K_KP_ENTER )
{ // backslash text are commands, else chat
if (key_lines[edit_line][1] == '\\' || key_lines[edit_line][1] == '/')
Cbuf_AddText (key_lines[edit_line]+2); // skip the >
else
Cbuf_AddText (key_lines[edit_line]+1); // valid command
Cbuf_AddText ("\n");
Com_Printf ("%s\n",key_lines[edit_line]);
edit_line = (edit_line + 1) & 31;
history_line = edit_line;
key_lines[edit_line][0] = ']';
key_linepos = 1;
if (cls.state == ca_disconnected)
SCR_UpdateScreen (); // force an update, because the command
// may take some time
return;
}
if (key == K_TAB)
{ // command completion
CompleteCommand ();
return;
}
if ( ( key == K_BACKSPACE ) || ( key == K_LEFTARROW ) || ( key == K_KP_LEFTARROW ) || ( ( key == 'h' ) && ( keydown[K_CTRL] ) ) )
{
if (key_linepos > 1)
key_linepos--;
return;
}
if ( ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ||
( ( key == 'p' ) && keydown[K_CTRL] ) )
{
do
{
history_line = (history_line - 1) & 31;
} while (history_line != edit_line
&& !key_lines[history_line][1]);
if (history_line == edit_line)
history_line = (edit_line+1)&31;
strcpy(key_lines[edit_line], key_lines[history_line]);
key_linepos = strlen(key_lines[edit_line]);
return;
}
if ( ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) ||
( ( key == 'n' ) && keydown[K_CTRL] ) )
{
if (history_line == edit_line) return;
do
{
history_line = (history_line + 1) & 31;
}
while (history_line != edit_line
&& !key_lines[history_line][1]);
if (history_line == edit_line)
{
key_lines[edit_line][0] = ']';
key_linepos = 1;
}
else
{
strcpy(key_lines[edit_line], key_lines[history_line]);
key_linepos = strlen(key_lines[edit_line]);
}
return;
}
if (key == K_PGUP || key == K_KP_PGUP )
{
con.display -= 2;
return;
}
if (key == K_PGDN || key == K_KP_PGDN )
{
con.display += 2;
if (con.display > con.current)
con.display = con.current;
return;
}
if (key == K_HOME || key == K_KP_HOME )
{
con.display = con.current - con.totallines + 10;
return;
}
if (key == K_END || key == K_KP_END )
{
con.display = con.current;
return;
}
if (key < 32 || key > 127)
return; // non printable
if (key_linepos < MAXCMDLINE-1)
{
key_lines[edit_line][key_linepos] = key;
key_linepos++;
key_lines[edit_line][key_linepos] = 0;
}
}
//============================================================================
qboolean chat_team;
char chat_buffer[MAXCMDLINE];
int chat_bufferlen = 0;
void Key_Message (int key)
{
if ( key == K_ENTER || key == K_KP_ENTER )
{
if (chat_team)
Cbuf_AddText ("say_team \"");
else
Cbuf_AddText ("say \"");
Cbuf_AddText(chat_buffer);
Cbuf_AddText("\"\n");
cls.key_dest = key_game;
chat_bufferlen = 0;
chat_buffer[0] = 0;
return;
}
if (key == K_ESCAPE)
{
cls.key_dest = key_game;
chat_bufferlen = 0;
chat_buffer[0] = 0;
return;
}
if (key < 32 || key > 127)
return; // non printable
if (key == K_BACKSPACE)
{
if (chat_bufferlen)
{
chat_bufferlen--;
chat_buffer[chat_bufferlen] = 0;
}
return;
}
if (chat_bufferlen == sizeof(chat_buffer)-1)
return; // all full
chat_buffer[chat_bufferlen++] = key;
chat_buffer[chat_bufferlen] = 0;
}
//============================================================================
/*
===================
Key_StringToKeynum
Returns a key number to be used to index keybindings[] by looking at
the given string. Single ascii characters return themselves, while
the K_* names are matched up.
===================
*/
int Key_StringToKeynum (char *str)
{
keyname_t *kn;
if (!str || !str[0])
return -1;
if (!str[1])
return str[0];
for (kn=keynames ; kn->name ; kn++)
{
if (!Q_strcasecmp(str,kn->name))
return kn->keynum;
}
return -1;
}
/*
===================
Key_KeynumToString
Returns a string (either a single ascii char, or a K_* name) for the
given keynum.
FIXME: handle quote special (general escape sequence?)
===================
*/
char *Key_KeynumToString (int keynum)
{
keyname_t *kn;
static char tinystr[2];
if (keynum == -1)
return "<KEY NOT FOUND>";
if (keynum > 32 && keynum < 127)
{ // printable ascii
tinystr[0] = keynum;
tinystr[1] = 0;
return tinystr;
}
for (kn=keynames ; kn->name ; kn++)
if (keynum == kn->keynum)
return kn->name;
return "<UNKNOWN KEYNUM>";
}
/*
===================
Key_SetBinding
===================
*/
void Key_SetBinding (int keynum, char *binding)
{
char *new;
int l;
if (keynum == -1)
return;
// free old bindings
if (keybindings[keynum])
{
Z_Free (keybindings[keynum]);
keybindings[keynum] = NULL;
}
// allocate memory for new binding
l = strlen (binding);
new = Z_Malloc (l+1);
strcpy (new, binding);
new[l] = 0;
keybindings[keynum] = new;
}
/*
===================
Key_Unbind_f
===================
*/
void Key_Unbind_f (void)
{
int b;
if (Cmd_Argc() != 2)
{
Com_Printf ("unbind <key> : remove commands from a key\n");
return;
}
b = Key_StringToKeynum (Cmd_Argv(1));
if (b==-1)
{
Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
return;
}
Key_SetBinding (b, "");
}
void Key_Unbindall_f (void)
{
int i;
for (i=0 ; i<256 ; i++)
if (keybindings[i])
Key_SetBinding (i, "");
}
/*
===================
Key_Bind_f
===================
*/
void Key_Bind_f (void)
{
int i, c, b;
char cmd[1024];
c = Cmd_Argc();
if (c < 2)
{
Com_Printf ("bind <key> [command] : attach a command to a key\n");
return;
}
b = Key_StringToKeynum (Cmd_Argv(1));
if (b==-1)
{
Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
return;
}
if (c == 2)
{
if (keybindings[b])
Com_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
else
Com_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
return;
}
// copy the rest of the command line
cmd[0] = 0; // start out with a null string
for (i=2 ; i< c ; i++)
{
strcat (cmd, Cmd_Argv(i));
if (i != (c-1))
strcat (cmd, " ");
}
Key_SetBinding (b, cmd);
}
/*
============
Key_WriteBindings
Writes lines containing "bind key value"
============
*/
void Key_WriteBindings (FILE *f)
{
int i;
for (i=0 ; i<256 ; i++)
if (keybindings[i] && keybindings[i][0])
fprintf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
}
/*
============
Key_Bindlist_f
============
*/
void Key_Bindlist_f (void)
{
int i;
for (i=0 ; i<256 ; i++)
if (keybindings[i] && keybindings[i][0])
Com_Printf ("%s \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
}
/*
===================
Key_Init
===================
*/
void Key_Init (void)
{
int i;
for (i=0 ; i<32 ; i++)
{
key_lines[i][0] = ']';
key_lines[i][1] = 0;
}
key_linepos = 1;
//
// init ascii characters in console mode
//
for (i=32 ; i<128 ; i++)
consolekeys[i] = true;
consolekeys[K_ENTER] = true;
consolekeys[K_KP_ENTER] = true;
consolekeys[K_TAB] = true;
consolekeys[K_LEFTARROW] = true;
consolekeys[K_KP_LEFTARROW] = true;
consolekeys[K_RIGHTARROW] = true;
consolekeys[K_KP_RIGHTARROW] = true;
consolekeys[K_UPARROW] = true;
consolekeys[K_KP_UPARROW] = true;
consolekeys[K_DOWNARROW] = true;
consolekeys[K_KP_DOWNARROW] = true;
consolekeys[K_BACKSPACE] = true;
consolekeys[K_HOME] = true;
consolekeys[K_KP_HOME] = true;
consolekeys[K_END] = true;
consolekeys[K_KP_END] = true;
consolekeys[K_PGUP] = true;
consolekeys[K_KP_PGUP] = true;
consolekeys[K_PGDN] = true;
consolekeys[K_KP_PGDN] = true;
consolekeys[K_SHIFT] = true;
consolekeys[K_INS] = true;
consolekeys[K_KP_INS] = true;
consolekeys[K_KP_DEL] = true;
consolekeys[K_KP_SLASH] = true;
consolekeys[K_KP_PLUS] = true;
consolekeys[K_KP_MINUS] = true;
consolekeys[K_KP_5] = true;
consolekeys['`'] = false;
consolekeys['~'] = false;
for (i=0 ; i<256 ; i++)
keyshift[i] = i;
for (i='a' ; i<='z' ; i++)
keyshift[i] = i - 'a' + 'A';
keyshift['1'] = '!';
keyshift['2'] = '@';
keyshift['3'] = '#';
keyshift['4'] = '$';
keyshift['5'] = '%';
keyshift['6'] = '^';
keyshift['7'] = '&';
keyshift['8'] = '*';
keyshift['9'] = '(';
keyshift['0'] = ')';
keyshift['-'] = '_';
keyshift['='] = '+';
keyshift[','] = '<';
keyshift['.'] = '>';
keyshift['/'] = '?';
keyshift[';'] = ':';
keyshift['\''] = '"';
keyshift['['] = '{';
keyshift[']'] = '}';
keyshift['`'] = '~';
keyshift['\\'] = '|';
menubound[K_ESCAPE] = true;
for (i=0 ; i<12 ; i++)
menubound[K_F1+i] = true;
//
// register our functions
//
Cmd_AddCommand ("bind",Key_Bind_f);
Cmd_AddCommand ("unbind",Key_Unbind_f);
Cmd_AddCommand ("unbindall",Key_Unbindall_f);
Cmd_AddCommand ("bindlist",Key_Bindlist_f);
}
/*
===================
Key_Event
Called by the system between frames for both key up and key down events
Should NOT be called during an interrupt!
===================
*/
void Key_Event (int key, qboolean down, unsigned time)
{
char *kb;
char cmd[1024];
// hack for modal presses
if (key_waiting == -1)
{
if (down)
key_waiting = key;
return;
}
// update auto-repeat status
if (down)
{
key_repeats[key]++;
if (key != K_BACKSPACE
&& key != K_PAUSE
&& key != K_PGUP
&& key != K_KP_PGUP
&& key != K_PGDN
&& key != K_KP_PGDN
&& key_repeats[key] > 1)
return; // ignore most autorepeats
if (key >= 200 && !keybindings[key])
Com_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) );
}
else
{
key_repeats[key] = 0;
}
if (key == K_SHIFT)
shift_down = down;
// console key is hardcoded, so the user can never unbind it
if (key == '`' || key == '~')
{
if (!down)
return;
Con_ToggleConsole_f ();
return;
}
// any key during the attract mode will bring up the menu
if (cl.attractloop && cls.key_dest != key_menu)
key = K_ESCAPE;
// menu key is hardcoded, so the user can never unbind it
if (key == K_ESCAPE)
{
if (!down)
return;
if (cl.frame.playerstate.stats[STAT_LAYOUTS] && cls.key_dest == key_game)
{ // put away help computer / inventory
Cbuf_AddText ("cmd putaway\n");
return;
}
switch (cls.key_dest)
{
case key_message:
Key_Message (key);
break;
case key_menu:
M_Keydown (key);
break;
case key_game:
case key_console:
M_Menu_Main_f ();
break;
default:
Com_Error (ERR_FATAL, "Bad cls.key_dest");
}
return;
}
// track if any key is down for BUTTON_ANY
keydown[key] = down;
if (down)
{
if (key_repeats[key] == 1)
anykeydown++;
}
else
{
anykeydown--;
if (anykeydown < 0)
anykeydown = 0;
}
//
// key up events only generate commands if the game key binding is
// a button command (leading + sign). These will occur even in console mode,
// to keep the character from continuing an action started before a console
// switch. Button commands include the kenum as a parameter, so multiple
// downs can be matched with ups
//
if (!down)
{
kb = keybindings[key];
if (kb && kb[0] == '+')
{
Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", kb+1, key, time);
Cbuf_AddText (cmd);
}
if (keyshift[key] != key)
{
kb = keybindings[keyshift[key]];
if (kb && kb[0] == '+')
{
Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", kb+1, key, time);
Cbuf_AddText (cmd);
}
}
return;
}
//
// if not a consolekey, send to the interpreter no matter what mode is
//
if ( (cls.key_dest == key_menu && menubound[key])
|| (cls.key_dest == key_console && !consolekeys[key])
|| (cls.key_dest == key_game && ( cls.state == ca_active || !consolekeys[key] ) ) )
{
kb = keybindings[key];
if (kb)
{
if (kb[0] == '+')
{ // button commands add keynum and time as a parm
Com_sprintf (cmd, sizeof(cmd), "%s %i %i\n", kb, key, time);
Cbuf_AddText (cmd);
}
else
{
Cbuf_AddText (kb);
Cbuf_AddText ("\n");
}
}
return;
}
if (!down)
return; // other systems only care about key down events
if (shift_down)
key = keyshift[key];
switch (cls.key_dest)
{
case key_message:
Key_Message (key);
break;
case key_menu:
M_Keydown (key);
break;
case key_game:
case key_console:
Key_Console (key);
break;
default:
Com_Error (ERR_FATAL, "Bad cls.key_dest");
}
}
/*
===================
Key_ClearStates
===================
*/
void Key_ClearStates (void)
{
int i;
anykeydown = false;
for (i=0 ; i<256 ; i++)
{
if ( keydown[i] || key_repeats[i] )
Key_Event( i, false, 0 );
keydown[i] = 0;
key_repeats[i] = 0;
}
}
/*
===================
Key_GetKey
===================
*/
int Key_GetKey (void)
{
key_waiting = -1;
while (key_waiting == -1)
Sys_SendKeyEvents ();
return key_waiting;
}

146
client/keys.h Normal file
View File

@ -0,0 +1,146 @@
/*
Copyright (C) 1997-2001 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.
*/
//
// these are the key numbers that should be passed to Key_Event
//
#define K_TAB 9
#define K_ENTER 13
#define K_ESCAPE 27
#define K_SPACE 32
// normal keys should be passed as lowercased ascii
#define K_BACKSPACE 127
#define K_UPARROW 128
#define K_DOWNARROW 129
#define K_LEFTARROW 130
#define K_RIGHTARROW 131
#define K_ALT 132
#define K_CTRL 133
#define K_SHIFT 134
#define K_F1 135
#define K_F2 136
#define K_F3 137
#define K_F4 138
#define K_F5 139
#define K_F6 140
#define K_F7 141
#define K_F8 142
#define K_F9 143
#define K_F10 144
#define K_F11 145
#define K_F12 146
#define K_INS 147
#define K_DEL 148
#define K_PGDN 149
#define K_PGUP 150
#define K_HOME 151
#define K_END 152
#define K_KP_HOME 160
#define K_KP_UPARROW 161
#define K_KP_PGUP 162
#define K_KP_LEFTARROW 163
#define K_KP_5 164
#define K_KP_RIGHTARROW 165
#define K_KP_END 166
#define K_KP_DOWNARROW 167
#define K_KP_PGDN 168
#define K_KP_ENTER 169
#define K_KP_INS 170
#define K_KP_DEL 171
#define K_KP_SLASH 172
#define K_KP_MINUS 173
#define K_KP_PLUS 174
#define K_PAUSE 255
//
// mouse buttons generate virtual keys
//
#define K_MOUSE1 200
#define K_MOUSE2 201
#define K_MOUSE3 202
//
// joystick buttons
//
#define K_JOY1 203
#define K_JOY2 204
#define K_JOY3 205
#define K_JOY4 206
//
// aux keys are for multi-buttoned joysticks to generate so they can use
// the normal binding process
//
#define K_AUX1 207
#define K_AUX2 208
#define K_AUX3 209
#define K_AUX4 210
#define K_AUX5 211
#define K_AUX6 212
#define K_AUX7 213
#define K_AUX8 214
#define K_AUX9 215
#define K_AUX10 216
#define K_AUX11 217
#define K_AUX12 218
#define K_AUX13 219
#define K_AUX14 220
#define K_AUX15 221
#define K_AUX16 222
#define K_AUX17 223
#define K_AUX18 224
#define K_AUX19 225
#define K_AUX20 226
#define K_AUX21 227
#define K_AUX22 228
#define K_AUX23 229
#define K_AUX24 230
#define K_AUX25 231
#define K_AUX26 232
#define K_AUX27 233
#define K_AUX28 234
#define K_AUX29 235
#define K_AUX30 236
#define K_AUX31 237
#define K_AUX32 238
#define K_MWHEELDOWN 239
#define K_MWHEELUP 240
extern char *keybindings[256];
extern int key_repeats[256];
extern int anykeydown;
extern char chat_buffer[];
extern int chat_bufferlen;
extern qboolean chat_team;
void Key_Event (int key, qboolean down, unsigned time);
void Key_Init (void);
void Key_WriteBindings (FILE *f);
void Key_SetBinding (int keynum, char *binding);
void Key_ClearStates (void);
int Key_GetKey (void);

4016
client/menu.c Normal file

File diff suppressed because it is too large Load Diff

674
client/qmenu.c Normal file
View File

@ -0,0 +1,674 @@
/*
Copyright (C) 1997-2001 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.
*/
#include <string.h>
#include <ctype.h>
#include "client.h"
#include "qmenu.h"
static void Action_DoEnter( menuaction_s *a );
static void Action_Draw( menuaction_s *a );
static void Menu_DrawStatusBar( const char *string );
static void Menulist_DoEnter( menulist_s *l );
static void MenuList_Draw( menulist_s *l );
static void Separator_Draw( menuseparator_s *s );
static void Slider_DoSlide( menuslider_s *s, int dir );
static void Slider_Draw( menuslider_s *s );
static void SpinControl_DoEnter( menulist_s *s );
static void SpinControl_Draw( menulist_s *s );
static void SpinControl_DoSlide( menulist_s *s, int dir );
#define RCOLUMN_OFFSET 16
#define LCOLUMN_OFFSET -16
extern refexport_t re;
extern viddef_t viddef;
#define VID_WIDTH viddef.width
#define VID_HEIGHT viddef.height
#define Draw_Char re.DrawChar
#define Draw_Fill re.DrawFill
void Action_DoEnter( menuaction_s *a )
{
if ( a->generic.callback )
a->generic.callback( a );
}
void Action_Draw( menuaction_s *a )
{
if ( a->generic.flags & QMF_LEFT_JUSTIFY )
{
if ( a->generic.flags & QMF_GRAYED )
Menu_DrawStringDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
else
Menu_DrawString( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
}
else
{
if ( a->generic.flags & QMF_GRAYED )
Menu_DrawStringR2LDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
else
Menu_DrawStringR2L( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
}
if ( a->generic.ownerdraw )
a->generic.ownerdraw( a );
}
qboolean Field_DoEnter( menufield_s *f )
{
if ( f->generic.callback )
{
f->generic.callback( f );
return true;
}
return false;
}
void Field_Draw( menufield_s *f )
{
int i;
char tempbuffer[128]="";
if ( f->generic.name )
Menu_DrawStringR2LDark( f->generic.x + f->generic.parent->x + LCOLUMN_OFFSET, f->generic.y + f->generic.parent->y, f->generic.name );
strncpy( tempbuffer, f->buffer + f->visible_offset, f->visible_length );
Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y - 4, 18 );
Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y + 4, 24 );
Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y - 4, 20 );
Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y + 4, 26 );
for ( i = 0; i < f->visible_length; i++ )
{
Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y - 4, 19 );
Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y + 4, 25 );
}
Menu_DrawString( f->generic.x + f->generic.parent->x + 24, f->generic.y + f->generic.parent->y, tempbuffer );
if ( Menu_ItemAtCursor( f->generic.parent ) == f )
{
int offset;
if ( f->visible_offset )
offset = f->visible_length;
else
offset = f->cursor;
if ( ( ( int ) ( Sys_Milliseconds() / 250 ) ) & 1 )
{
Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8,
f->generic.y + f->generic.parent->y,
11 );
}
else
{
Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8,
f->generic.y + f->generic.parent->y,
' ' );
}
}
}
qboolean Field_Key( menufield_s *f, int key )
{
extern int keydown[];
switch ( key )
{
case K_KP_SLASH:
key = '/';
break;
case K_KP_MINUS:
key = '-';
break;
case K_KP_PLUS:
key = '+';
break;
case K_KP_HOME:
key = '7';
break;
case K_KP_UPARROW:
key = '8';
break;
case K_KP_PGUP:
key = '9';
break;
case K_KP_LEFTARROW:
key = '4';
break;
case K_KP_5:
key = '5';
break;
case K_KP_RIGHTARROW:
key = '6';
break;
case K_KP_END:
key = '1';
break;
case K_KP_DOWNARROW:
key = '2';
break;
case K_KP_PGDN:
key = '3';
break;
case K_KP_INS:
key = '0';
break;
case K_KP_DEL:
key = '.';
break;
}
if ( key > 127 )
{
switch ( key )
{
case K_DEL:
default:
return false;
}
}
/*
** support pasting from the clipboard
*/
if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) ||
( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) )
{
char *cbd;
if ( ( cbd = Sys_GetClipboardData() ) != 0 )
{
strtok( cbd, "\n\r\b" );
strncpy( f->buffer, cbd, f->length - 1 );
f->cursor = strlen( f->buffer );
f->visible_offset = f->cursor - f->visible_length;
if ( f->visible_offset < 0 )
f->visible_offset = 0;
free( cbd );
}
return true;
}
switch ( key )
{
case K_KP_LEFTARROW:
case K_LEFTARROW:
case K_BACKSPACE:
if ( f->cursor > 0 )
{
memmove( &f->buffer[f->cursor-1], &f->buffer[f->cursor], strlen( &f->buffer[f->cursor] ) + 1 );
f->cursor--;
if ( f->visible_offset )
{
f->visible_offset--;
}
}
break;
case K_KP_DEL:
case K_DEL:
memmove( &f->buffer[f->cursor], &f->buffer[f->cursor+1], strlen( &f->buffer[f->cursor+1] ) + 1 );
break;
case K_KP_ENTER:
case K_ENTER:
case K_ESCAPE:
case K_TAB:
return false;
case K_SPACE:
default:
if ( !isdigit( key ) && ( f->generic.flags & QMF_NUMBERSONLY ) )
return false;
if ( f->cursor < f->length )
{
f->buffer[f->cursor++] = key;
f->buffer[f->cursor] = 0;
if ( f->cursor > f->visible_length )
{
f->visible_offset++;
}
}
}
return true;
}
void Menu_AddItem( menuframework_s *menu, void *item )
{
if ( menu->nitems == 0 )
menu->nslots = 0;
if ( menu->nitems < MAXMENUITEMS )
{
menu->items[menu->nitems] = item;
( ( menucommon_s * ) menu->items[menu->nitems] )->parent = menu;
menu->nitems++;
}
menu->nslots = Menu_TallySlots( menu );
}
/*
** Menu_AdjustCursor
**
** This function takes the given menu, the direction, and attempts
** to adjust the menu's cursor so that it's at the next available
** slot.
*/
void Menu_AdjustCursor( menuframework_s *m, int dir )
{
menucommon_s *citem;
/*
** see if it's in a valid spot
*/
if ( m->cursor >= 0 && m->cursor < m->nitems )
{
if ( ( citem = Menu_ItemAtCursor( m ) ) != 0 )
{
if ( citem->type != MTYPE_SEPARATOR )
return;
}
}
/*
** it's not in a valid spot, so crawl in the direction indicated until we
** find a valid spot
*/
if ( dir == 1 )
{
while ( 1 )
{
citem = Menu_ItemAtCursor( m );
if ( citem )
if ( citem->type != MTYPE_SEPARATOR )
break;
m->cursor += dir;
if ( m->cursor >= m->nitems )
m->cursor = 0;
}
}
else
{
while ( 1 )
{
citem = Menu_ItemAtCursor( m );
if ( citem )
if ( citem->type != MTYPE_SEPARATOR )
break;
m->cursor += dir;
if ( m->cursor < 0 )
m->cursor = m->nitems - 1;
}
}
}
void Menu_Center( menuframework_s *menu )
{
int height;
height = ( ( menucommon_s * ) menu->items[menu->nitems-1])->y;
height += 10;
menu->y = ( VID_HEIGHT - height ) / 2;
}
void Menu_Draw( menuframework_s *menu )
{
int i;
menucommon_s *item;
/*
** draw contents
*/
for ( i = 0; i < menu->nitems; i++ )
{
switch ( ( ( menucommon_s * ) menu->items[i] )->type )
{
case MTYPE_FIELD:
Field_Draw( ( menufield_s * ) menu->items[i] );
break;
case MTYPE_SLIDER:
Slider_Draw( ( menuslider_s * ) menu->items[i] );
break;
case MTYPE_LIST:
MenuList_Draw( ( menulist_s * ) menu->items[i] );
break;
case MTYPE_SPINCONTROL:
SpinControl_Draw( ( menulist_s * ) menu->items[i] );
break;
case MTYPE_ACTION:
Action_Draw( ( menuaction_s * ) menu->items[i] );
break;
case MTYPE_SEPARATOR:
Separator_Draw( ( menuseparator_s * ) menu->items[i] );
break;
}
}
item = Menu_ItemAtCursor( menu );
if ( item && item->cursordraw )
{
item->cursordraw( item );
}
else if ( menu->cursordraw )
{
menu->cursordraw( menu );
}
else if ( item && item->type != MTYPE_FIELD )
{
if ( item->flags & QMF_LEFT_JUSTIFY )
{
Draw_Char( menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) );
}
else
{
Draw_Char( menu->x + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) );
}
}
if ( item )
{
if ( item->statusbarfunc )
item->statusbarfunc( ( void * ) item );
else if ( item->statusbar )
Menu_DrawStatusBar( item->statusbar );
else
Menu_DrawStatusBar( menu->statusbar );
}
else
{
Menu_DrawStatusBar( menu->statusbar );
}
}
void Menu_DrawStatusBar( const char *string )
{
if ( string )
{
int l = strlen( string );
int maxrow = VID_HEIGHT / 8;
int maxcol = VID_WIDTH / 8;
int col = maxcol / 2 - l / 2;
Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 4 );
Menu_DrawString( col*8, VID_HEIGHT - 8, string );
}
else
{
Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 0 );
}
}
void Menu_DrawString( int x, int y, const char *string )
{
unsigned i;
for ( i = 0; i < strlen( string ); i++ )
{
Draw_Char( ( x + i*8 ), y, string[i] );
}
}
void Menu_DrawStringDark( int x, int y, const char *string )
{
unsigned i;
for ( i = 0; i < strlen( string ); i++ )
{
Draw_Char( ( x + i*8 ), y, string[i] + 128 );
}
}
void Menu_DrawStringR2L( int x, int y, const char *string )
{
unsigned i;
for ( i = 0; i < strlen( string ); i++ )
{
Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1] );
}
}
void Menu_DrawStringR2LDark( int x, int y, const char *string )
{
unsigned i;
for ( i = 0; i < strlen( string ); i++ )
{
Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1]+128 );
}
}
void *Menu_ItemAtCursor( menuframework_s *m )
{
if ( m->cursor < 0 || m->cursor >= m->nitems )
return 0;
return m->items[m->cursor];
}
qboolean Menu_SelectItem( menuframework_s *s )
{
menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s );
if ( item )
{
switch ( item->type )
{
case MTYPE_FIELD:
return Field_DoEnter( ( menufield_s * ) item ) ;
case MTYPE_ACTION:
Action_DoEnter( ( menuaction_s * ) item );
return true;
case MTYPE_LIST:
// Menulist_DoEnter( ( menulist_s * ) item );
return false;
case MTYPE_SPINCONTROL:
// SpinControl_DoEnter( ( menulist_s * ) item );
return false;
}
}
return false;
}
void Menu_SetStatusBar( menuframework_s *m, const char *string )
{
m->statusbar = string;
}
void Menu_SlideItem( menuframework_s *s, int dir )
{
menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s );
if ( item )
{
switch ( item->type )
{
case MTYPE_SLIDER:
Slider_DoSlide( ( menuslider_s * ) item, dir );
break;
case MTYPE_SPINCONTROL:
SpinControl_DoSlide( ( menulist_s * ) item, dir );
break;
}
}
}
int Menu_TallySlots( menuframework_s *menu )
{
int i;
int total = 0;
for ( i = 0; i < menu->nitems; i++ )
{
if ( ( ( menucommon_s * ) menu->items[i] )->type == MTYPE_LIST )
{
int nitems = 0;
const char **n = ( ( menulist_s * ) menu->items[i] )->itemnames;
while (*n)
nitems++, n++;
total += nitems;
}
else
{
total++;
}
}
return total;
}
void Menulist_DoEnter( menulist_s *l )
{
int start;
start = l->generic.y / 10 + 1;
l->curvalue = l->generic.parent->cursor - start;
if ( l->generic.callback )
l->generic.callback( l );
}
void MenuList_Draw( menulist_s *l )
{
const char **n;
int y = 0;
Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y, l->generic.name );
n = l->itemnames;
Draw_Fill( l->generic.x - 112 + l->generic.parent->x, l->generic.parent->y + l->generic.y + l->curvalue*10 + 10, 128, 10, 16 );
while ( *n )
{
Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y + y + 10, *n );
n++;
y += 10;
}
}
void Separator_Draw( menuseparator_s *s )
{
if ( s->generic.name )
Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->generic.name );
}
void Slider_DoSlide( menuslider_s *s, int dir )
{
s->curvalue += dir;
if ( s->curvalue > s->maxvalue )
s->curvalue = s->maxvalue;
else if ( s->curvalue < s->minvalue )
s->curvalue = s->minvalue;
if ( s->generic.callback )
s->generic.callback( s );
}
#define SLIDER_RANGE 10
void Slider_Draw( menuslider_s *s )
{
int i;
Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
s->generic.y + s->generic.parent->y,
s->generic.name );
s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
if ( s->range < 0)
s->range = 0;
if ( s->range > 1)
s->range = 1;
Draw_Char( s->generic.x + s->generic.parent->x + RCOLUMN_OFFSET, s->generic.y + s->generic.parent->y, 128);
for ( i = 0; i < SLIDER_RANGE; i++ )
Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 129);
Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 130);
Draw_Char( ( int ) ( 8 + RCOLUMN_OFFSET + s->generic.parent->x + s->generic.x + (SLIDER_RANGE-1)*8 * s->range ), s->generic.y + s->generic.parent->y, 131);
}
void SpinControl_DoEnter( menulist_s *s )
{
s->curvalue++;
if ( s->itemnames[s->curvalue] == 0 )
s->curvalue = 0;
if ( s->generic.callback )
s->generic.callback( s );
}
void SpinControl_DoSlide( menulist_s *s, int dir )
{
s->curvalue += dir;
if ( s->curvalue < 0 )
s->curvalue = 0;
else if ( s->itemnames[s->curvalue] == 0 )
s->curvalue--;
if ( s->generic.callback )
s->generic.callback( s );
}
void SpinControl_Draw( menulist_s *s )
{
char buffer[100];
if ( s->generic.name )
{
Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
s->generic.y + s->generic.parent->y,
s->generic.name );
}
if ( !strchr( s->itemnames[s->curvalue], '\n' ) )
{
Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->itemnames[s->curvalue] );
}
else
{
strcpy( buffer, s->itemnames[s->curvalue] );
*strchr( buffer, '\n' ) = 0;
Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, buffer );
strcpy( buffer, strchr( s->itemnames[s->curvalue], '\n' ) + 1 );
Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y + 10, buffer );
}
}

140
client/qmenu.h Normal file
View File

@ -0,0 +1,140 @@
/*
Copyright (C) 1997-2001 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.
*/
#ifndef __QMENU_H__
#define __QMENU_H__
#define MAXMENUITEMS 64
#define MTYPE_SLIDER 0
#define MTYPE_LIST 1
#define MTYPE_ACTION 2
#define MTYPE_SPINCONTROL 3
#define MTYPE_SEPARATOR 4
#define MTYPE_FIELD 5
#define K_TAB 9
#define K_ENTER 13
#define K_ESCAPE 27
#define K_SPACE 32
// normal keys should be passed as lowercased ascii
#define K_BACKSPACE 127
#define K_UPARROW 128
#define K_DOWNARROW 129
#define K_LEFTARROW 130
#define K_RIGHTARROW 131
#define QMF_LEFT_JUSTIFY 0x00000001
#define QMF_GRAYED 0x00000002
#define QMF_NUMBERSONLY 0x00000004
typedef struct _tag_menuframework
{
int x, y;
int cursor;
int nitems;
int nslots;
void *items[64];
const char *statusbar;
void (*cursordraw)( struct _tag_menuframework *m );
} menuframework_s;
typedef struct
{
int type;
const char *name;
int x, y;
menuframework_s *parent;
int cursor_offset;
int localdata[4];
unsigned flags;
const char *statusbar;
void (*callback)( void *self );
void (*statusbarfunc)( void *self );
void (*ownerdraw)( void *self );
void (*cursordraw)( void *self );
} menucommon_s;
typedef struct
{
menucommon_s generic;
char buffer[80];
int cursor;
int length;
int visible_length;
int visible_offset;
} menufield_s;
typedef struct
{
menucommon_s generic;
float minvalue;
float maxvalue;
float curvalue;
float range;
} menuslider_s;
typedef struct
{
menucommon_s generic;
int curvalue;
const char **itemnames;
} menulist_s;
typedef struct
{
menucommon_s generic;
} menuaction_s;
typedef struct
{
menucommon_s generic;
} menuseparator_s;
qboolean Field_Key( menufield_s *field, int key );
void Menu_AddItem( menuframework_s *menu, void *item );
void Menu_AdjustCursor( menuframework_s *menu, int dir );
void Menu_Center( menuframework_s *menu );
void Menu_Draw( menuframework_s *menu );
void *Menu_ItemAtCursor( menuframework_s *m );
qboolean Menu_SelectItem( menuframework_s *s );
void Menu_SetStatusBar( menuframework_s *s, const char *string );
void Menu_SlideItem( menuframework_s *s, int dir );
int Menu_TallySlots( menuframework_s *menu );
void Menu_DrawString( int, int, const char * );
void Menu_DrawStringDark( int, int, const char * );
void Menu_DrawStringR2L( int, int, const char * );
void Menu_DrawStringR2LDark( int, int, const char * );
#endif

224
client/ref.h Normal file
View File

@ -0,0 +1,224 @@
/*
Copyright (C) 1997-2001 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.
*/
#include "../qcommon/qcommon.h"
#define MAX_DLIGHTS 32
#define MAX_ENTITIES 128
#define MAX_PARTICLES 4096
#define MAX_LIGHTSTYLES 256
#define POWERSUIT_SCALE 4.0F
#define SHELL_RED_COLOR 0xF2
#define SHELL_GREEN_COLOR 0xD0
#define SHELL_BLUE_COLOR 0xF3
#define SHELL_RG_COLOR 0xDC
//#define SHELL_RB_COLOR 0x86
#define SHELL_RB_COLOR 0x68
#define SHELL_BG_COLOR 0x78
//ROGUE
#define SHELL_DOUBLE_COLOR 0xDF // 223
#define SHELL_HALF_DAM_COLOR 0x90
#define SHELL_CYAN_COLOR 0x72
//ROGUE
#define SHELL_WHITE_COLOR 0xD7
typedef struct entity_s
{
struct model_s *model; // opaque type outside refresh
float angles[3];
/*
** most recent data
*/
float origin[3]; // also used as RF_BEAM's "from"
int frame; // also used as RF_BEAM's diameter
/*
** previous data for lerping
*/
float oldorigin[3]; // also used as RF_BEAM's "to"
int oldframe;
/*
** misc
*/
float backlerp; // 0.0 = current, 1.0 = old
int skinnum; // also used as RF_BEAM's palette index
int lightstyle; // for flashing entities
float alpha; // ignore if RF_TRANSLUCENT isn't set
struct image_s *skin; // NULL for inline skin
int flags;
} entity_t;
#define ENTITY_FLAGS 68
typedef struct
{
vec3_t origin;
vec3_t color;
float intensity;
} dlight_t;
typedef struct
{
vec3_t origin;
int color;
float alpha;
} particle_t;
typedef struct
{
float rgb[3]; // 0.0 - 2.0
float white; // highest of rgb
} lightstyle_t;
typedef struct
{
int x, y, width, height;// in virtual screen coordinates
float fov_x, fov_y;
float vieworg[3];
float viewangles[3];
float blend[4]; // rgba 0-1 full screen blend
float time; // time is uesed to auto animate
int rdflags; // RDF_UNDERWATER, etc
byte *areabits; // if not NULL, only areas with set bits will be drawn
lightstyle_t *lightstyles; // [MAX_LIGHTSTYLES]
int num_entities;
entity_t *entities;
int num_dlights;
dlight_t *dlights;
int num_particles;
particle_t *particles;
} refdef_t;
#define API_VERSION 3
//
// these are the functions exported by the refresh module
//
typedef struct
{
// if api_version is different, the dll cannot be used
int api_version;
// called when the library is loaded
qboolean (*Init) ( void *hinstance, void *wndproc );
// called before the library is unloaded
void (*Shutdown) (void);
// All data that will be used in a level should be
// registered before rendering any frames to prevent disk hits,
// but they can still be registered at a later time
// if necessary.
//
// EndRegistration will free any remaining data that wasn't registered.
// Any model_s or skin_s pointers from before the BeginRegistration
// are no longer valid after EndRegistration.
//
// Skins and images need to be differentiated, because skins
// are flood filled to eliminate mip map edge errors, and pics have
// an implicit "pics/" prepended to the name. (a pic name that starts with a
// slash will not use the "pics/" prefix or the ".pcx" postfix)
void (*BeginRegistration) (char *map);
struct model_s *(*RegisterModel) (char *name);
struct image_s *(*RegisterSkin) (char *name);
struct image_s *(*RegisterPic) (char *name);
void (*SetSky) (char *name, float rotate, vec3_t axis);
void (*EndRegistration) (void);
void (*RenderFrame) (refdef_t *fd);
void (*DrawGetPicSize) (int *w, int *h, char *name); // will return 0 0 if not found
void (*DrawPic) (int x, int y, char *name);
void (*DrawStretchPic) (int x, int y, int w, int h, char *name);
void (*DrawChar) (int x, int y, int c);
void (*DrawTileClear) (int x, int y, int w, int h, char *name);
void (*DrawFill) (int x, int y, int w, int h, int c);
void (*DrawFadeScreen) (void);
// Draw images for cinematic rendering (which can have a different palette). Note that calls
void (*DrawStretchRaw) (int x, int y, int w, int h, int cols, int rows, byte *data);
/*
** video mode and refresh state management entry points
*/
void (*CinematicSetPalette)( const unsigned char *palette); // NULL = game palette
void (*BeginFrame)( float camera_separation );
void (*EndFrame) (void);
void (*AppActivate)( qboolean activate );
} refexport_t;
//
// these are the functions imported by the refresh module
//
typedef struct
{
void (*Sys_Error) (int err_level, char *str, ...);
void (*Cmd_AddCommand) (char *name, void(*cmd)(void));
void (*Cmd_RemoveCommand) (char *name);
int (*Cmd_Argc) (void);
char *(*Cmd_Argv) (int i);
void (*Cmd_ExecuteText) (int exec_when, char *text);
void (*Con_Printf) (int print_level, char *str, ...);
// files will be memory mapped read only
// the returned buffer may be part of a larger pak file,
// or a discrete file from anywhere in the quake search path
// a -1 return means the file does not exist
// NULL can be passed for buf to just determine existance
int (*FS_LoadFile) (char *name, void **buf);
void (*FS_FreeFile) (void *buf);
// gamedir will be the current directory that generated
// files should be stored to, ie: "f:\quake\id1"
char *(*FS_Gamedir) (void);
cvar_t *(*Cvar_Get) (char *name, char *value, int flags);
cvar_t *(*Cvar_Set)( char *name, char *value );
void (*Cvar_SetValue)( char *name, float value );
qboolean (*Vid_GetModeInfo)( int *width, int *height, int mode );
void (*Vid_MenuInit)( void );
void (*Vid_NewWindow)( int width, int height );
} refimport_t;
// this is the only function actually exported at the linker level
typedef refexport_t (*GetRefAPI_t) (refimport_t);

62
client/screen.h Normal file
View File

@ -0,0 +1,62 @@
/*
Copyright (C) 1997-2001 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.h
void SCR_Init (void);
void SCR_UpdateScreen (void);
void SCR_SizeUp (void);
void SCR_SizeDown (void);
void SCR_CenterPrint (char *str);
void SCR_BeginLoadingPlaque (void);
void SCR_EndLoadingPlaque (void);
void SCR_DebugGraph (float value, int color);
void SCR_TouchPics (void);
void SCR_RunConsole (void);
extern float scr_con_current;
extern float scr_conlines; // lines of console to display
extern int sb_lines;
extern cvar_t *scr_viewsize;
extern cvar_t *crosshair;
extern vrect_t scr_vrect; // position of render window
extern char crosshair_pic[MAX_QPATH];
extern int crosshair_width, crosshair_height;
void SCR_AddDirtyPoint (int x, int y);
void SCR_DirtyScreen (void);
//
// scr_cin.c
//
void SCR_PlayCinematic (char *name);
qboolean SCR_DrawCinematic (void);
void SCR_RunCinematic (void);
void SCR_StopCinematic (void);
void SCR_FinishCinematic (void);

1214
client/snd_dma.c Normal file

File diff suppressed because it is too large Load Diff

164
client/snd_loc.h Normal file
View File

@ -0,0 +1,164 @@
/*
Copyright (C) 1997-2001 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.
*/
// snd_loc.h -- private sound functions
// !!! if this is changed, the asm code must change !!!
typedef struct
{
int left;
int right;
} portable_samplepair_t;
typedef struct
{
int length;
int loopstart;
int speed; // not needed, because converted on load?
int width;
int stereo;
byte data[1]; // variable sized
} sfxcache_t;
typedef struct sfx_s
{
char name[MAX_QPATH];
int registration_sequence;
sfxcache_t *cache;
char *truename;
} sfx_t;
// a playsound_t will be generated by each call to S_StartSound,
// when the mixer reaches playsound->begin, the playsound will
// be assigned to a channel
typedef struct playsound_s
{
struct playsound_s *prev, *next;
sfx_t *sfx;
float volume;
float attenuation;
int entnum;
int entchannel;
qboolean fixed_origin; // use origin field instead of entnum's origin
vec3_t origin;
unsigned begin; // begin on this sample
} playsound_t;
typedef struct
{
int channels;
int samples; // mono samples in buffer
int submission_chunk; // don't mix less than this #
int samplepos; // in mono samples
int samplebits;
int speed;
byte *buffer;
} dma_t;
// !!! if this is changed, the asm code must change !!!
typedef struct
{
sfx_t *sfx; // sfx number
int leftvol; // 0-255 volume
int rightvol; // 0-255 volume
int end; // end time in global paintsamples
int pos; // sample position in sfx
int looping; // where to loop, -1 = no looping OBSOLETE?
int entnum; // to allow overriding a specific sound
int entchannel; //
vec3_t origin; // only use if fixed_origin is set
vec_t dist_mult; // distance multiplier (attenuation/clipK)
int master_vol; // 0-255 master volume
qboolean fixed_origin; // use origin instead of fetching entnum's origin
qboolean autosound; // from an entity->sound, cleared each frame
} channel_t;
typedef struct
{
int rate;
int width;
int channels;
int loopstart;
int samples;
int dataofs; // chunk starts this many bytes from file start
} wavinfo_t;
/*
====================================================================
SYSTEM SPECIFIC FUNCTIONS
====================================================================
*/
// initializes cycling through a DMA buffer and returns information on it
qboolean SNDDMA_Init(void);
// gets the current DMA position
int SNDDMA_GetDMAPos(void);
// shutdown the DMA xfer.
void SNDDMA_Shutdown(void);
void SNDDMA_BeginPainting (void);
void SNDDMA_Submit(void);
//====================================================================
#define MAX_CHANNELS 32
extern channel_t channels[MAX_CHANNELS];
extern int paintedtime;
extern int s_rawend;
extern vec3_t listener_origin;
extern vec3_t listener_forward;
extern vec3_t listener_right;
extern vec3_t listener_up;
extern dma_t dma;
extern playsound_t s_pendingplays;
#define MAX_RAW_SAMPLES 8192
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
extern cvar_t *s_volume;
extern cvar_t *s_nosound;
extern cvar_t *s_loadas8bit;
extern cvar_t *s_khz;
extern cvar_t *s_show;
extern cvar_t *s_mixahead;
extern cvar_t *s_testsound;
extern cvar_t *s_primary;
wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength);
void S_InitScaletable (void);
sfxcache_t *S_LoadSound (sfx_t *s);
void S_IssuePlaysound (playsound_t *ps);
void S_PaintChannels(int endtime);
// picks a channel based on priorities, empty slots, number of channels
channel_t *S_PickChannel(int entnum, int entchannel);
// spatializes a channel
void S_Spatialize(channel_t *ch);

359
client/snd_mem.c Normal file
View File

@ -0,0 +1,359 @@
/*
Copyright (C) 1997-2001 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.
*/
// snd_mem.c: sound caching
#include "client.h"
#include "snd_loc.h"
int cache_full_cycle;
byte *S_Alloc (int size);
/*
================
ResampleSfx
================
*/
void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
{
int outcount;
int srcsample;
float stepscale;
int i;
int sample, samplefrac, fracstep;
sfxcache_t *sc;
sc = sfx->cache;
if (!sc)
return;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = sc->length / stepscale;
sc->length = outcount;
if (sc->loopstart != -1)
sc->loopstart = sc->loopstart / stepscale;
sc->speed = dma.speed;
if (s_loadas8bit->value)
sc->width = 1;
else
sc->width = inwidth;
sc->stereo = 0;
// resample / decimate to the current source rate
if (stepscale == 1 && inwidth == 1 && sc->width == 1)
{
// fast special case
for (i=0 ; i<outcount ; i++)
((signed char *)sc->data)[i]
= (int)( (unsigned char)(data[i]) - 128);
}
else
{
// general case
samplefrac = 0;
fracstep = stepscale*256;
for (i=0 ; i<outcount ; i++)
{
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if (inwidth == 2)
sample = LittleShort ( ((short *)data)[srcsample] );
else
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
if (sc->width == 2)
((short *)sc->data)[i] = sample;
else
((signed char *)sc->data)[i] = sample >> 8;
}
}
}
//=============================================================================
/*
==============
S_LoadSound
==============
*/
sfxcache_t *S_LoadSound (sfx_t *s)
{
char namebuffer[MAX_QPATH];
byte *data;
wavinfo_t info;
int len;
float stepscale;
sfxcache_t *sc;
int size;
char *name;
if (s->name[0] == '*')
return NULL;
// see if still in memory
sc = s->cache;
if (sc)
return sc;
//Com_Printf ("S_LoadSound: %x\n", (int)stackbuf);
// load it in
if (s->truename)
name = s->truename;
else
name = s->name;
if (name[0] == '#')
strcpy(namebuffer, &name[1]);
else
Com_sprintf (namebuffer, sizeof(namebuffer), "sound/%s", name);
// Com_Printf ("loading %s\n",namebuffer);
size = FS_LoadFile (namebuffer, (void **)&data);
if (!data)
{
Com_DPrintf ("Couldn't load %s\n", namebuffer);
return NULL;
}
info = GetWavinfo (s->name, data, size);
if (info.channels != 1)
{
Com_Printf ("%s is a stereo sample\n",s->name);
FS_FreeFile (data);
return NULL;
}
stepscale = (float)info.rate / dma.speed;
len = info.samples / stepscale;
len = len * info.width * info.channels;
sc = s->cache = Z_Malloc (len + sizeof(sfxcache_t));
if (!sc)
{
FS_FreeFile (data);
return NULL;
}
sc->length = info.samples;
sc->loopstart = info.loopstart;
sc->speed = info.rate;
sc->width = info.width;
sc->stereo = info.channels;
ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);
FS_FreeFile (data);
return sc;
}
/*
===============================================================================
WAV loading
===============================================================================
*/
byte *data_p;
byte *iff_end;
byte *last_chunk;
byte *iff_data;
int iff_chunk_len;
short GetLittleShort(void)
{
short val = 0;
val = *data_p;
val = val + (*(data_p+1)<<8);
data_p += 2;
return val;
}
int GetLittleLong(void)
{
int val = 0;
val = *data_p;
val = val + (*(data_p+1)<<8);
val = val + (*(data_p+2)<<16);
val = val + (*(data_p+3)<<24);
data_p += 4;
return val;
}
void FindNextChunk(char *name)
{
while (1)
{
data_p=last_chunk;
if (data_p >= iff_end)
{ // didn't find the chunk
data_p = NULL;
return;
}
data_p += 4;
iff_chunk_len = GetLittleLong();
if (iff_chunk_len < 0)
{
data_p = NULL;
return;
}
// if (iff_chunk_len > 1024*1024)
// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
data_p -= 8;
last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
if (!strncmp(data_p, name, 4))
return;
}
}
void FindChunk(char *name)
{
last_chunk = iff_data;
FindNextChunk (name);
}
void DumpChunks(void)
{
char str[5];
str[4] = 0;
data_p=iff_data;
do
{
memcpy (str, data_p, 4);
data_p += 4;
iff_chunk_len = GetLittleLong();
Com_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
data_p += (iff_chunk_len + 1) & ~1;
} while (data_p < iff_end);
}
/*
============
GetWavinfo
============
*/
wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
{
wavinfo_t info;
int i;
int format;
int samples;
memset (&info, 0, sizeof(info));
if (!wav)
return info;
iff_data = wav;
iff_end = wav + wavlength;
// find "RIFF" chunk
FindChunk("RIFF");
if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
{
Com_Printf("Missing RIFF/WAVE chunks\n");
return info;
}
// get "fmt " chunk
iff_data = data_p + 12;
// DumpChunks ();
FindChunk("fmt ");
if (!data_p)
{
Com_Printf("Missing fmt chunk\n");
return info;
}
data_p += 8;
format = GetLittleShort();
if (format != 1)
{
Com_Printf("Microsoft PCM format only\n");
return info;
}
info.channels = GetLittleShort();
info.rate = GetLittleLong();
data_p += 4+2;
info.width = GetLittleShort() / 8;
// get cue chunk
FindChunk("cue ");
if (data_p)
{
data_p += 32;
info.loopstart = GetLittleLong();
// Com_Printf("loopstart=%d\n", sfx->loopstart);
// if the next chunk is a LIST chunk, look for a cue length marker
FindNextChunk ("LIST");
if (data_p)
{
if (!strncmp (data_p + 28, "mark", 4))
{ // this is not a proper parse, but it works with cooledit...
data_p += 24;
i = GetLittleLong (); // samples in loop
info.samples = info.loopstart + i;
// Com_Printf("looped length: %i\n", i);
}
}
}
else
info.loopstart = -1;
// find data chunk
FindChunk("data");
if (!data_p)
{
Com_Printf("Missing data chunk\n");
return info;
}
data_p += 4;
samples = GetLittleLong () / info.width;
if (info.samples)
{
if (samples < info.samples)
Com_Error (ERR_DROP, "Sound %s has a bad loop length", name);
}
else
info.samples = samples;
info.dataofs = data_p - wav;
return info;
}

497
client/snd_mix.c Normal file
View File

@ -0,0 +1,497 @@
/*
Copyright (C) 1997-2001 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.
*/
// snd_mix.c -- portable code to mix sounds for snd_dma.c
#include "client.h"
#include "snd_loc.h"
#define PAINTBUFFER_SIZE 2048
portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
int snd_scaletable[32][256];
int *snd_p, snd_linear_count, snd_vol;
short *snd_out;
void S_WriteLinearBlastStereo16 (void);
#if !(defined __linux__ && defined __i386__)
#if !id386
void S_WriteLinearBlastStereo16 (void)
{
int i;
int val;
for (i=0 ; i<snd_linear_count ; i+=2)
{
val = snd_p[i]>>8;
if (val > 0x7fff)
snd_out[i] = 0x7fff;
else if (val < (short)0x8000)
snd_out[i] = (short)0x8000;
else
snd_out[i] = val;
val = snd_p[i+1]>>8;
if (val > 0x7fff)
snd_out[i+1] = 0x7fff;
else if (val < (short)0x8000)
snd_out[i+1] = (short)0x8000;
else
snd_out[i+1] = val;
}
}
#else
__declspec( naked ) void S_WriteLinearBlastStereo16 (void)
{
__asm {
push edi
push ebx
mov ecx,ds:dword ptr[snd_linear_count]
mov ebx,ds:dword ptr[snd_p]
mov edi,ds:dword ptr[snd_out]
LWLBLoopTop:
mov eax,ds:dword ptr[-8+ebx+ecx*4]
sar eax,8
cmp eax,07FFFh
jg LClampHigh
cmp eax,0FFFF8000h
jnl LClampDone
mov eax,0FFFF8000h
jmp LClampDone
LClampHigh:
mov eax,07FFFh
LClampDone:
mov edx,ds:dword ptr[-4+ebx+ecx*4]
sar edx,8
cmp edx,07FFFh
jg LClampHigh2
cmp edx,0FFFF8000h
jnl LClampDone2
mov edx,0FFFF8000h
jmp LClampDone2
LClampHigh2:
mov edx,07FFFh
LClampDone2:
shl edx,16
and eax,0FFFFh
or edx,eax
mov ds:dword ptr[-4+edi+ecx*2],edx
sub ecx,2
jnz LWLBLoopTop
pop ebx
pop edi
ret
}
}
#endif
#endif
void S_TransferStereo16 (unsigned long *pbuf, int endtime)
{
int lpos;
int lpaintedtime;
snd_p = (int *) paintbuffer;
lpaintedtime = paintedtime;
while (lpaintedtime < endtime)
{
// handle recirculating buffer issues
lpos = lpaintedtime & ((dma.samples>>1)-1);
snd_out = (short *) pbuf + (lpos<<1);
snd_linear_count = (dma.samples>>1) - lpos;
if (lpaintedtime + snd_linear_count > endtime)
snd_linear_count = endtime - lpaintedtime;
snd_linear_count <<= 1;
// write a linear blast of samples
S_WriteLinearBlastStereo16 ();
snd_p += snd_linear_count;
lpaintedtime += (snd_linear_count>>1);
}
}
/*
===================
S_TransferPaintBuffer
===================
*/
void S_TransferPaintBuffer(int endtime)
{
int out_idx;
int count;
int out_mask;
int *p;
int step;
int val;
unsigned long *pbuf;
pbuf = (unsigned long *)dma.buffer;
if (s_testsound->value)
{
int i;
int count;
// write a fixed sine wave
count = (endtime - paintedtime);
for (i=0 ; i<count ; i++)
paintbuffer[i].left = paintbuffer[i].right = sin((paintedtime+i)*0.1)*20000*256;
}
if (dma.samplebits == 16 && dma.channels == 2)
{ // optimized case
S_TransferStereo16 (pbuf, endtime);
}
else
{ // general case
p = (int *) paintbuffer;
count = (endtime - paintedtime) * dma.channels;
out_mask = dma.samples - 1;
out_idx = paintedtime * dma.channels & out_mask;
step = 3 - dma.channels;
if (dma.samplebits == 16)
{
short *out = (short *) pbuf;
while (count--)
{
val = *p >> 8;
p+= step;
if (val > 0x7fff)
val = 0x7fff;
else if (val < (short)0x8000)
val = (short)0x8000;
out[out_idx] = val;
out_idx = (out_idx + 1) & out_mask;
}
}
else if (dma.samplebits == 8)
{
unsigned char *out = (unsigned char *) pbuf;
while (count--)
{
val = *p >> 8;
p+= step;
if (val > 0x7fff)
val = 0x7fff;
else if (val < (short)0x8000)
val = (short)0x8000;
out[out_idx] = (val>>8) + 128;
out_idx = (out_idx + 1) & out_mask;
}
}
}
}
/*
===============================================================================
CHANNEL MIXING
===============================================================================
*/
void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime, int offset);
void S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime, int offset);
void S_PaintChannels(int endtime)
{
int i;
int end;
channel_t *ch;
sfxcache_t *sc;
int ltime, count;
playsound_t *ps;
snd_vol = s_volume->value*256;
//Com_Printf ("%i to %i\n", paintedtime, endtime);
while (paintedtime < endtime)
{
// if paintbuffer is smaller than DMA buffer
end = endtime;
if (endtime - paintedtime > PAINTBUFFER_SIZE)
end = paintedtime + PAINTBUFFER_SIZE;
// start any playsounds
while (1)
{
ps = s_pendingplays.next;
if (ps == &s_pendingplays)
break; // no more pending sounds
if (ps->begin <= paintedtime)
{
S_IssuePlaysound (ps);
continue;
}
if (ps->begin < end)
end = ps->begin; // stop here
break;
}
// clear the paint buffer
if (s_rawend < paintedtime)
{
// Com_Printf ("clear\n");
memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
}
else
{ // copy from the streaming sound source
int s;
int stop;
stop = (end < s_rawend) ? end : s_rawend;
for (i=paintedtime ; i<stop ; i++)
{
s = i&(MAX_RAW_SAMPLES-1);
paintbuffer[i-paintedtime] = s_rawsamples[s];
}
// if (i != end)
// Com_Printf ("partial stream\n");
// else
// Com_Printf ("full stream\n");
for ( ; i<end ; i++)
{
paintbuffer[i-paintedtime].left =
paintbuffer[i-paintedtime].right = 0;
}
}
// paint in the channels.
ch = channels;
for (i=0; i<MAX_CHANNELS ; i++, ch++)
{
ltime = paintedtime;
while (ltime < end)
{
if (!ch->sfx || (!ch->leftvol && !ch->rightvol) )
break;
// max painting is to the end of the buffer
count = end - ltime;
// might be stopped by running out of data
if (ch->end - ltime < count)
count = ch->end - ltime;
sc = S_LoadSound (ch->sfx);
if (!sc)
break;
if (count > 0 && ch->sfx)
{
if (sc->width == 1)// FIXME; 8 bit asm is wrong now
S_PaintChannelFrom8(ch, sc, count, ltime - paintedtime);
else
S_PaintChannelFrom16(ch, sc, count, ltime - paintedtime);
ltime += count;
}
// if at end of loop, restart
if (ltime >= ch->end)
{
if (ch->autosound)
{ // autolooping sounds always go back to start
ch->pos = 0;
ch->end = ltime + sc->length;
}
else if (sc->loopstart >= 0)
{
ch->pos = sc->loopstart;
ch->end = ltime + sc->length - ch->pos;
}
else
{ // channel just stopped
ch->sfx = NULL;
}
}
}
}
// transfer out according to DMA format
S_TransferPaintBuffer(end);
paintedtime = end;
}
}
void S_InitScaletable (void)
{
int i, j;
int scale;
s_volume->modified = false;
for (i=0 ; i<32 ; i++)
{
scale = i * 8 * 256 * s_volume->value;
for (j=0 ; j<256 ; j++)
snd_scaletable[i][j] = ((signed char)j) * scale;
}
}
#if !(defined __linux__ && defined __i386__)
#if !id386
void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset)
{
int data;
int *lscale, *rscale;
unsigned char *sfx;
int i;
portable_samplepair_t *samp;
if (ch->leftvol > 255)
ch->leftvol = 255;
if (ch->rightvol > 255)
ch->rightvol = 255;
lscale = snd_scaletable[ ch->leftvol >> 11];
rscale = snd_scaletable[ ch->rightvol >> 11];
sfx = (signed char *)sc->data + ch->pos;
samp = &paintbuffer[offset];
for (i=0 ; i<count ; i++, samp++)
{
data = sfx[i];
samp->left += lscale[data];
samp->right += rscale[data];
}
ch->pos += count;
}
#else
__declspec( naked ) void S_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count, int offset)
{
__asm {
push esi
push edi
push ebx
push ebp
mov ebx,ds:dword ptr[4+16+esp]
mov esi,ds:dword ptr[8+16+esp]
mov eax,ds:dword ptr[4+ebx]
mov edx,ds:dword ptr[8+ebx]
cmp eax,255
jna LLeftSet
mov eax,255
LLeftSet:
cmp edx,255
jna LRightSet
mov edx,255
LRightSet:
and eax,0F8h
add esi,20
and edx,0F8h
mov edi,ds:dword ptr[16+ebx]
mov ecx,ds:dword ptr[12+16+esp]
add esi,edi
shl eax,7
add edi,ecx
shl edx,7
mov ds:dword ptr[16+ebx],edi
add eax,offset snd_scaletable
add edx,offset snd_scaletable
sub ebx,ebx
mov bl,ds:byte ptr[-1+esi+ecx*1]
test ecx,1
jz LMix8Loop
mov edi,ds:dword ptr[eax+ebx*4]
mov ebp,ds:dword ptr[edx+ebx*4]
add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
mov bl,ds:byte ptr[-2+esi+ecx*1]
dec ecx
jz LDone
LMix8Loop:
mov edi,ds:dword ptr[eax+ebx*4]
mov ebp,ds:dword ptr[edx+ebx*4]
add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
mov bl,ds:byte ptr[-2+esi+ecx*1]
mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
mov edi,ds:dword ptr[eax+ebx*4]
mov ebp,ds:dword ptr[edx+ebx*4]
mov bl,ds:byte ptr[-3+esi+ecx*1]
add edi,ds:dword ptr[paintbuffer+0-8*2+ecx*8]
add ebp,ds:dword ptr[paintbuffer+4-8*2+ecx*8]
mov ds:dword ptr[paintbuffer+0-8*2+ecx*8],edi
mov ds:dword ptr[paintbuffer+4-8*2+ecx*8],ebp
sub ecx,2
jnz LMix8Loop
LDone:
pop ebp
pop ebx
pop edi
pop esi
ret
}
}
#endif
#endif
void S_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count, int offset)
{
int data;
int left, right;
int leftvol, rightvol;
signed short *sfx;
int i;
portable_samplepair_t *samp;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
sfx = (signed short *)sc->data + ch->pos;
samp = &paintbuffer[offset];
for (i=0 ; i<count ; i++, samp++)
{
data = sfx[i];
left = (data * leftvol)>>8;
right = (data * rightvol)>>8;
samp->left += left;
samp->right += right;
}
ch->pos += count;
}

45
client/sound.h Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright (C) 1997-2001 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.
*/
struct sfx_s;
void S_Init (void);
void S_Shutdown (void);
// if origin is NULL, the sound will be dynamically sourced from the entity
void S_StartSound (vec3_t origin, int entnum, int entchannel, struct sfx_s *sfx, float fvol, float attenuation, float timeofs);
void S_StartLocalSound (char *s);
void S_RawSamples (int samples, int rate, int width, int channels, byte *data);
void S_StopAllSounds(void);
void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up);
void S_Activate (qboolean active);
void S_BeginRegistration (void);
struct sfx_s *S_RegisterSound (char *sample);
void S_EndRegistration (void);
struct sfx_s *S_FindName (char *name, qboolean create);
// the sound code makes callbacks to the client for entitiy position
// information, so entities can be dynamically re-spatialized
void CL_GetEntitySoundOrigin (int ent, vec3_t org);

42
client/vid.h Normal file
View File

@ -0,0 +1,42 @@
/*
Copyright (C) 1997-2001 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.
*/
// vid.h -- video driver defs
typedef struct vrect_s
{
int x,y,width,height;
} vrect_t;
typedef struct
{
int width;
int height;
} viddef_t;
extern viddef_t viddef; // global video state
// Video module initialisation etc
void VID_Init (void);
void VID_Shutdown (void);
void VID_CheckChanges (void);
void VID_MenuInit( void );
void VID_MenuDraw( void );
const char *VID_MenuKey( int );

95
client/x86.c Normal file
View File

@ -0,0 +1,95 @@
/*
Copyright (C) 1997-2001 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.
*/
#include <stdlib.h>
#include "client.h"
#if id386
static unsigned long bias;
static unsigned long *histogram;
static unsigned long start, range;
static unsigned long bias;
__declspec( naked ) void x86_TimerStart( void )
{
__asm _emit 0fh
__asm _emit 31h
__asm mov start, eax
__asm ret
}
__declspec( naked ) void x86_TimerStop( void )
{
__asm push edi
__asm mov edi, histogram
__asm _emit 0fh
__asm _emit 31h
__asm sub eax, start
__asm sub eax, bias
__asm js discard
__asm cmp eax, range
__asm jge discard
__asm lea edi, [edi + eax*4]
__asm inc dword ptr [edi]
discard:
__asm pop edi
__asm ret
}
#pragma warning( disable: 4035 )
static __declspec( naked ) unsigned long x86_TimerStopBias( void )
{
__asm push edi
__asm mov edi, histogram
__asm _emit 0fh
__asm _emit 31h
__asm sub eax, start
__asm pop edi
__asm ret
}
#pragma warning( default:4035 )
void x86_TimerInit( unsigned long smallest, unsigned length )
{
int i;
unsigned long biastable[100];
range = length;
bias = 10000;
for ( i = 0; i < 100; i++ )
{
x86_TimerStart();
biastable[i] = x86_TimerStopBias();
if ( bias > biastable[i] )
bias = biastable[i];
}
bias += smallest;
histogram = Z_Malloc( range * sizeof( unsigned long ) );
}
unsigned long *x86_TimerGetHistogram( void )
{
return histogram;
}
#endif