mirror of
https://github.com/ioquake/ioq3.git
synced 2025-05-30 00:20:59 +00:00
The Quake III Arena sources as originally released under the GPL license on August 20, 2005.
This commit is contained in:
commit
dbe4ddb103
1409 changed files with 806066 additions and 0 deletions
138
code/cgame/Conscript
Normal file
138
code/cgame/Conscript
Normal file
|
@ -0,0 +1,138 @@
|
|||
# cgame building
|
||||
# builds the cgame for vanilla Q3 and TA
|
||||
|
||||
# there are slight differences between Q3 and TA build:
|
||||
# -DMISSIONPACK
|
||||
# TA has cg_newdraw.c and ../ui/ui_shared.c
|
||||
# the config is passed in the imported variable TARGET_DIR
|
||||
|
||||
# qvm building against native:
|
||||
# only native has cg_syscalls.c
|
||||
# only qvm has ../game/bg_lib.c
|
||||
# qvm uses a custom cg_syscalls.asm with equ stubs
|
||||
|
||||
Import qw( BASE_CFLAGS TARGET_DIR INSTALL_DIR NO_VM NO_SO CC CXX LINK );
|
||||
|
||||
$env = new cons(
|
||||
# the code has the very bad habit of doing things like #include "../ui/ui_shared.h"
|
||||
# this seems to confuse the dependency analysis, explicit toplevel includes seem to fix
|
||||
CPPPATH => '#cgame:#game:#q3_ui',
|
||||
CC => $CC,
|
||||
CXX => $CXX,
|
||||
LINK => $LINK,
|
||||
ENV => { PATH => $ENV{PATH}, HOME => $ENV{HOME} },
|
||||
CFLAGS => $BASE_CFLAGS . '-fPIC',
|
||||
LDFLAGS => '-shared -ldl -lm'
|
||||
);
|
||||
|
||||
# for TA, use -DMISSIONPACK
|
||||
%ta_env_hash = $env->copy(
|
||||
CPPPATH => '#cgame:#game:#ui'
|
||||
);
|
||||
$ta_env_hash{CFLAGS} = '-DMISSIONPACK ' . $ta_env_hash{CFLAGS};
|
||||
$ta_env = new cons(%ta_env_hash);
|
||||
|
||||
# qvm building
|
||||
# we heavily customize the cons environment
|
||||
$vm_env = new cons(
|
||||
# the code has the very bad habit of doing things like #include "../ui/ui_shared.h"
|
||||
# this seems to confuse the dependency analysis, explicit toplevel includes seem to fix
|
||||
CPPPATH => '#cgame:#game:#q3_ui',
|
||||
CC => 'q3lcc',
|
||||
CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
|
||||
SUFOBJ => '.asm',
|
||||
LINK => 'q3asm',
|
||||
CFLAGS => '-DQ3_VM -DCGAME -S -Wf-target=bytecode -Wf-g',
|
||||
# need to know where to find the compiler tools
|
||||
ENV => { PATH => $ENV{PATH} . ":./qvmtools", },
|
||||
);
|
||||
|
||||
# TA qvm building
|
||||
%vm_ta_env_hash = $vm_env->copy(
|
||||
CPPPATH => '#cgame:#game:#ui'
|
||||
);
|
||||
$vm_ta_env_hash{CFLAGS} = '-DMISSIONPACK ' . $vm_ta_env_hash{CFLAGS};
|
||||
$vm_ta_env = new cons(%vm_ta_env_hash);
|
||||
|
||||
# the file with vmMain function MUST be the first one of the list
|
||||
@FILES = qw(
|
||||
cg_main.c
|
||||
../game/bg_misc.c
|
||||
../game/bg_pmove.c
|
||||
../game/bg_slidemove.c
|
||||
../game/q_math.c
|
||||
../game/q_shared.c
|
||||
cg_consolecmds.c
|
||||
cg_draw.c
|
||||
cg_drawtools.c
|
||||
cg_effects.c
|
||||
cg_ents.c
|
||||
cg_event.c
|
||||
cg_info.c
|
||||
cg_localents.c
|
||||
cg_marks.c
|
||||
cg_players.c
|
||||
cg_playerstate.c
|
||||
cg_predict.c
|
||||
cg_scoreboard.c
|
||||
cg_servercmds.c
|
||||
cg_snapshot.c
|
||||
cg_view.c
|
||||
cg_weapons.c
|
||||
);
|
||||
$FILESREF = \@FILES;
|
||||
|
||||
# only in .so
|
||||
# (VM uses a custom .asm with equ stubs)
|
||||
@SO_FILES = qw(
|
||||
cg_syscalls.c
|
||||
);
|
||||
$SO_FILESREF = \@SO_FILES;
|
||||
|
||||
# only for VM
|
||||
@VM_FILES = qw(
|
||||
../game/bg_lib.c
|
||||
cg_syscalls.asm
|
||||
);
|
||||
$VM_FILESREF = \@VM_FILES;
|
||||
|
||||
# common additionals for TA
|
||||
@TA_FILES = qw(
|
||||
cg_newdraw.c
|
||||
../ui/ui_shared.c
|
||||
);
|
||||
$TA_FILESREF = \@TA_FILES;
|
||||
|
||||
# FIXME CPU string
|
||||
if ($TARGET_DIR eq 'Q3')
|
||||
{
|
||||
if ($NO_SO eq 0)
|
||||
{
|
||||
Program $env 'cgamei386.so', @$FILESREF, @$SO_FILESREF;
|
||||
Install $env $INSTALL_DIR, 'cgamei386.so';
|
||||
}
|
||||
if ($NO_VM eq 0)
|
||||
{
|
||||
Depends $vm_env 'cgame.qvm', '#qvmtools/q3lcc';
|
||||
Depends $vm_env 'cgame.qvm', '#qvmtools/q3asm';
|
||||
Program $vm_env 'cgame.qvm', @$FILESREF, @$VM_FILESREF;
|
||||
Install $vm_env $INSTALL_DIR . '/vm', 'cgame.qvm';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($NO_SO eq 0)
|
||||
{
|
||||
Program $ta_env 'cgamei386.so',
|
||||
@$FILESREF, @$SO_FILESREF, @$TA_FILESREF;
|
||||
Install $ta_env $INSTALL_DIR, 'cgamei386.so';
|
||||
}
|
||||
if ($NO_VM eq 0)
|
||||
{
|
||||
Depends $vm_env 'cgame.qvm', '#qvmtools/q3lcc';
|
||||
Depends $vm_env 'cgame.qvm', '#qvmtools/q3asm';
|
||||
Program $vm_ta_env 'cgame.qvm',
|
||||
@$FILESREF, @$VM_FILESREF, @$TA_FILESREF;
|
||||
Install $vm_ta_env $INSTALL_DIR . '/vm', 'cgame.qvm';
|
||||
}
|
||||
}
|
578
code/cgame/cg_consolecmds.c
Normal file
578
code/cgame/cg_consolecmds.c
Normal file
|
@ -0,0 +1,578 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_consolecmds.c -- text commands typed in at the local console, or
|
||||
// executed by a key binding
|
||||
|
||||
#include "cg_local.h"
|
||||
#include "../ui/ui_shared.h"
|
||||
#ifdef MISSIONPACK
|
||||
extern menuDef_t *menuScoreboard;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void CG_TargetCommand_f( void ) {
|
||||
int targetNum;
|
||||
char test[4];
|
||||
|
||||
targetNum = CG_CrosshairPlayer();
|
||||
if (!targetNum ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Argv( 1, test, 4 );
|
||||
trap_SendConsoleCommand( va( "gc %i %i", targetNum, atoi( test ) ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_SizeUp_f
|
||||
|
||||
Keybinding command
|
||||
=================
|
||||
*/
|
||||
static void CG_SizeUp_f (void) {
|
||||
trap_Cvar_Set("cg_viewsize", va("%i",(int)(cg_viewsize.integer+10)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_SizeDown_f
|
||||
|
||||
Keybinding command
|
||||
=================
|
||||
*/
|
||||
static void CG_SizeDown_f (void) {
|
||||
trap_Cvar_Set("cg_viewsize", va("%i",(int)(cg_viewsize.integer-10)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_Viewpos_f
|
||||
|
||||
Debugging command to print the current position
|
||||
=============
|
||||
*/
|
||||
static void CG_Viewpos_f (void) {
|
||||
CG_Printf ("(%i %i %i) : %i\n", (int)cg.refdef.vieworg[0],
|
||||
(int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2],
|
||||
(int)cg.refdefViewAngles[YAW]);
|
||||
}
|
||||
|
||||
|
||||
static void CG_ScoresDown_f( void ) {
|
||||
|
||||
#ifdef MISSIONPACK
|
||||
CG_BuildSpectatorString();
|
||||
#endif
|
||||
if ( cg.scoresRequestTime + 2000 < cg.time ) {
|
||||
// the scores are more than two seconds out of data,
|
||||
// so request new ones
|
||||
cg.scoresRequestTime = cg.time;
|
||||
trap_SendClientCommand( "score" );
|
||||
|
||||
// leave the current scores up if they were already
|
||||
// displayed, but if this is the first hit, clear them out
|
||||
if ( !cg.showScores ) {
|
||||
cg.showScores = qtrue;
|
||||
cg.numScores = 0;
|
||||
}
|
||||
} else {
|
||||
// show the cached contents even if they just pressed if it
|
||||
// is within two seconds
|
||||
cg.showScores = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
static void CG_ScoresUp_f( void ) {
|
||||
if ( cg.showScores ) {
|
||||
cg.showScores = qfalse;
|
||||
cg.scoreFadeTime = cg.time;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MISSIONPACK
|
||||
extern menuDef_t *menuScoreboard;
|
||||
void Menu_Reset(); // FIXME: add to right include file
|
||||
|
||||
static void CG_LoadHud_f( void) {
|
||||
char buff[1024];
|
||||
const char *hudSet;
|
||||
memset(buff, 0, sizeof(buff));
|
||||
|
||||
String_Init();
|
||||
Menu_Reset();
|
||||
|
||||
trap_Cvar_VariableStringBuffer("cg_hudFiles", buff, sizeof(buff));
|
||||
hudSet = buff;
|
||||
if (hudSet[0] == '\0') {
|
||||
hudSet = "ui/hud.txt";
|
||||
}
|
||||
|
||||
CG_LoadMenus(hudSet);
|
||||
menuScoreboard = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void CG_scrollScoresDown_f( void) {
|
||||
if (menuScoreboard && cg.scoreBoardShowing) {
|
||||
Menu_ScrollFeeder(menuScoreboard, FEEDER_SCOREBOARD, qtrue);
|
||||
Menu_ScrollFeeder(menuScoreboard, FEEDER_REDTEAM_LIST, qtrue);
|
||||
Menu_ScrollFeeder(menuScoreboard, FEEDER_BLUETEAM_LIST, qtrue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void CG_scrollScoresUp_f( void) {
|
||||
if (menuScoreboard && cg.scoreBoardShowing) {
|
||||
Menu_ScrollFeeder(menuScoreboard, FEEDER_SCOREBOARD, qfalse);
|
||||
Menu_ScrollFeeder(menuScoreboard, FEEDER_REDTEAM_LIST, qfalse);
|
||||
Menu_ScrollFeeder(menuScoreboard, FEEDER_BLUETEAM_LIST, qfalse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void CG_spWin_f( void) {
|
||||
trap_Cvar_Set("cg_cameraOrbit", "2");
|
||||
trap_Cvar_Set("cg_cameraOrbitDelay", "35");
|
||||
trap_Cvar_Set("cg_thirdPerson", "1");
|
||||
trap_Cvar_Set("cg_thirdPersonAngle", "0");
|
||||
trap_Cvar_Set("cg_thirdPersonRange", "100");
|
||||
CG_AddBufferedSound(cgs.media.winnerSound);
|
||||
//trap_S_StartLocalSound(cgs.media.winnerSound, CHAN_ANNOUNCER);
|
||||
CG_CenterPrint("YOU WIN!", SCREEN_HEIGHT * .30, 0);
|
||||
}
|
||||
|
||||
static void CG_spLose_f( void) {
|
||||
trap_Cvar_Set("cg_cameraOrbit", "2");
|
||||
trap_Cvar_Set("cg_cameraOrbitDelay", "35");
|
||||
trap_Cvar_Set("cg_thirdPerson", "1");
|
||||
trap_Cvar_Set("cg_thirdPersonAngle", "0");
|
||||
trap_Cvar_Set("cg_thirdPersonRange", "100");
|
||||
CG_AddBufferedSound(cgs.media.loserSound);
|
||||
//trap_S_StartLocalSound(cgs.media.loserSound, CHAN_ANNOUNCER);
|
||||
CG_CenterPrint("YOU LOSE...", SCREEN_HEIGHT * .30, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void CG_TellTarget_f( void ) {
|
||||
int clientNum;
|
||||
char command[128];
|
||||
char message[128];
|
||||
|
||||
clientNum = CG_CrosshairPlayer();
|
||||
if ( clientNum == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Args( message, 128 );
|
||||
Com_sprintf( command, 128, "tell %i %s", clientNum, message );
|
||||
trap_SendClientCommand( command );
|
||||
}
|
||||
|
||||
static void CG_TellAttacker_f( void ) {
|
||||
int clientNum;
|
||||
char command[128];
|
||||
char message[128];
|
||||
|
||||
clientNum = CG_LastAttacker();
|
||||
if ( clientNum == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Args( message, 128 );
|
||||
Com_sprintf( command, 128, "tell %i %s", clientNum, message );
|
||||
trap_SendClientCommand( command );
|
||||
}
|
||||
|
||||
static void CG_VoiceTellTarget_f( void ) {
|
||||
int clientNum;
|
||||
char command[128];
|
||||
char message[128];
|
||||
|
||||
clientNum = CG_CrosshairPlayer();
|
||||
if ( clientNum == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Args( message, 128 );
|
||||
Com_sprintf( command, 128, "vtell %i %s", clientNum, message );
|
||||
trap_SendClientCommand( command );
|
||||
}
|
||||
|
||||
static void CG_VoiceTellAttacker_f( void ) {
|
||||
int clientNum;
|
||||
char command[128];
|
||||
char message[128];
|
||||
|
||||
clientNum = CG_LastAttacker();
|
||||
if ( clientNum == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Args( message, 128 );
|
||||
Com_sprintf( command, 128, "vtell %i %s", clientNum, message );
|
||||
trap_SendClientCommand( command );
|
||||
}
|
||||
|
||||
#ifdef MISSIONPACK
|
||||
static void CG_NextTeamMember_f( void ) {
|
||||
CG_SelectNextPlayer();
|
||||
}
|
||||
|
||||
static void CG_PrevTeamMember_f( void ) {
|
||||
CG_SelectPrevPlayer();
|
||||
}
|
||||
|
||||
// ASS U ME's enumeration order as far as task specific orders, OFFENSE is zero, CAMP is last
|
||||
//
|
||||
static void CG_NextOrder_f( void ) {
|
||||
clientInfo_t *ci = cgs.clientinfo + cg.snap->ps.clientNum;
|
||||
if (ci) {
|
||||
if (!ci->teamLeader && sortedTeamPlayers[cg_currentSelectedPlayer.integer] != cg.snap->ps.clientNum) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cgs.currentOrder < TEAMTASK_CAMP) {
|
||||
cgs.currentOrder++;
|
||||
|
||||
if (cgs.currentOrder == TEAMTASK_RETRIEVE) {
|
||||
if (!CG_OtherTeamHasFlag()) {
|
||||
cgs.currentOrder++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cgs.currentOrder == TEAMTASK_ESCORT) {
|
||||
if (!CG_YourTeamHasFlag()) {
|
||||
cgs.currentOrder++;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
cgs.currentOrder = TEAMTASK_OFFENSE;
|
||||
}
|
||||
cgs.orderPending = qtrue;
|
||||
cgs.orderTime = cg.time + 3000;
|
||||
}
|
||||
|
||||
|
||||
static void CG_ConfirmOrder_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vtell %d %s\n", cgs.acceptLeader, VOICECHAT_YES));
|
||||
trap_SendConsoleCommand("+button5; wait; -button5");
|
||||
if (cg.time < cgs.acceptOrderTime) {
|
||||
trap_SendClientCommand(va("teamtask %d\n", cgs.acceptTask));
|
||||
cgs.acceptOrderTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void CG_DenyOrder_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vtell %d %s\n", cgs.acceptLeader, VOICECHAT_NO));
|
||||
trap_SendConsoleCommand("+button6; wait; -button6");
|
||||
if (cg.time < cgs.acceptOrderTime) {
|
||||
cgs.acceptOrderTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void CG_TaskOffense_f (void ) {
|
||||
if (cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONGETFLAG));
|
||||
} else {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONOFFENSE));
|
||||
}
|
||||
trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_OFFENSE));
|
||||
}
|
||||
|
||||
static void CG_TaskDefense_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONDEFENSE));
|
||||
trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_DEFENSE));
|
||||
}
|
||||
|
||||
static void CG_TaskPatrol_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONPATROL));
|
||||
trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_PATROL));
|
||||
}
|
||||
|
||||
static void CG_TaskCamp_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONCAMPING));
|
||||
trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_CAMP));
|
||||
}
|
||||
|
||||
static void CG_TaskFollow_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONFOLLOW));
|
||||
trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_FOLLOW));
|
||||
}
|
||||
|
||||
static void CG_TaskRetrieve_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONRETURNFLAG));
|
||||
trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_RETRIEVE));
|
||||
}
|
||||
|
||||
static void CG_TaskEscort_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_ONFOLLOWCARRIER));
|
||||
trap_SendClientCommand(va("teamtask %d\n", TEAMTASK_ESCORT));
|
||||
}
|
||||
|
||||
static void CG_TaskOwnFlag_f (void ) {
|
||||
trap_SendConsoleCommand(va("cmd vsay_team %s\n", VOICECHAT_IHAVEFLAG));
|
||||
}
|
||||
|
||||
static void CG_TauntKillInsult_f (void ) {
|
||||
trap_SendConsoleCommand("cmd vsay kill_insult\n");
|
||||
}
|
||||
|
||||
static void CG_TauntPraise_f (void ) {
|
||||
trap_SendConsoleCommand("cmd vsay praise\n");
|
||||
}
|
||||
|
||||
static void CG_TauntTaunt_f (void ) {
|
||||
trap_SendConsoleCommand("cmd vtaunt\n");
|
||||
}
|
||||
|
||||
static void CG_TauntDeathInsult_f (void ) {
|
||||
trap_SendConsoleCommand("cmd vsay death_insult\n");
|
||||
}
|
||||
|
||||
static void CG_TauntGauntlet_f (void ) {
|
||||
trap_SendConsoleCommand("cmd vsay kill_guantlet\n");
|
||||
}
|
||||
|
||||
static void CG_TaskSuicide_f (void ) {
|
||||
int clientNum;
|
||||
char command[128];
|
||||
|
||||
clientNum = CG_CrosshairPlayer();
|
||||
if ( clientNum == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Com_sprintf( command, 128, "tell %i suicide", clientNum );
|
||||
trap_SendClientCommand( command );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_TeamMenu_f
|
||||
==================
|
||||
*/
|
||||
/*
|
||||
static void CG_TeamMenu_f( void ) {
|
||||
if (trap_Key_GetCatcher() & KEYCATCH_CGAME) {
|
||||
CG_EventHandling(CGAME_EVENT_NONE);
|
||||
trap_Key_SetCatcher(0);
|
||||
} else {
|
||||
CG_EventHandling(CGAME_EVENT_TEAMMENU);
|
||||
//trap_Key_SetCatcher(KEYCATCH_CGAME);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_EditHud_f
|
||||
==================
|
||||
*/
|
||||
/*
|
||||
static void CG_EditHud_f( void ) {
|
||||
//cls.keyCatchers ^= KEYCATCH_CGAME;
|
||||
//VM_Call (cgvm, CG_EVENT_HANDLING, (cls.keyCatchers & KEYCATCH_CGAME) ? CGAME_EVENT_EDITHUD : CGAME_EVENT_NONE);
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_StartOrbit_f
|
||||
==================
|
||||
*/
|
||||
|
||||
static void CG_StartOrbit_f( void ) {
|
||||
char var[MAX_TOKEN_CHARS];
|
||||
|
||||
trap_Cvar_VariableStringBuffer( "developer", var, sizeof( var ) );
|
||||
if ( !atoi(var) ) {
|
||||
return;
|
||||
}
|
||||
if (cg_cameraOrbit.value != 0) {
|
||||
trap_Cvar_Set ("cg_cameraOrbit", "0");
|
||||
trap_Cvar_Set("cg_thirdPerson", "0");
|
||||
} else {
|
||||
trap_Cvar_Set("cg_cameraOrbit", "5");
|
||||
trap_Cvar_Set("cg_thirdPerson", "1");
|
||||
trap_Cvar_Set("cg_thirdPersonAngle", "0");
|
||||
trap_Cvar_Set("cg_thirdPersonRange", "100");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static void CG_Camera_f( void ) {
|
||||
char name[1024];
|
||||
trap_Argv( 1, name, sizeof(name));
|
||||
if (trap_loadCamera(name)) {
|
||||
cg.cameraMode = qtrue;
|
||||
trap_startCamera(cg.time);
|
||||
} else {
|
||||
CG_Printf ("Unable to load camera %s\n",name);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *cmd;
|
||||
void (*function)(void);
|
||||
} consoleCommand_t;
|
||||
|
||||
static consoleCommand_t commands[] = {
|
||||
{ "testgun", CG_TestGun_f },
|
||||
{ "testmodel", CG_TestModel_f },
|
||||
{ "nextframe", CG_TestModelNextFrame_f },
|
||||
{ "prevframe", CG_TestModelPrevFrame_f },
|
||||
{ "nextskin", CG_TestModelNextSkin_f },
|
||||
{ "prevskin", CG_TestModelPrevSkin_f },
|
||||
{ "viewpos", CG_Viewpos_f },
|
||||
{ "+scores", CG_ScoresDown_f },
|
||||
{ "-scores", CG_ScoresUp_f },
|
||||
{ "+zoom", CG_ZoomDown_f },
|
||||
{ "-zoom", CG_ZoomUp_f },
|
||||
{ "sizeup", CG_SizeUp_f },
|
||||
{ "sizedown", CG_SizeDown_f },
|
||||
{ "weapnext", CG_NextWeapon_f },
|
||||
{ "weapprev", CG_PrevWeapon_f },
|
||||
{ "weapon", CG_Weapon_f },
|
||||
{ "tell_target", CG_TellTarget_f },
|
||||
{ "tell_attacker", CG_TellAttacker_f },
|
||||
{ "vtell_target", CG_VoiceTellTarget_f },
|
||||
{ "vtell_attacker", CG_VoiceTellAttacker_f },
|
||||
{ "tcmd", CG_TargetCommand_f },
|
||||
#ifdef MISSIONPACK
|
||||
{ "loadhud", CG_LoadHud_f },
|
||||
{ "nextTeamMember", CG_NextTeamMember_f },
|
||||
{ "prevTeamMember", CG_PrevTeamMember_f },
|
||||
{ "nextOrder", CG_NextOrder_f },
|
||||
{ "confirmOrder", CG_ConfirmOrder_f },
|
||||
{ "denyOrder", CG_DenyOrder_f },
|
||||
{ "taskOffense", CG_TaskOffense_f },
|
||||
{ "taskDefense", CG_TaskDefense_f },
|
||||
{ "taskPatrol", CG_TaskPatrol_f },
|
||||
{ "taskCamp", CG_TaskCamp_f },
|
||||
{ "taskFollow", CG_TaskFollow_f },
|
||||
{ "taskRetrieve", CG_TaskRetrieve_f },
|
||||
{ "taskEscort", CG_TaskEscort_f },
|
||||
{ "taskSuicide", CG_TaskSuicide_f },
|
||||
{ "taskOwnFlag", CG_TaskOwnFlag_f },
|
||||
{ "tauntKillInsult", CG_TauntKillInsult_f },
|
||||
{ "tauntPraise", CG_TauntPraise_f },
|
||||
{ "tauntTaunt", CG_TauntTaunt_f },
|
||||
{ "tauntDeathInsult", CG_TauntDeathInsult_f },
|
||||
{ "tauntGauntlet", CG_TauntGauntlet_f },
|
||||
{ "spWin", CG_spWin_f },
|
||||
{ "spLose", CG_spLose_f },
|
||||
{ "scoresDown", CG_scrollScoresDown_f },
|
||||
{ "scoresUp", CG_scrollScoresUp_f },
|
||||
#endif
|
||||
{ "startOrbit", CG_StartOrbit_f },
|
||||
//{ "camera", CG_Camera_f },
|
||||
{ "loaddeferred", CG_LoadDeferredPlayers }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_ConsoleCommand
|
||||
|
||||
The string has been tokenized and can be retrieved with
|
||||
Cmd_Argc() / Cmd_Argv()
|
||||
=================
|
||||
*/
|
||||
qboolean CG_ConsoleCommand( void ) {
|
||||
const char *cmd;
|
||||
int i;
|
||||
|
||||
cmd = CG_Argv(0);
|
||||
|
||||
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) {
|
||||
if ( !Q_stricmp( cmd, commands[i].cmd ) ) {
|
||||
commands[i].function();
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_InitConsoleCommands
|
||||
|
||||
Let the client system know about all of our commands
|
||||
so it can perform tab completion
|
||||
=================
|
||||
*/
|
||||
void CG_InitConsoleCommands( void ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) {
|
||||
trap_AddCommand( commands[i].cmd );
|
||||
}
|
||||
|
||||
//
|
||||
// the game server will interpret these commands, which will be automatically
|
||||
// forwarded to the server after they are not recognized locally
|
||||
//
|
||||
trap_AddCommand ("kill");
|
||||
trap_AddCommand ("say");
|
||||
trap_AddCommand ("say_team");
|
||||
trap_AddCommand ("tell");
|
||||
trap_AddCommand ("vsay");
|
||||
trap_AddCommand ("vsay_team");
|
||||
trap_AddCommand ("vtell");
|
||||
trap_AddCommand ("vtaunt");
|
||||
trap_AddCommand ("vosay");
|
||||
trap_AddCommand ("vosay_team");
|
||||
trap_AddCommand ("votell");
|
||||
trap_AddCommand ("give");
|
||||
trap_AddCommand ("god");
|
||||
trap_AddCommand ("notarget");
|
||||
trap_AddCommand ("noclip");
|
||||
trap_AddCommand ("team");
|
||||
trap_AddCommand ("follow");
|
||||
trap_AddCommand ("levelshot");
|
||||
trap_AddCommand ("addbot");
|
||||
trap_AddCommand ("setviewpos");
|
||||
trap_AddCommand ("callvote");
|
||||
trap_AddCommand ("vote");
|
||||
trap_AddCommand ("callteamvote");
|
||||
trap_AddCommand ("teamvote");
|
||||
trap_AddCommand ("stats");
|
||||
trap_AddCommand ("teamtask");
|
||||
trap_AddCommand ("loaddefered"); // spelled wrong, but not changing for demo
|
||||
}
|
2659
code/cgame/cg_draw.c
Normal file
2659
code/cgame/cg_draw.c
Normal file
File diff suppressed because it is too large
Load diff
824
code/cgame/cg_drawtools.c
Normal file
824
code/cgame/cg_drawtools.c
Normal file
|
@ -0,0 +1,824 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
================
|
||||
CG_AdjustFrom640
|
||||
|
||||
Adjusted for resolution and screen aspect ratio
|
||||
================
|
||||
*/
|
||||
void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) {
|
||||
#if 0
|
||||
// adjust for wide screens
|
||||
if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) {
|
||||
*x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) );
|
||||
}
|
||||
#endif
|
||||
// scale for screen sizes
|
||||
*x *= cgs.screenXScale;
|
||||
*y *= cgs.screenYScale;
|
||||
*w *= cgs.screenXScale;
|
||||
*h *= cgs.screenYScale;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_FillRect
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void CG_FillRect( float x, float y, float width, float height, const float *color ) {
|
||||
trap_R_SetColor( color );
|
||||
|
||||
CG_AdjustFrom640( &x, &y, &width, &height );
|
||||
trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_DrawSides
|
||||
|
||||
Coords are virtual 640x480
|
||||
================
|
||||
*/
|
||||
void CG_DrawSides(float x, float y, float w, float h, float size) {
|
||||
CG_AdjustFrom640( &x, &y, &w, &h );
|
||||
size *= cgs.screenXScale;
|
||||
trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
}
|
||||
|
||||
void CG_DrawTopBottom(float x, float y, float w, float h, float size) {
|
||||
CG_AdjustFrom640( &x, &y, &w, &h );
|
||||
size *= cgs.screenYScale;
|
||||
trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
|
||||
}
|
||||
/*
|
||||
================
|
||||
UI_DrawRect
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) {
|
||||
trap_R_SetColor( color );
|
||||
|
||||
CG_DrawTopBottom(x, y, width, height, size);
|
||||
CG_DrawSides(x, y, width, height, size);
|
||||
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_DrawPic
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
|
||||
CG_AdjustFrom640( &x, &y, &width, &height );
|
||||
trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_DrawChar
|
||||
|
||||
Coordinates and size in 640*480 virtual screen size
|
||||
===============
|
||||
*/
|
||||
void CG_DrawChar( int x, int y, int width, int height, int ch ) {
|
||||
int row, col;
|
||||
float frow, fcol;
|
||||
float size;
|
||||
float ax, ay, aw, ah;
|
||||
|
||||
ch &= 255;
|
||||
|
||||
if ( ch == ' ' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ax = x;
|
||||
ay = y;
|
||||
aw = width;
|
||||
ah = height;
|
||||
CG_AdjustFrom640( &ax, &ay, &aw, &ah );
|
||||
|
||||
row = ch>>4;
|
||||
col = ch&15;
|
||||
|
||||
frow = row*0.0625;
|
||||
fcol = col*0.0625;
|
||||
size = 0.0625;
|
||||
|
||||
trap_R_DrawStretchPic( ax, ay, aw, ah,
|
||||
fcol, frow,
|
||||
fcol + size, frow + size,
|
||||
cgs.media.charsetShader );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_DrawStringExt
|
||||
|
||||
Draws a multi-colored string with a drop shadow, optionally forcing
|
||||
to a fixed color.
|
||||
|
||||
Coordinates are at 640 by 480 virtual resolution
|
||||
==================
|
||||
*/
|
||||
void CG_DrawStringExt( int x, int y, const char *string, const float *setColor,
|
||||
qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) {
|
||||
vec4_t color;
|
||||
const char *s;
|
||||
int xx;
|
||||
int cnt;
|
||||
|
||||
if (maxChars <= 0)
|
||||
maxChars = 32767; // do them all!
|
||||
|
||||
// draw the drop shadow
|
||||
if (shadow) {
|
||||
color[0] = color[1] = color[2] = 0;
|
||||
color[3] = setColor[3];
|
||||
trap_R_SetColor( color );
|
||||
s = string;
|
||||
xx = x;
|
||||
cnt = 0;
|
||||
while ( *s && cnt < maxChars) {
|
||||
if ( Q_IsColorString( s ) ) {
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
CG_DrawChar( xx + 2, y + 2, charWidth, charHeight, *s );
|
||||
cnt++;
|
||||
xx += charWidth;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
// draw the colored text
|
||||
s = string;
|
||||
xx = x;
|
||||
cnt = 0;
|
||||
trap_R_SetColor( setColor );
|
||||
while ( *s && cnt < maxChars) {
|
||||
if ( Q_IsColorString( s ) ) {
|
||||
if ( !forceColor ) {
|
||||
memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
|
||||
color[3] = setColor[3];
|
||||
trap_R_SetColor( color );
|
||||
}
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
CG_DrawChar( xx, y, charWidth, charHeight, *s );
|
||||
xx += charWidth;
|
||||
cnt++;
|
||||
s++;
|
||||
}
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
void CG_DrawBigString( int x, int y, const char *s, float alpha ) {
|
||||
float color[4];
|
||||
|
||||
color[0] = color[1] = color[2] = 1.0;
|
||||
color[3] = alpha;
|
||||
CG_DrawStringExt( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 );
|
||||
}
|
||||
|
||||
void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) {
|
||||
CG_DrawStringExt( x, y, s, color, qtrue, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 );
|
||||
}
|
||||
|
||||
void CG_DrawSmallString( int x, int y, const char *s, float alpha ) {
|
||||
float color[4];
|
||||
|
||||
color[0] = color[1] = color[2] = 1.0;
|
||||
color[3] = alpha;
|
||||
CG_DrawStringExt( x, y, s, color, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 );
|
||||
}
|
||||
|
||||
void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) {
|
||||
CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_DrawStrlen
|
||||
|
||||
Returns character count, skiping color escape codes
|
||||
=================
|
||||
*/
|
||||
int CG_DrawStrlen( const char *str ) {
|
||||
const char *s = str;
|
||||
int count = 0;
|
||||
|
||||
while ( *s ) {
|
||||
if ( Q_IsColorString( s ) ) {
|
||||
s += 2;
|
||||
} else {
|
||||
count++;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
CG_TileClearBox
|
||||
|
||||
This repeats a 64*64 tile graphic to fill the screen around a sized down
|
||||
refresh window.
|
||||
=============
|
||||
*/
|
||||
static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) {
|
||||
float s1, t1, s2, t2;
|
||||
|
||||
s1 = x/64.0;
|
||||
t1 = y/64.0;
|
||||
s2 = (x+w)/64.0;
|
||||
t2 = (y+h)/64.0;
|
||||
trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
CG_TileClear
|
||||
|
||||
Clear around a sized down screen
|
||||
==============
|
||||
*/
|
||||
void CG_TileClear( void ) {
|
||||
int top, bottom, left, right;
|
||||
int w, h;
|
||||
|
||||
w = cgs.glconfig.vidWidth;
|
||||
h = cgs.glconfig.vidHeight;
|
||||
|
||||
if ( cg.refdef.x == 0 && cg.refdef.y == 0 &&
|
||||
cg.refdef.width == w && cg.refdef.height == h ) {
|
||||
return; // full screen rendering
|
||||
}
|
||||
|
||||
top = cg.refdef.y;
|
||||
bottom = top + cg.refdef.height-1;
|
||||
left = cg.refdef.x;
|
||||
right = left + cg.refdef.width-1;
|
||||
|
||||
// clear above view screen
|
||||
CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader );
|
||||
|
||||
// clear below view screen
|
||||
CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader );
|
||||
|
||||
// clear left of view screen
|
||||
CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader );
|
||||
|
||||
// clear right of view screen
|
||||
CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_FadeColor
|
||||
================
|
||||
*/
|
||||
float *CG_FadeColor( int startMsec, int totalMsec ) {
|
||||
static vec4_t color;
|
||||
int t;
|
||||
|
||||
if ( startMsec == 0 ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t = cg.time - startMsec;
|
||||
|
||||
if ( t >= totalMsec ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// fade out
|
||||
if ( totalMsec - t < FADE_TIME ) {
|
||||
color[3] = ( totalMsec - t ) * 1.0/FADE_TIME;
|
||||
} else {
|
||||
color[3] = 1.0;
|
||||
}
|
||||
color[0] = color[1] = color[2] = 1;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_TeamColor
|
||||
================
|
||||
*/
|
||||
float *CG_TeamColor( int team ) {
|
||||
static vec4_t red = {1, 0.2f, 0.2f, 1};
|
||||
static vec4_t blue = {0.2f, 0.2f, 1, 1};
|
||||
static vec4_t other = {1, 1, 1, 1};
|
||||
static vec4_t spectator = {0.7f, 0.7f, 0.7f, 1};
|
||||
|
||||
switch ( team ) {
|
||||
case TEAM_RED:
|
||||
return red;
|
||||
case TEAM_BLUE:
|
||||
return blue;
|
||||
case TEAM_SPECTATOR:
|
||||
return spectator;
|
||||
default:
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_GetColorForHealth
|
||||
=================
|
||||
*/
|
||||
void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ) {
|
||||
int count;
|
||||
int max;
|
||||
|
||||
// calculate the total points of damage that can
|
||||
// be sustained at the current health / armor level
|
||||
if ( health <= 0 ) {
|
||||
VectorClear( hcolor ); // black
|
||||
hcolor[3] = 1;
|
||||
return;
|
||||
}
|
||||
count = armor;
|
||||
max = health * ARMOR_PROTECTION / ( 1.0 - ARMOR_PROTECTION );
|
||||
if ( max < count ) {
|
||||
count = max;
|
||||
}
|
||||
health += count;
|
||||
|
||||
// set the color based on health
|
||||
hcolor[0] = 1.0;
|
||||
hcolor[3] = 1.0;
|
||||
if ( health >= 100 ) {
|
||||
hcolor[2] = 1.0;
|
||||
} else if ( health < 66 ) {
|
||||
hcolor[2] = 0;
|
||||
} else {
|
||||
hcolor[2] = ( health - 66 ) / 33.0;
|
||||
}
|
||||
|
||||
if ( health > 60 ) {
|
||||
hcolor[1] = 1.0;
|
||||
} else if ( health < 30 ) {
|
||||
hcolor[1] = 0;
|
||||
} else {
|
||||
hcolor[1] = ( health - 30 ) / 30.0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_ColorForHealth
|
||||
=================
|
||||
*/
|
||||
void CG_ColorForHealth( vec4_t hcolor ) {
|
||||
|
||||
CG_GetColorForHealth( cg.snap->ps.stats[STAT_HEALTH],
|
||||
cg.snap->ps.stats[STAT_ARMOR], hcolor );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// bk001205 - code below duplicated in q3_ui/ui-atoms.c
|
||||
// bk001205 - FIXME: does this belong in ui_shared.c?
|
||||
// bk001205 - FIXME: HARD_LINKED flags not visible here
|
||||
#ifndef Q3_STATIC // bk001205 - q_shared defines not visible here
|
||||
/*
|
||||
=================
|
||||
UI_DrawProportionalString2
|
||||
=================
|
||||
*/
|
||||
static int propMap[128][3] = {
|
||||
{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
|
||||
{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
|
||||
|
||||
{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
|
||||
{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
|
||||
|
||||
{0, 0, PROP_SPACE_WIDTH}, // SPACE
|
||||
{11, 122, 7}, // !
|
||||
{154, 181, 14}, // "
|
||||
{55, 122, 17}, // #
|
||||
{79, 122, 18}, // $
|
||||
{101, 122, 23}, // %
|
||||
{153, 122, 18}, // &
|
||||
{9, 93, 7}, // '
|
||||
{207, 122, 8}, // (
|
||||
{230, 122, 9}, // )
|
||||
{177, 122, 18}, // *
|
||||
{30, 152, 18}, // +
|
||||
{85, 181, 7}, // ,
|
||||
{34, 93, 11}, // -
|
||||
{110, 181, 6}, // .
|
||||
{130, 152, 14}, // /
|
||||
|
||||
{22, 64, 17}, // 0
|
||||
{41, 64, 12}, // 1
|
||||
{58, 64, 17}, // 2
|
||||
{78, 64, 18}, // 3
|
||||
{98, 64, 19}, // 4
|
||||
{120, 64, 18}, // 5
|
||||
{141, 64, 18}, // 6
|
||||
{204, 64, 16}, // 7
|
||||
{162, 64, 17}, // 8
|
||||
{182, 64, 18}, // 9
|
||||
{59, 181, 7}, // :
|
||||
{35,181, 7}, // ;
|
||||
{203, 152, 14}, // <
|
||||
{56, 93, 14}, // =
|
||||
{228, 152, 14}, // >
|
||||
{177, 181, 18}, // ?
|
||||
|
||||
{28, 122, 22}, // @
|
||||
{5, 4, 18}, // A
|
||||
{27, 4, 18}, // B
|
||||
{48, 4, 18}, // C
|
||||
{69, 4, 17}, // D
|
||||
{90, 4, 13}, // E
|
||||
{106, 4, 13}, // F
|
||||
{121, 4, 18}, // G
|
||||
{143, 4, 17}, // H
|
||||
{164, 4, 8}, // I
|
||||
{175, 4, 16}, // J
|
||||
{195, 4, 18}, // K
|
||||
{216, 4, 12}, // L
|
||||
{230, 4, 23}, // M
|
||||
{6, 34, 18}, // N
|
||||
{27, 34, 18}, // O
|
||||
|
||||
{48, 34, 18}, // P
|
||||
{68, 34, 18}, // Q
|
||||
{90, 34, 17}, // R
|
||||
{110, 34, 18}, // S
|
||||
{130, 34, 14}, // T
|
||||
{146, 34, 18}, // U
|
||||
{166, 34, 19}, // V
|
||||
{185, 34, 29}, // W
|
||||
{215, 34, 18}, // X
|
||||
{234, 34, 18}, // Y
|
||||
{5, 64, 14}, // Z
|
||||
{60, 152, 7}, // [
|
||||
{106, 151, 13}, // '\'
|
||||
{83, 152, 7}, // ]
|
||||
{128, 122, 17}, // ^
|
||||
{4, 152, 21}, // _
|
||||
|
||||
{134, 181, 5}, // '
|
||||
{5, 4, 18}, // A
|
||||
{27, 4, 18}, // B
|
||||
{48, 4, 18}, // C
|
||||
{69, 4, 17}, // D
|
||||
{90, 4, 13}, // E
|
||||
{106, 4, 13}, // F
|
||||
{121, 4, 18}, // G
|
||||
{143, 4, 17}, // H
|
||||
{164, 4, 8}, // I
|
||||
{175, 4, 16}, // J
|
||||
{195, 4, 18}, // K
|
||||
{216, 4, 12}, // L
|
||||
{230, 4, 23}, // M
|
||||
{6, 34, 18}, // N
|
||||
{27, 34, 18}, // O
|
||||
|
||||
{48, 34, 18}, // P
|
||||
{68, 34, 18}, // Q
|
||||
{90, 34, 17}, // R
|
||||
{110, 34, 18}, // S
|
||||
{130, 34, 14}, // T
|
||||
{146, 34, 18}, // U
|
||||
{166, 34, 19}, // V
|
||||
{185, 34, 29}, // W
|
||||
{215, 34, 18}, // X
|
||||
{234, 34, 18}, // Y
|
||||
{5, 64, 14}, // Z
|
||||
{153, 152, 13}, // {
|
||||
{11, 181, 5}, // |
|
||||
{180, 152, 13}, // }
|
||||
{79, 93, 17}, // ~
|
||||
{0, 0, -1} // DEL
|
||||
};
|
||||
|
||||
static int propMapB[26][3] = {
|
||||
{11, 12, 33},
|
||||
{49, 12, 31},
|
||||
{85, 12, 31},
|
||||
{120, 12, 30},
|
||||
{156, 12, 21},
|
||||
{183, 12, 21},
|
||||
{207, 12, 32},
|
||||
|
||||
{13, 55, 30},
|
||||
{49, 55, 13},
|
||||
{66, 55, 29},
|
||||
{101, 55, 31},
|
||||
{135, 55, 21},
|
||||
{158, 55, 40},
|
||||
{204, 55, 32},
|
||||
|
||||
{12, 97, 31},
|
||||
{48, 97, 31},
|
||||
{82, 97, 30},
|
||||
{118, 97, 30},
|
||||
{153, 97, 30},
|
||||
{185, 97, 25},
|
||||
{213, 97, 30},
|
||||
|
||||
{11, 139, 32},
|
||||
{42, 139, 51},
|
||||
{93, 139, 32},
|
||||
{126, 139, 31},
|
||||
{158, 139, 25},
|
||||
};
|
||||
|
||||
#define PROPB_GAP_WIDTH 4
|
||||
#define PROPB_SPACE_WIDTH 12
|
||||
#define PROPB_HEIGHT 36
|
||||
|
||||
/*
|
||||
=================
|
||||
UI_DrawBannerString
|
||||
=================
|
||||
*/
|
||||
static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color )
|
||||
{
|
||||
const char* s;
|
||||
unsigned char ch; // bk001204 : array subscript
|
||||
float ax;
|
||||
float ay;
|
||||
float aw;
|
||||
float ah;
|
||||
float frow;
|
||||
float fcol;
|
||||
float fwidth;
|
||||
float fheight;
|
||||
|
||||
// draw the colored text
|
||||
trap_R_SetColor( color );
|
||||
|
||||
ax = x * cgs.screenXScale + cgs.screenXBias;
|
||||
ay = y * cgs.screenXScale;
|
||||
|
||||
s = str;
|
||||
while ( *s )
|
||||
{
|
||||
ch = *s & 127;
|
||||
if ( ch == ' ' ) {
|
||||
ax += ((float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH)* cgs.screenXScale;
|
||||
}
|
||||
else if ( ch >= 'A' && ch <= 'Z' ) {
|
||||
ch -= 'A';
|
||||
fcol = (float)propMapB[ch][0] / 256.0f;
|
||||
frow = (float)propMapB[ch][1] / 256.0f;
|
||||
fwidth = (float)propMapB[ch][2] / 256.0f;
|
||||
fheight = (float)PROPB_HEIGHT / 256.0f;
|
||||
aw = (float)propMapB[ch][2] * cgs.screenXScale;
|
||||
ah = (float)PROPB_HEIGHT * cgs.screenXScale;
|
||||
trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, cgs.media.charsetPropB );
|
||||
ax += (aw + (float)PROPB_GAP_WIDTH * cgs.screenXScale);
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) {
|
||||
const char * s;
|
||||
int ch;
|
||||
int width;
|
||||
vec4_t drawcolor;
|
||||
|
||||
// find the width of the drawn text
|
||||
s = str;
|
||||
width = 0;
|
||||
while ( *s ) {
|
||||
ch = *s;
|
||||
if ( ch == ' ' ) {
|
||||
width += PROPB_SPACE_WIDTH;
|
||||
}
|
||||
else if ( ch >= 'A' && ch <= 'Z' ) {
|
||||
width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
width -= PROPB_GAP_WIDTH;
|
||||
|
||||
switch( style & UI_FORMATMASK ) {
|
||||
case UI_CENTER:
|
||||
x -= width / 2;
|
||||
break;
|
||||
|
||||
case UI_RIGHT:
|
||||
x -= width;
|
||||
break;
|
||||
|
||||
case UI_LEFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( style & UI_DROPSHADOW ) {
|
||||
drawcolor[0] = drawcolor[1] = drawcolor[2] = 0;
|
||||
drawcolor[3] = color[3];
|
||||
UI_DrawBannerString2( x+2, y+2, str, drawcolor );
|
||||
}
|
||||
|
||||
UI_DrawBannerString2( x, y, str, color );
|
||||
}
|
||||
|
||||
|
||||
int UI_ProportionalStringWidth( const char* str ) {
|
||||
const char * s;
|
||||
int ch;
|
||||
int charWidth;
|
||||
int width;
|
||||
|
||||
s = str;
|
||||
width = 0;
|
||||
while ( *s ) {
|
||||
ch = *s & 127;
|
||||
charWidth = propMap[ch][2];
|
||||
if ( charWidth != -1 ) {
|
||||
width += charWidth;
|
||||
width += PROP_GAP_WIDTH;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
width -= PROP_GAP_WIDTH;
|
||||
return width;
|
||||
}
|
||||
|
||||
static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset )
|
||||
{
|
||||
const char* s;
|
||||
unsigned char ch; // bk001204 - unsigned
|
||||
float ax;
|
||||
float ay;
|
||||
float aw;
|
||||
float ah;
|
||||
float frow;
|
||||
float fcol;
|
||||
float fwidth;
|
||||
float fheight;
|
||||
|
||||
// draw the colored text
|
||||
trap_R_SetColor( color );
|
||||
|
||||
ax = x * cgs.screenXScale + cgs.screenXBias;
|
||||
ay = y * cgs.screenXScale;
|
||||
|
||||
s = str;
|
||||
while ( *s )
|
||||
{
|
||||
ch = *s & 127;
|
||||
if ( ch == ' ' ) {
|
||||
aw = (float)PROP_SPACE_WIDTH * cgs.screenXScale * sizeScale;
|
||||
} else if ( propMap[ch][2] != -1 ) {
|
||||
fcol = (float)propMap[ch][0] / 256.0f;
|
||||
frow = (float)propMap[ch][1] / 256.0f;
|
||||
fwidth = (float)propMap[ch][2] / 256.0f;
|
||||
fheight = (float)PROP_HEIGHT / 256.0f;
|
||||
aw = (float)propMap[ch][2] * cgs.screenXScale * sizeScale;
|
||||
ah = (float)PROP_HEIGHT * cgs.screenXScale * sizeScale;
|
||||
trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, charset );
|
||||
} else {
|
||||
aw = 0;
|
||||
}
|
||||
|
||||
ax += (aw + (float)PROP_GAP_WIDTH * cgs.screenXScale * sizeScale);
|
||||
s++;
|
||||
}
|
||||
|
||||
trap_R_SetColor( NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
UI_ProportionalSizeScale
|
||||
=================
|
||||
*/
|
||||
float UI_ProportionalSizeScale( int style ) {
|
||||
if( style & UI_SMALLFONT ) {
|
||||
return 0.75;
|
||||
}
|
||||
|
||||
return 1.00;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
UI_DrawProportionalString
|
||||
=================
|
||||
*/
|
||||
void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) {
|
||||
vec4_t drawcolor;
|
||||
int width;
|
||||
float sizeScale;
|
||||
|
||||
sizeScale = UI_ProportionalSizeScale( style );
|
||||
|
||||
switch( style & UI_FORMATMASK ) {
|
||||
case UI_CENTER:
|
||||
width = UI_ProportionalStringWidth( str ) * sizeScale;
|
||||
x -= width / 2;
|
||||
break;
|
||||
|
||||
case UI_RIGHT:
|
||||
width = UI_ProportionalStringWidth( str ) * sizeScale;
|
||||
x -= width;
|
||||
break;
|
||||
|
||||
case UI_LEFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( style & UI_DROPSHADOW ) {
|
||||
drawcolor[0] = drawcolor[1] = drawcolor[2] = 0;
|
||||
drawcolor[3] = color[3];
|
||||
UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, cgs.media.charsetProp );
|
||||
}
|
||||
|
||||
if ( style & UI_INVERSE ) {
|
||||
drawcolor[0] = color[0] * 0.8;
|
||||
drawcolor[1] = color[1] * 0.8;
|
||||
drawcolor[2] = color[2] * 0.8;
|
||||
drawcolor[3] = color[3];
|
||||
UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetProp );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( style & UI_PULSE ) {
|
||||
drawcolor[0] = color[0] * 0.8;
|
||||
drawcolor[1] = color[1] * 0.8;
|
||||
drawcolor[2] = color[2] * 0.8;
|
||||
drawcolor[3] = color[3];
|
||||
UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp );
|
||||
|
||||
drawcolor[0] = color[0];
|
||||
drawcolor[1] = color[1];
|
||||
drawcolor[2] = color[2];
|
||||
drawcolor[3] = 0.5 + 0.5 * sin( cg.time / PULSE_DIVISOR );
|
||||
UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetPropGlow );
|
||||
return;
|
||||
}
|
||||
|
||||
UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp );
|
||||
}
|
||||
#endif // Q3STATIC
|
718
code/cgame/cg_effects.c
Normal file
718
code/cgame/cg_effects.c
Normal file
|
@ -0,0 +1,718 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_effects.c -- these functions generate localentities, usually as a result
|
||||
// of event processing
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_BubbleTrail
|
||||
|
||||
Bullets shot underwater
|
||||
==================
|
||||
*/
|
||||
void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ) {
|
||||
vec3_t move;
|
||||
vec3_t vec;
|
||||
float len;
|
||||
int i;
|
||||
|
||||
if ( cg_noProjectileTrail.integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy (start, move);
|
||||
VectorSubtract (end, start, vec);
|
||||
len = VectorNormalize (vec);
|
||||
|
||||
// advance a random amount first
|
||||
i = rand() % (int)spacing;
|
||||
VectorMA( move, i, vec, move );
|
||||
|
||||
VectorScale (vec, spacing, vec);
|
||||
|
||||
for ( ; i < len; i += spacing ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = LEF_PUFF_DONT_SCALE;
|
||||
le->leType = LE_MOVE_SCALE_FADE;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = cg.time + 1000 + random() * 250;
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
||||
|
||||
re = &le->refEntity;
|
||||
re->shaderTime = cg.time / 1000.0f;
|
||||
|
||||
re->reType = RT_SPRITE;
|
||||
re->rotation = 0;
|
||||
re->radius = 3;
|
||||
re->customShader = cgs.media.waterBubbleShader;
|
||||
re->shaderRGBA[0] = 0xff;
|
||||
re->shaderRGBA[1] = 0xff;
|
||||
re->shaderRGBA[2] = 0xff;
|
||||
re->shaderRGBA[3] = 0xff;
|
||||
|
||||
le->color[3] = 1.0;
|
||||
|
||||
le->pos.trType = TR_LINEAR;
|
||||
le->pos.trTime = cg.time;
|
||||
VectorCopy( move, le->pos.trBase );
|
||||
le->pos.trDelta[0] = crandom()*5;
|
||||
le->pos.trDelta[1] = crandom()*5;
|
||||
le->pos.trDelta[2] = crandom()*5 + 6;
|
||||
|
||||
VectorAdd (move, vec, move);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CG_SmokePuff
|
||||
|
||||
Adds a smoke puff or blood trail localEntity.
|
||||
=====================
|
||||
*/
|
||||
localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel,
|
||||
float radius,
|
||||
float r, float g, float b, float a,
|
||||
float duration,
|
||||
int startTime,
|
||||
int fadeInTime,
|
||||
int leFlags,
|
||||
qhandle_t hShader ) {
|
||||
static int seed = 0x92;
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
// int fadeInTime = startTime + duration / 2;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = leFlags;
|
||||
le->radius = radius;
|
||||
|
||||
re = &le->refEntity;
|
||||
re->rotation = Q_random( &seed ) * 360;
|
||||
re->radius = radius;
|
||||
re->shaderTime = startTime / 1000.0f;
|
||||
|
||||
le->leType = LE_MOVE_SCALE_FADE;
|
||||
le->startTime = startTime;
|
||||
le->fadeInTime = fadeInTime;
|
||||
le->endTime = startTime + duration;
|
||||
if ( fadeInTime > startTime ) {
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime );
|
||||
}
|
||||
else {
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
||||
}
|
||||
le->color[0] = r;
|
||||
le->color[1] = g;
|
||||
le->color[2] = b;
|
||||
le->color[3] = a;
|
||||
|
||||
|
||||
le->pos.trType = TR_LINEAR;
|
||||
le->pos.trTime = startTime;
|
||||
VectorCopy( vel, le->pos.trDelta );
|
||||
VectorCopy( p, le->pos.trBase );
|
||||
|
||||
VectorCopy( p, re->origin );
|
||||
re->customShader = hShader;
|
||||
|
||||
// rage pro can't alpha fade, so use a different shader
|
||||
if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) {
|
||||
re->customShader = cgs.media.smokePuffRageProShader;
|
||||
re->shaderRGBA[0] = 0xff;
|
||||
re->shaderRGBA[1] = 0xff;
|
||||
re->shaderRGBA[2] = 0xff;
|
||||
re->shaderRGBA[3] = 0xff;
|
||||
} else {
|
||||
re->shaderRGBA[0] = le->color[0] * 0xff;
|
||||
re->shaderRGBA[1] = le->color[1] * 0xff;
|
||||
re->shaderRGBA[2] = le->color[2] * 0xff;
|
||||
re->shaderRGBA[3] = 0xff;
|
||||
}
|
||||
|
||||
re->reType = RT_SPRITE;
|
||||
re->radius = le->radius;
|
||||
|
||||
return le;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_SpawnEffect
|
||||
|
||||
Player teleporting in or out
|
||||
==================
|
||||
*/
|
||||
void CG_SpawnEffect( vec3_t org ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = 0;
|
||||
le->leType = LE_FADE_RGB;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = cg.time + 500;
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
||||
|
||||
le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
re->reType = RT_MODEL;
|
||||
re->shaderTime = cg.time / 1000.0f;
|
||||
|
||||
#ifndef MISSIONPACK
|
||||
re->customShader = cgs.media.teleportEffectShader;
|
||||
#endif
|
||||
re->hModel = cgs.media.teleportEffectModel;
|
||||
AxisClear( re->axis );
|
||||
|
||||
VectorCopy( org, re->origin );
|
||||
#ifdef MISSIONPACK
|
||||
re->origin[2] += 16;
|
||||
#else
|
||||
re->origin[2] -= 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef MISSIONPACK
|
||||
/*
|
||||
===============
|
||||
CG_LightningBoltBeam
|
||||
===============
|
||||
*/
|
||||
void CG_LightningBoltBeam( vec3_t start, vec3_t end ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *beam;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = 0;
|
||||
le->leType = LE_SHOWREFENTITY;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = cg.time + 50;
|
||||
|
||||
beam = &le->refEntity;
|
||||
|
||||
VectorCopy( start, beam->origin );
|
||||
// this is the end point
|
||||
VectorCopy( end, beam->oldorigin );
|
||||
|
||||
beam->reType = RT_LIGHTNING;
|
||||
beam->customShader = cgs.media.lightningShader;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_KamikazeEffect
|
||||
==================
|
||||
*/
|
||||
void CG_KamikazeEffect( vec3_t org ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = 0;
|
||||
le->leType = LE_KAMIKAZE;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = cg.time + 3000;//2250;
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
||||
|
||||
le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0;
|
||||
|
||||
VectorClear(le->angles.trBase);
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
re->reType = RT_MODEL;
|
||||
re->shaderTime = cg.time / 1000.0f;
|
||||
|
||||
re->hModel = cgs.media.kamikazeEffectModel;
|
||||
|
||||
VectorCopy( org, re->origin );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_ObeliskExplode
|
||||
==================
|
||||
*/
|
||||
void CG_ObeliskExplode( vec3_t org, int entityNum ) {
|
||||
localEntity_t *le;
|
||||
vec3_t origin;
|
||||
|
||||
// create an explosion
|
||||
VectorCopy( org, origin );
|
||||
origin[2] += 64;
|
||||
le = CG_MakeExplosion( origin, vec3_origin,
|
||||
cgs.media.dishFlashModel,
|
||||
cgs.media.rocketExplosionShader,
|
||||
600, qtrue );
|
||||
le->light = 300;
|
||||
le->lightColor[0] = 1;
|
||||
le->lightColor[1] = 0.75;
|
||||
le->lightColor[2] = 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_ObeliskPain
|
||||
==================
|
||||
*/
|
||||
void CG_ObeliskPain( vec3_t org ) {
|
||||
float r;
|
||||
sfxHandle_t sfx;
|
||||
|
||||
// hit sound
|
||||
r = rand() & 3;
|
||||
if ( r < 2 ) {
|
||||
sfx = cgs.media.obeliskHitSound1;
|
||||
} else if ( r == 2 ) {
|
||||
sfx = cgs.media.obeliskHitSound2;
|
||||
} else {
|
||||
sfx = cgs.media.obeliskHitSound3;
|
||||
}
|
||||
trap_S_StartSound ( org, ENTITYNUM_NONE, CHAN_BODY, sfx );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_InvulnerabilityImpact
|
||||
==================
|
||||
*/
|
||||
void CG_InvulnerabilityImpact( vec3_t org, vec3_t angles ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
int r;
|
||||
sfxHandle_t sfx;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = 0;
|
||||
le->leType = LE_INVULIMPACT;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = cg.time + 1000;
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
||||
|
||||
le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
re->reType = RT_MODEL;
|
||||
re->shaderTime = cg.time / 1000.0f;
|
||||
|
||||
re->hModel = cgs.media.invulnerabilityImpactModel;
|
||||
|
||||
VectorCopy( org, re->origin );
|
||||
AnglesToAxis( angles, re->axis );
|
||||
|
||||
r = rand() & 3;
|
||||
if ( r < 2 ) {
|
||||
sfx = cgs.media.invulnerabilityImpactSound1;
|
||||
} else if ( r == 2 ) {
|
||||
sfx = cgs.media.invulnerabilityImpactSound2;
|
||||
} else {
|
||||
sfx = cgs.media.invulnerabilityImpactSound3;
|
||||
}
|
||||
trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, sfx );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_InvulnerabilityJuiced
|
||||
==================
|
||||
*/
|
||||
void CG_InvulnerabilityJuiced( vec3_t org ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
vec3_t angles;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = 0;
|
||||
le->leType = LE_INVULJUICED;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = cg.time + 10000;
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
||||
|
||||
le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
re->reType = RT_MODEL;
|
||||
re->shaderTime = cg.time / 1000.0f;
|
||||
|
||||
re->hModel = cgs.media.invulnerabilityJuicedModel;
|
||||
|
||||
VectorCopy( org, re->origin );
|
||||
VectorClear(angles);
|
||||
AnglesToAxis( angles, re->axis );
|
||||
|
||||
trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, cgs.media.invulnerabilityJuicedSound );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_ScorePlum
|
||||
==================
|
||||
*/
|
||||
void CG_ScorePlum( int client, vec3_t org, int score ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
vec3_t angles;
|
||||
static vec3_t lastPos;
|
||||
|
||||
// only visualize for the client that scored
|
||||
if (client != cg.predictedPlayerState.clientNum || cg_scorePlum.integer == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
le->leFlags = 0;
|
||||
le->leType = LE_SCOREPLUM;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = cg.time + 4000;
|
||||
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
||||
|
||||
|
||||
le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0;
|
||||
le->radius = score;
|
||||
|
||||
VectorCopy( org, le->pos.trBase );
|
||||
if (org[2] >= lastPos[2] - 20 && org[2] <= lastPos[2] + 20) {
|
||||
le->pos.trBase[2] -= 20;
|
||||
}
|
||||
|
||||
//CG_Printf( "Plum origin %i %i %i -- %i\n", (int)org[0], (int)org[1], (int)org[2], (int)Distance(org, lastPos));
|
||||
VectorCopy(org, lastPos);
|
||||
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
re->reType = RT_SPRITE;
|
||||
re->radius = 16;
|
||||
|
||||
VectorClear(angles);
|
||||
AnglesToAxis( angles, re->axis );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_MakeExplosion
|
||||
====================
|
||||
*/
|
||||
localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir,
|
||||
qhandle_t hModel, qhandle_t shader,
|
||||
int msec, qboolean isSprite ) {
|
||||
float ang;
|
||||
localEntity_t *ex;
|
||||
int offset;
|
||||
vec3_t tmpVec, newOrigin;
|
||||
|
||||
if ( msec <= 0 ) {
|
||||
CG_Error( "CG_MakeExplosion: msec = %i", msec );
|
||||
}
|
||||
|
||||
// skew the time a bit so they aren't all in sync
|
||||
offset = rand() & 63;
|
||||
|
||||
ex = CG_AllocLocalEntity();
|
||||
if ( isSprite ) {
|
||||
ex->leType = LE_SPRITE_EXPLOSION;
|
||||
|
||||
// randomly rotate sprite orientation
|
||||
ex->refEntity.rotation = rand() % 360;
|
||||
VectorScale( dir, 16, tmpVec );
|
||||
VectorAdd( tmpVec, origin, newOrigin );
|
||||
} else {
|
||||
ex->leType = LE_EXPLOSION;
|
||||
VectorCopy( origin, newOrigin );
|
||||
|
||||
// set axis with random rotate
|
||||
if ( !dir ) {
|
||||
AxisClear( ex->refEntity.axis );
|
||||
} else {
|
||||
ang = rand() % 360;
|
||||
VectorCopy( dir, ex->refEntity.axis[0] );
|
||||
RotateAroundDirection( ex->refEntity.axis, ang );
|
||||
}
|
||||
}
|
||||
|
||||
ex->startTime = cg.time - offset;
|
||||
ex->endTime = ex->startTime + msec;
|
||||
|
||||
// bias the time so all shader effects start correctly
|
||||
ex->refEntity.shaderTime = ex->startTime / 1000.0f;
|
||||
|
||||
ex->refEntity.hModel = hModel;
|
||||
ex->refEntity.customShader = shader;
|
||||
|
||||
// set origin
|
||||
VectorCopy( newOrigin, ex->refEntity.origin );
|
||||
VectorCopy( newOrigin, ex->refEntity.oldorigin );
|
||||
|
||||
ex->color[0] = ex->color[1] = ex->color[2] = 1.0;
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_Bleed
|
||||
|
||||
This is the spurt of blood when a character gets hit
|
||||
=================
|
||||
*/
|
||||
void CG_Bleed( vec3_t origin, int entityNum ) {
|
||||
localEntity_t *ex;
|
||||
|
||||
if ( !cg_blood.integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ex = CG_AllocLocalEntity();
|
||||
ex->leType = LE_EXPLOSION;
|
||||
|
||||
ex->startTime = cg.time;
|
||||
ex->endTime = ex->startTime + 500;
|
||||
|
||||
VectorCopy ( origin, ex->refEntity.origin);
|
||||
ex->refEntity.reType = RT_SPRITE;
|
||||
ex->refEntity.rotation = rand() % 360;
|
||||
ex->refEntity.radius = 24;
|
||||
|
||||
ex->refEntity.customShader = cgs.media.bloodExplosionShader;
|
||||
|
||||
// don't show player's own blood in view
|
||||
if ( entityNum == cg.snap->ps.clientNum ) {
|
||||
ex->refEntity.renderfx |= RF_THIRD_PERSON;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_LaunchGib
|
||||
==================
|
||||
*/
|
||||
void CG_LaunchGib( vec3_t origin, vec3_t velocity, qhandle_t hModel ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
re = &le->refEntity;
|
||||
|
||||
le->leType = LE_FRAGMENT;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = le->startTime + 5000 + random() * 3000;
|
||||
|
||||
VectorCopy( origin, re->origin );
|
||||
AxisCopy( axisDefault, re->axis );
|
||||
re->hModel = hModel;
|
||||
|
||||
le->pos.trType = TR_GRAVITY;
|
||||
VectorCopy( origin, le->pos.trBase );
|
||||
VectorCopy( velocity, le->pos.trDelta );
|
||||
le->pos.trTime = cg.time;
|
||||
|
||||
le->bounceFactor = 0.6f;
|
||||
|
||||
le->leBounceSoundType = LEBS_BLOOD;
|
||||
le->leMarkType = LEMT_BLOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_GibPlayer
|
||||
|
||||
Generated a bunch of gibs launching out from the bodies location
|
||||
===================
|
||||
*/
|
||||
#define GIB_VELOCITY 250
|
||||
#define GIB_JUMP 250
|
||||
void CG_GibPlayer( vec3_t playerOrigin ) {
|
||||
vec3_t origin, velocity;
|
||||
|
||||
if ( !cg_blood.integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
if ( rand() & 1 ) {
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibSkull );
|
||||
} else {
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibBrain );
|
||||
}
|
||||
|
||||
// allow gibs to be turned off for speed
|
||||
if ( !cg_gibs.integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibAbdomen );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibArm );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibChest );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibFist );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibFoot );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibForearm );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibIntestine );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibLeg );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*GIB_VELOCITY;
|
||||
velocity[1] = crandom()*GIB_VELOCITY;
|
||||
velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY;
|
||||
CG_LaunchGib( origin, velocity, cgs.media.gibLeg );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_LaunchGib
|
||||
==================
|
||||
*/
|
||||
void CG_LaunchExplode( vec3_t origin, vec3_t velocity, qhandle_t hModel ) {
|
||||
localEntity_t *le;
|
||||
refEntity_t *re;
|
||||
|
||||
le = CG_AllocLocalEntity();
|
||||
re = &le->refEntity;
|
||||
|
||||
le->leType = LE_FRAGMENT;
|
||||
le->startTime = cg.time;
|
||||
le->endTime = le->startTime + 10000 + random() * 6000;
|
||||
|
||||
VectorCopy( origin, re->origin );
|
||||
AxisCopy( axisDefault, re->axis );
|
||||
re->hModel = hModel;
|
||||
|
||||
le->pos.trType = TR_GRAVITY;
|
||||
VectorCopy( origin, le->pos.trBase );
|
||||
VectorCopy( velocity, le->pos.trDelta );
|
||||
le->pos.trTime = cg.time;
|
||||
|
||||
le->bounceFactor = 0.1f;
|
||||
|
||||
le->leBounceSoundType = LEBS_BRASS;
|
||||
le->leMarkType = LEMT_NONE;
|
||||
}
|
||||
|
||||
#define EXP_VELOCITY 100
|
||||
#define EXP_JUMP 150
|
||||
/*
|
||||
===================
|
||||
CG_GibPlayer
|
||||
|
||||
Generated a bunch of gibs launching out from the bodies location
|
||||
===================
|
||||
*/
|
||||
void CG_BigExplode( vec3_t playerOrigin ) {
|
||||
vec3_t origin, velocity;
|
||||
|
||||
if ( !cg_blood.integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*EXP_VELOCITY;
|
||||
velocity[1] = crandom()*EXP_VELOCITY;
|
||||
velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY;
|
||||
CG_LaunchExplode( origin, velocity, cgs.media.smoke2 );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*EXP_VELOCITY;
|
||||
velocity[1] = crandom()*EXP_VELOCITY;
|
||||
velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY;
|
||||
CG_LaunchExplode( origin, velocity, cgs.media.smoke2 );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*EXP_VELOCITY*1.5;
|
||||
velocity[1] = crandom()*EXP_VELOCITY*1.5;
|
||||
velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY;
|
||||
CG_LaunchExplode( origin, velocity, cgs.media.smoke2 );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*EXP_VELOCITY*2.0;
|
||||
velocity[1] = crandom()*EXP_VELOCITY*2.0;
|
||||
velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY;
|
||||
CG_LaunchExplode( origin, velocity, cgs.media.smoke2 );
|
||||
|
||||
VectorCopy( playerOrigin, origin );
|
||||
velocity[0] = crandom()*EXP_VELOCITY*2.5;
|
||||
velocity[1] = crandom()*EXP_VELOCITY*2.5;
|
||||
velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY;
|
||||
CG_LaunchExplode( origin, velocity, cgs.media.smoke2 );
|
||||
}
|
||||
|
1037
code/cgame/cg_ents.c
Normal file
1037
code/cgame/cg_ents.c
Normal file
File diff suppressed because it is too large
Load diff
1205
code/cgame/cg_event.c
Normal file
1205
code/cgame/cg_event.c
Normal file
File diff suppressed because it is too large
Load diff
297
code/cgame/cg_info.c
Normal file
297
code/cgame/cg_info.c
Normal file
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_info.c -- display information while data is being loading
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
#define MAX_LOADING_PLAYER_ICONS 16
|
||||
#define MAX_LOADING_ITEM_ICONS 26
|
||||
|
||||
static int loadingPlayerIconCount;
|
||||
static int loadingItemIconCount;
|
||||
static qhandle_t loadingPlayerIcons[MAX_LOADING_PLAYER_ICONS];
|
||||
static qhandle_t loadingItemIcons[MAX_LOADING_ITEM_ICONS];
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_DrawLoadingIcons
|
||||
===================
|
||||
*/
|
||||
static void CG_DrawLoadingIcons( void ) {
|
||||
int n;
|
||||
int x, y;
|
||||
|
||||
for( n = 0; n < loadingPlayerIconCount; n++ ) {
|
||||
x = 16 + n * 78;
|
||||
y = 324-40;
|
||||
CG_DrawPic( x, y, 64, 64, loadingPlayerIcons[n] );
|
||||
}
|
||||
|
||||
for( n = 0; n < loadingItemIconCount; n++ ) {
|
||||
y = 400-40;
|
||||
if( n >= 13 ) {
|
||||
y += 40;
|
||||
}
|
||||
x = 16 + n % 13 * 48;
|
||||
CG_DrawPic( x, y, 32, 32, loadingItemIcons[n] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
======================
|
||||
CG_LoadingString
|
||||
|
||||
======================
|
||||
*/
|
||||
void CG_LoadingString( const char *s ) {
|
||||
Q_strncpyz( cg.infoScreenText, s, sizeof( cg.infoScreenText ) );
|
||||
|
||||
trap_UpdateScreen();
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_LoadingItem
|
||||
===================
|
||||
*/
|
||||
void CG_LoadingItem( int itemNum ) {
|
||||
gitem_t *item;
|
||||
|
||||
item = &bg_itemlist[itemNum];
|
||||
|
||||
if ( item->icon && loadingItemIconCount < MAX_LOADING_ITEM_ICONS ) {
|
||||
loadingItemIcons[loadingItemIconCount++] = trap_R_RegisterShaderNoMip( item->icon );
|
||||
}
|
||||
|
||||
CG_LoadingString( item->pickup_name );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_LoadingClient
|
||||
===================
|
||||
*/
|
||||
void CG_LoadingClient( int clientNum ) {
|
||||
const char *info;
|
||||
char *skin;
|
||||
char personality[MAX_QPATH];
|
||||
char model[MAX_QPATH];
|
||||
char iconName[MAX_QPATH];
|
||||
|
||||
info = CG_ConfigString( CS_PLAYERS + clientNum );
|
||||
|
||||
if ( loadingPlayerIconCount < MAX_LOADING_PLAYER_ICONS ) {
|
||||
Q_strncpyz( model, Info_ValueForKey( info, "model" ), sizeof( model ) );
|
||||
skin = Q_strrchr( model, '/' );
|
||||
if ( skin ) {
|
||||
*skin++ = '\0';
|
||||
} else {
|
||||
skin = "default";
|
||||
}
|
||||
|
||||
Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", model, skin );
|
||||
|
||||
loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName );
|
||||
if ( !loadingPlayerIcons[loadingPlayerIconCount] ) {
|
||||
Com_sprintf( iconName, MAX_QPATH, "models/players/characters/%s/icon_%s.tga", model, skin );
|
||||
loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName );
|
||||
}
|
||||
if ( !loadingPlayerIcons[loadingPlayerIconCount] ) {
|
||||
Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", DEFAULT_MODEL, "default" );
|
||||
loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName );
|
||||
}
|
||||
if ( loadingPlayerIcons[loadingPlayerIconCount] ) {
|
||||
loadingPlayerIconCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Q_strncpyz( personality, Info_ValueForKey( info, "n" ), sizeof(personality) );
|
||||
Q_CleanStr( personality );
|
||||
|
||||
if( cgs.gametype == GT_SINGLE_PLAYER ) {
|
||||
trap_S_RegisterSound( va( "sound/player/announce/%s.wav", personality ), qtrue );
|
||||
}
|
||||
|
||||
CG_LoadingString( personality );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_DrawInformation
|
||||
|
||||
Draw all the status / pacifier stuff during level loading
|
||||
====================
|
||||
*/
|
||||
void CG_DrawInformation( void ) {
|
||||
const char *s;
|
||||
const char *info;
|
||||
const char *sysInfo;
|
||||
int y;
|
||||
int value;
|
||||
qhandle_t levelshot;
|
||||
qhandle_t detail;
|
||||
char buf[1024];
|
||||
|
||||
info = CG_ConfigString( CS_SERVERINFO );
|
||||
sysInfo = CG_ConfigString( CS_SYSTEMINFO );
|
||||
|
||||
s = Info_ValueForKey( info, "mapname" );
|
||||
levelshot = trap_R_RegisterShaderNoMip( va( "levelshots/%s.tga", s ) );
|
||||
if ( !levelshot ) {
|
||||
levelshot = trap_R_RegisterShaderNoMip( "menu/art/unknownmap" );
|
||||
}
|
||||
trap_R_SetColor( NULL );
|
||||
CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, levelshot );
|
||||
|
||||
// blend a detail texture over it
|
||||
detail = trap_R_RegisterShader( "levelShotDetail" );
|
||||
trap_R_DrawStretchPic( 0, 0, cgs.glconfig.vidWidth, cgs.glconfig.vidHeight, 0, 0, 2.5, 2, detail );
|
||||
|
||||
// draw the icons of things as they are loaded
|
||||
CG_DrawLoadingIcons();
|
||||
|
||||
// the first 150 rows are reserved for the client connection
|
||||
// screen to write into
|
||||
if ( cg.infoScreenText[0] ) {
|
||||
UI_DrawProportionalString( 320, 128-32, va("Loading... %s", cg.infoScreenText),
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
} else {
|
||||
UI_DrawProportionalString( 320, 128-32, "Awaiting snapshot...",
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
}
|
||||
|
||||
// draw info string information
|
||||
|
||||
y = 180-32;
|
||||
|
||||
// don't print server lines if playing a local game
|
||||
trap_Cvar_VariableStringBuffer( "sv_running", buf, sizeof( buf ) );
|
||||
if ( !atoi( buf ) ) {
|
||||
// server hostname
|
||||
Q_strncpyz(buf, Info_ValueForKey( info, "sv_hostname" ), 1024);
|
||||
Q_CleanStr(buf);
|
||||
UI_DrawProportionalString( 320, y, buf,
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
|
||||
// pure server
|
||||
s = Info_ValueForKey( sysInfo, "sv_pure" );
|
||||
if ( s[0] == '1' ) {
|
||||
UI_DrawProportionalString( 320, y, "Pure Server",
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
}
|
||||
|
||||
// server-specific message of the day
|
||||
s = CG_ConfigString( CS_MOTD );
|
||||
if ( s[0] ) {
|
||||
UI_DrawProportionalString( 320, y, s,
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
}
|
||||
|
||||
// some extra space after hostname and motd
|
||||
y += 10;
|
||||
}
|
||||
|
||||
// map-specific message (long map name)
|
||||
s = CG_ConfigString( CS_MESSAGE );
|
||||
if ( s[0] ) {
|
||||
UI_DrawProportionalString( 320, y, s,
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
}
|
||||
|
||||
// cheats warning
|
||||
s = Info_ValueForKey( sysInfo, "sv_cheats" );
|
||||
if ( s[0] == '1' ) {
|
||||
UI_DrawProportionalString( 320, y, "CHEATS ARE ENABLED",
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
}
|
||||
|
||||
// game type
|
||||
switch ( cgs.gametype ) {
|
||||
case GT_FFA:
|
||||
s = "Free For All";
|
||||
break;
|
||||
case GT_SINGLE_PLAYER:
|
||||
s = "Single Player";
|
||||
break;
|
||||
case GT_TOURNAMENT:
|
||||
s = "Tournament";
|
||||
break;
|
||||
case GT_TEAM:
|
||||
s = "Team Deathmatch";
|
||||
break;
|
||||
case GT_CTF:
|
||||
s = "Capture The Flag";
|
||||
break;
|
||||
#ifdef MISSIONPACK
|
||||
case GT_1FCTF:
|
||||
s = "One Flag CTF";
|
||||
break;
|
||||
case GT_OBELISK:
|
||||
s = "Overload";
|
||||
break;
|
||||
case GT_HARVESTER:
|
||||
s = "Harvester";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
s = "Unknown Gametype";
|
||||
break;
|
||||
}
|
||||
UI_DrawProportionalString( 320, y, s,
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
|
||||
value = atoi( Info_ValueForKey( info, "timelimit" ) );
|
||||
if ( value ) {
|
||||
UI_DrawProportionalString( 320, y, va( "timelimit %i", value ),
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
}
|
||||
|
||||
if (cgs.gametype < GT_CTF ) {
|
||||
value = atoi( Info_ValueForKey( info, "fraglimit" ) );
|
||||
if ( value ) {
|
||||
UI_DrawProportionalString( 320, y, va( "fraglimit %i", value ),
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
if (cgs.gametype >= GT_CTF) {
|
||||
value = atoi( Info_ValueForKey( info, "capturelimit" ) );
|
||||
if ( value ) {
|
||||
UI_DrawProportionalString( 320, y, va( "capturelimit %i", value ),
|
||||
UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite );
|
||||
y += PROP_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1669
code/cgame/cg_local.h
Normal file
1669
code/cgame/cg_local.h
Normal file
File diff suppressed because it is too large
Load diff
886
code/cgame/cg_localents.c
Normal file
886
code/cgame/cg_localents.c
Normal file
|
@ -0,0 +1,886 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
|
||||
// cg_localents.c -- every frame, generate renderer commands for locally
|
||||
// processed entities, like smoke puffs, gibs, shells, etc.
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
#define MAX_LOCAL_ENTITIES 512
|
||||
localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES];
|
||||
localEntity_t cg_activeLocalEntities; // double linked list
|
||||
localEntity_t *cg_freeLocalEntities; // single linked list
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_InitLocalEntities
|
||||
|
||||
This is called at startup and for tournement restarts
|
||||
===================
|
||||
*/
|
||||
void CG_InitLocalEntities( void ) {
|
||||
int i;
|
||||
|
||||
memset( cg_localEntities, 0, sizeof( cg_localEntities ) );
|
||||
cg_activeLocalEntities.next = &cg_activeLocalEntities;
|
||||
cg_activeLocalEntities.prev = &cg_activeLocalEntities;
|
||||
cg_freeLocalEntities = cg_localEntities;
|
||||
for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) {
|
||||
cg_localEntities[i].next = &cg_localEntities[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_FreeLocalEntity
|
||||
==================
|
||||
*/
|
||||
void CG_FreeLocalEntity( localEntity_t *le ) {
|
||||
if ( !le->prev ) {
|
||||
CG_Error( "CG_FreeLocalEntity: not active" );
|
||||
}
|
||||
|
||||
// remove from the doubly linked active list
|
||||
le->prev->next = le->next;
|
||||
le->next->prev = le->prev;
|
||||
|
||||
// the free list is only singly linked
|
||||
le->next = cg_freeLocalEntities;
|
||||
cg_freeLocalEntities = le;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_AllocLocalEntity
|
||||
|
||||
Will allways succeed, even if it requires freeing an old active entity
|
||||
===================
|
||||
*/
|
||||
localEntity_t *CG_AllocLocalEntity( void ) {
|
||||
localEntity_t *le;
|
||||
|
||||
if ( !cg_freeLocalEntities ) {
|
||||
// no free entities, so free the one at the end of the chain
|
||||
// remove the oldest active entity
|
||||
CG_FreeLocalEntity( cg_activeLocalEntities.prev );
|
||||
}
|
||||
|
||||
le = cg_freeLocalEntities;
|
||||
cg_freeLocalEntities = cg_freeLocalEntities->next;
|
||||
|
||||
memset( le, 0, sizeof( *le ) );
|
||||
|
||||
// link into the active list
|
||||
le->next = cg_activeLocalEntities.next;
|
||||
le->prev = &cg_activeLocalEntities;
|
||||
cg_activeLocalEntities.next->prev = le;
|
||||
cg_activeLocalEntities.next = le;
|
||||
return le;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================================================================================
|
||||
|
||||
FRAGMENT PROCESSING
|
||||
|
||||
A fragment localentity interacts with the environment in some way (hitting walls),
|
||||
or generates more localentities along a trail.
|
||||
|
||||
====================================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
CG_BloodTrail
|
||||
|
||||
Leave expanding blood puffs behind gibs
|
||||
================
|
||||
*/
|
||||
void CG_BloodTrail( localEntity_t *le ) {
|
||||
int t;
|
||||
int t2;
|
||||
int step;
|
||||
vec3_t newOrigin;
|
||||
localEntity_t *blood;
|
||||
|
||||
step = 150;
|
||||
t = step * ( (cg.time - cg.frametime + step ) / step );
|
||||
t2 = step * ( cg.time / step );
|
||||
|
||||
for ( ; t <= t2; t += step ) {
|
||||
BG_EvaluateTrajectory( &le->pos, t, newOrigin );
|
||||
|
||||
blood = CG_SmokePuff( newOrigin, vec3_origin,
|
||||
20, // radius
|
||||
1, 1, 1, 1, // color
|
||||
2000, // trailTime
|
||||
t, // startTime
|
||||
0, // fadeInTime
|
||||
0, // flags
|
||||
cgs.media.bloodTrailShader );
|
||||
// use the optimized version
|
||||
blood->leType = LE_FALL_SCALE_FADE;
|
||||
// drop a total of 40 units over its lifetime
|
||||
blood->pos.trDelta[2] = 40;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_FragmentBounceMark
|
||||
================
|
||||
*/
|
||||
void CG_FragmentBounceMark( localEntity_t *le, trace_t *trace ) {
|
||||
int radius;
|
||||
|
||||
if ( le->leMarkType == LEMT_BLOOD ) {
|
||||
|
||||
radius = 16 + (rand()&31);
|
||||
CG_ImpactMark( cgs.media.bloodMarkShader, trace->endpos, trace->plane.normal, random()*360,
|
||||
1,1,1,1, qtrue, radius, qfalse );
|
||||
} else if ( le->leMarkType == LEMT_BURN ) {
|
||||
|
||||
radius = 8 + (rand()&15);
|
||||
CG_ImpactMark( cgs.media.burnMarkShader, trace->endpos, trace->plane.normal, random()*360,
|
||||
1,1,1,1, qtrue, radius, qfalse );
|
||||
}
|
||||
|
||||
|
||||
// don't allow a fragment to make multiple marks, or they
|
||||
// pile up while settling
|
||||
le->leMarkType = LEMT_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_FragmentBounceSound
|
||||
================
|
||||
*/
|
||||
void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace ) {
|
||||
if ( le->leBounceSoundType == LEBS_BLOOD ) {
|
||||
// half the gibs will make splat sounds
|
||||
if ( rand() & 1 ) {
|
||||
int r = rand()&3;
|
||||
sfxHandle_t s;
|
||||
|
||||
if ( r == 0 ) {
|
||||
s = cgs.media.gibBounce1Sound;
|
||||
} else if ( r == 1 ) {
|
||||
s = cgs.media.gibBounce2Sound;
|
||||
} else {
|
||||
s = cgs.media.gibBounce3Sound;
|
||||
}
|
||||
trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s );
|
||||
}
|
||||
} else if ( le->leBounceSoundType == LEBS_BRASS ) {
|
||||
|
||||
}
|
||||
|
||||
// don't allow a fragment to make multiple bounce sounds,
|
||||
// or it gets too noisy as they settle
|
||||
le->leBounceSoundType = LEBS_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_ReflectVelocity
|
||||
================
|
||||
*/
|
||||
void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) {
|
||||
vec3_t velocity;
|
||||
float dot;
|
||||
int hitTime;
|
||||
|
||||
// reflect the velocity on the trace plane
|
||||
hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
|
||||
BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
|
||||
dot = DotProduct( velocity, trace->plane.normal );
|
||||
VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta );
|
||||
|
||||
VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );
|
||||
|
||||
VectorCopy( trace->endpos, le->pos.trBase );
|
||||
le->pos.trTime = cg.time;
|
||||
|
||||
|
||||
// check for stop, making sure that even on low FPS systems it doesn't bobble
|
||||
if ( trace->allsolid ||
|
||||
( trace->plane.normal[2] > 0 &&
|
||||
( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) {
|
||||
le->pos.trType = TR_STATIONARY;
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_AddFragment
|
||||
================
|
||||
*/
|
||||
void CG_AddFragment( localEntity_t *le ) {
|
||||
vec3_t newOrigin;
|
||||
trace_t trace;
|
||||
|
||||
if ( le->pos.trType == TR_STATIONARY ) {
|
||||
// sink into the ground if near the removal time
|
||||
int t;
|
||||
float oldZ;
|
||||
|
||||
t = le->endTime - cg.time;
|
||||
if ( t < SINK_TIME ) {
|
||||
// we must use an explicit lighting origin, otherwise the
|
||||
// lighting would be lost as soon as the origin went
|
||||
// into the ground
|
||||
VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin );
|
||||
le->refEntity.renderfx |= RF_LIGHTING_ORIGIN;
|
||||
oldZ = le->refEntity.origin[2];
|
||||
le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME );
|
||||
trap_R_AddRefEntityToScene( &le->refEntity );
|
||||
le->refEntity.origin[2] = oldZ;
|
||||
} else {
|
||||
trap_R_AddRefEntityToScene( &le->refEntity );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate new position
|
||||
BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin );
|
||||
|
||||
// trace a line from previous position to new position
|
||||
CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID );
|
||||
if ( trace.fraction == 1.0 ) {
|
||||
// still in free fall
|
||||
VectorCopy( newOrigin, le->refEntity.origin );
|
||||
|
||||
if ( le->leFlags & LEF_TUMBLE ) {
|
||||
vec3_t angles;
|
||||
|
||||
BG_EvaluateTrajectory( &le->angles, cg.time, angles );
|
||||
AnglesToAxis( angles, le->refEntity.axis );
|
||||
}
|
||||
|
||||
trap_R_AddRefEntityToScene( &le->refEntity );
|
||||
|
||||
// add a blood trail
|
||||
if ( le->leBounceSoundType == LEBS_BLOOD ) {
|
||||
CG_BloodTrail( le );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if it is in a nodrop zone, remove it
|
||||
// this keeps gibs from waiting at the bottom of pits of death
|
||||
// and floating levels
|
||||
if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) {
|
||||
CG_FreeLocalEntity( le );
|
||||
return;
|
||||
}
|
||||
|
||||
// leave a mark
|
||||
CG_FragmentBounceMark( le, &trace );
|
||||
|
||||
// do a bouncy sound
|
||||
CG_FragmentBounceSound( le, &trace );
|
||||
|
||||
// reflect the velocity on the trace plane
|
||||
CG_ReflectVelocity( le, &trace );
|
||||
|
||||
trap_R_AddRefEntityToScene( &le->refEntity );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================================================
|
||||
|
||||
TRIVIAL LOCAL ENTITIES
|
||||
|
||||
These only do simple scaling or modulation before passing to the renderer
|
||||
=====================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_AddFadeRGB
|
||||
====================
|
||||
*/
|
||||
void CG_AddFadeRGB( localEntity_t *le ) {
|
||||
refEntity_t *re;
|
||||
float c;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
c = ( le->endTime - cg.time ) * le->lifeRate;
|
||||
c *= 0xff;
|
||||
|
||||
re->shaderRGBA[0] = le->color[0] * c;
|
||||
re->shaderRGBA[1] = le->color[1] * c;
|
||||
re->shaderRGBA[2] = le->color[2] * c;
|
||||
re->shaderRGBA[3] = le->color[3] * c;
|
||||
|
||||
trap_R_AddRefEntityToScene( re );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_AddMoveScaleFade
|
||||
==================
|
||||
*/
|
||||
static void CG_AddMoveScaleFade( localEntity_t *le ) {
|
||||
refEntity_t *re;
|
||||
float c;
|
||||
vec3_t delta;
|
||||
float len;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) {
|
||||
// fade / grow time
|
||||
c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime );
|
||||
}
|
||||
else {
|
||||
// fade / grow time
|
||||
c = ( le->endTime - cg.time ) * le->lifeRate;
|
||||
}
|
||||
|
||||
re->shaderRGBA[3] = 0xff * c * le->color[3];
|
||||
|
||||
if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) {
|
||||
re->radius = le->radius * ( 1.0 - c ) + 8;
|
||||
}
|
||||
|
||||
BG_EvaluateTrajectory( &le->pos, cg.time, re->origin );
|
||||
|
||||
// if the view would be "inside" the sprite, kill the sprite
|
||||
// so it doesn't add too much overdraw
|
||||
VectorSubtract( re->origin, cg.refdef.vieworg, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len < le->radius ) {
|
||||
CG_FreeLocalEntity( le );
|
||||
return;
|
||||
}
|
||||
|
||||
trap_R_AddRefEntityToScene( re );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_AddScaleFade
|
||||
|
||||
For rocket smokes that hang in place, fade out, and are
|
||||
removed if the view passes through them.
|
||||
There are often many of these, so it needs to be simple.
|
||||
===================
|
||||
*/
|
||||
static void CG_AddScaleFade( localEntity_t *le ) {
|
||||
refEntity_t *re;
|
||||
float c;
|
||||
vec3_t delta;
|
||||
float len;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
// fade / grow time
|
||||
c = ( le->endTime - cg.time ) * le->lifeRate;
|
||||
|
||||
re->shaderRGBA[3] = 0xff * c * le->color[3];
|
||||
re->radius = le->radius * ( 1.0 - c ) + 8;
|
||||
|
||||
// if the view would be "inside" the sprite, kill the sprite
|
||||
// so it doesn't add too much overdraw
|
||||
VectorSubtract( re->origin, cg.refdef.vieworg, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len < le->radius ) {
|
||||
CG_FreeLocalEntity( le );
|
||||
return;
|
||||
}
|
||||
|
||||
trap_R_AddRefEntityToScene( re );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_AddFallScaleFade
|
||||
|
||||
This is just an optimized CG_AddMoveScaleFade
|
||||
For blood mists that drift down, fade out, and are
|
||||
removed if the view passes through them.
|
||||
There are often 100+ of these, so it needs to be simple.
|
||||
=================
|
||||
*/
|
||||
static void CG_AddFallScaleFade( localEntity_t *le ) {
|
||||
refEntity_t *re;
|
||||
float c;
|
||||
vec3_t delta;
|
||||
float len;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
// fade time
|
||||
c = ( le->endTime - cg.time ) * le->lifeRate;
|
||||
|
||||
re->shaderRGBA[3] = 0xff * c * le->color[3];
|
||||
|
||||
re->origin[2] = le->pos.trBase[2] - ( 1.0 - c ) * le->pos.trDelta[2];
|
||||
|
||||
re->radius = le->radius * ( 1.0 - c ) + 16;
|
||||
|
||||
// if the view would be "inside" the sprite, kill the sprite
|
||||
// so it doesn't add too much overdraw
|
||||
VectorSubtract( re->origin, cg.refdef.vieworg, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len < le->radius ) {
|
||||
CG_FreeLocalEntity( le );
|
||||
return;
|
||||
}
|
||||
|
||||
trap_R_AddRefEntityToScene( re );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_AddExplosion
|
||||
================
|
||||
*/
|
||||
static void CG_AddExplosion( localEntity_t *ex ) {
|
||||
refEntity_t *ent;
|
||||
|
||||
ent = &ex->refEntity;
|
||||
|
||||
// add the entity
|
||||
trap_R_AddRefEntityToScene(ent);
|
||||
|
||||
// add the dlight
|
||||
if ( ex->light ) {
|
||||
float light;
|
||||
|
||||
light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime );
|
||||
if ( light < 0.5 ) {
|
||||
light = 1.0;
|
||||
} else {
|
||||
light = 1.0 - ( light - 0.5 ) * 2;
|
||||
}
|
||||
light = ex->light * light;
|
||||
trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_AddSpriteExplosion
|
||||
================
|
||||
*/
|
||||
static void CG_AddSpriteExplosion( localEntity_t *le ) {
|
||||
refEntity_t re;
|
||||
float c;
|
||||
|
||||
re = le->refEntity;
|
||||
|
||||
c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime );
|
||||
if ( c > 1 ) {
|
||||
c = 1.0; // can happen during connection problems
|
||||
}
|
||||
|
||||
re.shaderRGBA[0] = 0xff;
|
||||
re.shaderRGBA[1] = 0xff;
|
||||
re.shaderRGBA[2] = 0xff;
|
||||
re.shaderRGBA[3] = 0xff * c * 0.33;
|
||||
|
||||
re.reType = RT_SPRITE;
|
||||
re.radius = 42 * ( 1.0 - c ) + 30;
|
||||
|
||||
trap_R_AddRefEntityToScene( &re );
|
||||
|
||||
// add the dlight
|
||||
if ( le->light ) {
|
||||
float light;
|
||||
|
||||
light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime );
|
||||
if ( light < 0.5 ) {
|
||||
light = 1.0;
|
||||
} else {
|
||||
light = 1.0 - ( light - 0.5 ) * 2;
|
||||
}
|
||||
light = le->light * light;
|
||||
trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef MISSIONPACK
|
||||
/*
|
||||
====================
|
||||
CG_AddKamikaze
|
||||
====================
|
||||
*/
|
||||
void CG_AddKamikaze( localEntity_t *le ) {
|
||||
refEntity_t *re;
|
||||
refEntity_t shockwave;
|
||||
float c;
|
||||
vec3_t test, axis[3];
|
||||
int t;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
t = cg.time - le->startTime;
|
||||
VectorClear( test );
|
||||
AnglesToAxis( test, axis );
|
||||
|
||||
if (t > KAMI_SHOCKWAVE_STARTTIME && t < KAMI_SHOCKWAVE_ENDTIME) {
|
||||
|
||||
if (!(le->leFlags & LEF_SOUND1)) {
|
||||
// trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeExplodeSound );
|
||||
trap_S_StartLocalSound(cgs.media.kamikazeExplodeSound, CHAN_AUTO);
|
||||
le->leFlags |= LEF_SOUND1;
|
||||
}
|
||||
// 1st kamikaze shockwave
|
||||
memset(&shockwave, 0, sizeof(shockwave));
|
||||
shockwave.hModel = cgs.media.kamikazeShockWave;
|
||||
shockwave.reType = RT_MODEL;
|
||||
shockwave.shaderTime = re->shaderTime;
|
||||
VectorCopy(re->origin, shockwave.origin);
|
||||
|
||||
c = (float)(t - KAMI_SHOCKWAVE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME);
|
||||
VectorScale( axis[0], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] );
|
||||
VectorScale( axis[1], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] );
|
||||
VectorScale( axis[2], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] );
|
||||
shockwave.nonNormalizedAxes = qtrue;
|
||||
|
||||
if (t > KAMI_SHOCKWAVEFADE_STARTTIME) {
|
||||
c = (float)(t - KAMI_SHOCKWAVEFADE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVEFADE_STARTTIME);
|
||||
}
|
||||
else {
|
||||
c = 0;
|
||||
}
|
||||
c *= 0xff;
|
||||
shockwave.shaderRGBA[0] = 0xff - c;
|
||||
shockwave.shaderRGBA[1] = 0xff - c;
|
||||
shockwave.shaderRGBA[2] = 0xff - c;
|
||||
shockwave.shaderRGBA[3] = 0xff - c;
|
||||
|
||||
trap_R_AddRefEntityToScene( &shockwave );
|
||||
}
|
||||
|
||||
if (t > KAMI_EXPLODE_STARTTIME && t < KAMI_IMPLODE_ENDTIME) {
|
||||
// explosion and implosion
|
||||
c = ( le->endTime - cg.time ) * le->lifeRate;
|
||||
c *= 0xff;
|
||||
re->shaderRGBA[0] = le->color[0] * c;
|
||||
re->shaderRGBA[1] = le->color[1] * c;
|
||||
re->shaderRGBA[2] = le->color[2] * c;
|
||||
re->shaderRGBA[3] = le->color[3] * c;
|
||||
|
||||
if( t < KAMI_IMPLODE_STARTTIME ) {
|
||||
c = (float)(t - KAMI_EXPLODE_STARTTIME) / (float)(KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME);
|
||||
}
|
||||
else {
|
||||
if (!(le->leFlags & LEF_SOUND2)) {
|
||||
// trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeImplodeSound );
|
||||
trap_S_StartLocalSound(cgs.media.kamikazeImplodeSound, CHAN_AUTO);
|
||||
le->leFlags |= LEF_SOUND2;
|
||||
}
|
||||
c = (float)(KAMI_IMPLODE_ENDTIME - t) / (float) (KAMI_IMPLODE_ENDTIME - KAMI_IMPLODE_STARTTIME);
|
||||
}
|
||||
VectorScale( axis[0], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[0] );
|
||||
VectorScale( axis[1], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[1] );
|
||||
VectorScale( axis[2], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[2] );
|
||||
re->nonNormalizedAxes = qtrue;
|
||||
|
||||
trap_R_AddRefEntityToScene( re );
|
||||
// add the dlight
|
||||
trap_R_AddLightToScene( re->origin, c * 1000.0, 1.0, 1.0, c );
|
||||
}
|
||||
|
||||
if (t > KAMI_SHOCKWAVE2_STARTTIME && t < KAMI_SHOCKWAVE2_ENDTIME) {
|
||||
// 2nd kamikaze shockwave
|
||||
if (le->angles.trBase[0] == 0 &&
|
||||
le->angles.trBase[1] == 0 &&
|
||||
le->angles.trBase[2] == 0) {
|
||||
le->angles.trBase[0] = random() * 360;
|
||||
le->angles.trBase[1] = random() * 360;
|
||||
le->angles.trBase[2] = random() * 360;
|
||||
}
|
||||
else {
|
||||
c = 0;
|
||||
}
|
||||
memset(&shockwave, 0, sizeof(shockwave));
|
||||
shockwave.hModel = cgs.media.kamikazeShockWave;
|
||||
shockwave.reType = RT_MODEL;
|
||||
shockwave.shaderTime = re->shaderTime;
|
||||
VectorCopy(re->origin, shockwave.origin);
|
||||
|
||||
test[0] = le->angles.trBase[0];
|
||||
test[1] = le->angles.trBase[1];
|
||||
test[2] = le->angles.trBase[2];
|
||||
AnglesToAxis( test, axis );
|
||||
|
||||
c = (float)(t - KAMI_SHOCKWAVE2_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2_STARTTIME);
|
||||
VectorScale( axis[0], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] );
|
||||
VectorScale( axis[1], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] );
|
||||
VectorScale( axis[2], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] );
|
||||
shockwave.nonNormalizedAxes = qtrue;
|
||||
|
||||
if (t > KAMI_SHOCKWAVE2FADE_STARTTIME) {
|
||||
c = (float)(t - KAMI_SHOCKWAVE2FADE_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2FADE_STARTTIME);
|
||||
}
|
||||
else {
|
||||
c = 0;
|
||||
}
|
||||
c *= 0xff;
|
||||
shockwave.shaderRGBA[0] = 0xff - c;
|
||||
shockwave.shaderRGBA[1] = 0xff - c;
|
||||
shockwave.shaderRGBA[2] = 0xff - c;
|
||||
shockwave.shaderRGBA[3] = 0xff - c;
|
||||
|
||||
trap_R_AddRefEntityToScene( &shockwave );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_AddInvulnerabilityImpact
|
||||
===================
|
||||
*/
|
||||
void CG_AddInvulnerabilityImpact( localEntity_t *le ) {
|
||||
trap_R_AddRefEntityToScene( &le->refEntity );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_AddInvulnerabilityJuiced
|
||||
===================
|
||||
*/
|
||||
void CG_AddInvulnerabilityJuiced( localEntity_t *le ) {
|
||||
int t;
|
||||
|
||||
t = cg.time - le->startTime;
|
||||
if ( t > 3000 ) {
|
||||
le->refEntity.axis[0][0] = (float) 1.0 + 0.3 * (t - 3000) / 2000;
|
||||
le->refEntity.axis[1][1] = (float) 1.0 + 0.3 * (t - 3000) / 2000;
|
||||
le->refEntity.axis[2][2] = (float) 0.7 + 0.3 * (2000 - (t - 3000)) / 2000;
|
||||
}
|
||||
if ( t > 5000 ) {
|
||||
le->endTime = 0;
|
||||
CG_GibPlayer( le->refEntity.origin );
|
||||
}
|
||||
else {
|
||||
trap_R_AddRefEntityToScene( &le->refEntity );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_AddRefEntity
|
||||
===================
|
||||
*/
|
||||
void CG_AddRefEntity( localEntity_t *le ) {
|
||||
if (le->endTime < cg.time) {
|
||||
CG_FreeLocalEntity( le );
|
||||
return;
|
||||
}
|
||||
trap_R_AddRefEntityToScene( &le->refEntity );
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
===================
|
||||
CG_AddScorePlum
|
||||
===================
|
||||
*/
|
||||
#define NUMBER_SIZE 8
|
||||
|
||||
void CG_AddScorePlum( localEntity_t *le ) {
|
||||
refEntity_t *re;
|
||||
vec3_t origin, delta, dir, vec, up = {0, 0, 1};
|
||||
float c, len;
|
||||
int i, score, digits[10], numdigits, negative;
|
||||
|
||||
re = &le->refEntity;
|
||||
|
||||
c = ( le->endTime - cg.time ) * le->lifeRate;
|
||||
|
||||
score = le->radius;
|
||||
if (score < 0) {
|
||||
re->shaderRGBA[0] = 0xff;
|
||||
re->shaderRGBA[1] = 0x11;
|
||||
re->shaderRGBA[2] = 0x11;
|
||||
}
|
||||
else {
|
||||
re->shaderRGBA[0] = 0xff;
|
||||
re->shaderRGBA[1] = 0xff;
|
||||
re->shaderRGBA[2] = 0xff;
|
||||
if (score >= 50) {
|
||||
re->shaderRGBA[1] = 0;
|
||||
} else if (score >= 20) {
|
||||
re->shaderRGBA[0] = re->shaderRGBA[1] = 0;
|
||||
} else if (score >= 10) {
|
||||
re->shaderRGBA[2] = 0;
|
||||
} else if (score >= 2) {
|
||||
re->shaderRGBA[0] = re->shaderRGBA[2] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
if (c < 0.25)
|
||||
re->shaderRGBA[3] = 0xff * 4 * c;
|
||||
else
|
||||
re->shaderRGBA[3] = 0xff;
|
||||
|
||||
re->radius = NUMBER_SIZE / 2;
|
||||
|
||||
VectorCopy(le->pos.trBase, origin);
|
||||
origin[2] += 110 - c * 100;
|
||||
|
||||
VectorSubtract(cg.refdef.vieworg, origin, dir);
|
||||
CrossProduct(dir, up, vec);
|
||||
VectorNormalize(vec);
|
||||
|
||||
VectorMA(origin, -10 + 20 * sin(c * 2 * M_PI), vec, origin);
|
||||
|
||||
// if the view would be "inside" the sprite, kill the sprite
|
||||
// so it doesn't add too much overdraw
|
||||
VectorSubtract( origin, cg.refdef.vieworg, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len < 20 ) {
|
||||
CG_FreeLocalEntity( le );
|
||||
return;
|
||||
}
|
||||
|
||||
negative = qfalse;
|
||||
if (score < 0) {
|
||||
negative = qtrue;
|
||||
score = -score;
|
||||
}
|
||||
|
||||
for (numdigits = 0; !(numdigits && !score); numdigits++) {
|
||||
digits[numdigits] = score % 10;
|
||||
score = score / 10;
|
||||
}
|
||||
|
||||
if (negative) {
|
||||
digits[numdigits] = 10;
|
||||
numdigits++;
|
||||
}
|
||||
|
||||
for (i = 0; i < numdigits; i++) {
|
||||
VectorMA(origin, (float) (((float) numdigits / 2) - i) * NUMBER_SIZE, vec, re->origin);
|
||||
re->customShader = cgs.media.numberShaders[digits[numdigits-1-i]];
|
||||
trap_R_AddRefEntityToScene( re );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_AddLocalEntities
|
||||
|
||||
===================
|
||||
*/
|
||||
void CG_AddLocalEntities( void ) {
|
||||
localEntity_t *le, *next;
|
||||
|
||||
// walk the list backwards, so any new local entities generated
|
||||
// (trails, marks, etc) will be present this frame
|
||||
le = cg_activeLocalEntities.prev;
|
||||
for ( ; le != &cg_activeLocalEntities ; le = next ) {
|
||||
// grab next now, so if the local entity is freed we
|
||||
// still have it
|
||||
next = le->prev;
|
||||
|
||||
if ( cg.time >= le->endTime ) {
|
||||
CG_FreeLocalEntity( le );
|
||||
continue;
|
||||
}
|
||||
switch ( le->leType ) {
|
||||
default:
|
||||
CG_Error( "Bad leType: %i", le->leType );
|
||||
break;
|
||||
|
||||
case LE_MARK:
|
||||
break;
|
||||
|
||||
case LE_SPRITE_EXPLOSION:
|
||||
CG_AddSpriteExplosion( le );
|
||||
break;
|
||||
|
||||
case LE_EXPLOSION:
|
||||
CG_AddExplosion( le );
|
||||
break;
|
||||
|
||||
case LE_FRAGMENT: // gibs and brass
|
||||
CG_AddFragment( le );
|
||||
break;
|
||||
|
||||
case LE_MOVE_SCALE_FADE: // water bubbles
|
||||
CG_AddMoveScaleFade( le );
|
||||
break;
|
||||
|
||||
case LE_FADE_RGB: // teleporters, railtrails
|
||||
CG_AddFadeRGB( le );
|
||||
break;
|
||||
|
||||
case LE_FALL_SCALE_FADE: // gib blood trails
|
||||
CG_AddFallScaleFade( le );
|
||||
break;
|
||||
|
||||
case LE_SCALE_FADE: // rocket trails
|
||||
CG_AddScaleFade( le );
|
||||
break;
|
||||
|
||||
case LE_SCOREPLUM:
|
||||
CG_AddScorePlum( le );
|
||||
break;
|
||||
|
||||
#ifdef MISSIONPACK
|
||||
case LE_KAMIKAZE:
|
||||
CG_AddKamikaze( le );
|
||||
break;
|
||||
case LE_INVULIMPACT:
|
||||
CG_AddInvulnerabilityImpact( le );
|
||||
break;
|
||||
case LE_INVULJUICED:
|
||||
CG_AddInvulnerabilityJuiced( le );
|
||||
break;
|
||||
case LE_SHOWREFENTITY:
|
||||
CG_AddRefEntity( le );
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
1997
code/cgame/cg_main.c
Normal file
1997
code/cgame/cg_main.c
Normal file
File diff suppressed because it is too large
Load diff
2274
code/cgame/cg_marks.c
Normal file
2274
code/cgame/cg_marks.c
Normal file
File diff suppressed because it is too large
Load diff
1852
code/cgame/cg_newdraw.c
Normal file
1852
code/cgame/cg_newdraw.c
Normal file
File diff suppressed because it is too large
Load diff
2018
code/cgame/cg_particles.c
Normal file
2018
code/cgame/cg_particles.c
Normal file
File diff suppressed because it is too large
Load diff
2619
code/cgame/cg_players.c
Normal file
2619
code/cgame/cg_players.c
Normal file
File diff suppressed because it is too large
Load diff
526
code/cgame/cg_playerstate.c
Normal file
526
code/cgame/cg_playerstate.c
Normal file
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_playerstate.c -- this file acts on changes in a new playerState_t
|
||||
// With normal play, this will be done after local prediction, but when
|
||||
// following another player or playing back a demo, it will be checked
|
||||
// when the snapshot transitions like all the other entities
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
/*
|
||||
==============
|
||||
CG_CheckAmmo
|
||||
|
||||
If the ammo has gone low enough to generate the warning, play a sound
|
||||
==============
|
||||
*/
|
||||
void CG_CheckAmmo( void ) {
|
||||
int i;
|
||||
int total;
|
||||
int previous;
|
||||
int weapons;
|
||||
|
||||
// see about how many seconds of ammo we have remaining
|
||||
weapons = cg.snap->ps.stats[ STAT_WEAPONS ];
|
||||
total = 0;
|
||||
for ( i = WP_MACHINEGUN ; i < WP_NUM_WEAPONS ; i++ ) {
|
||||
if ( ! ( weapons & ( 1 << i ) ) ) {
|
||||
continue;
|
||||
}
|
||||
switch ( i ) {
|
||||
case WP_ROCKET_LAUNCHER:
|
||||
case WP_GRENADE_LAUNCHER:
|
||||
case WP_RAILGUN:
|
||||
case WP_SHOTGUN:
|
||||
#ifdef MISSIONPACK
|
||||
case WP_PROX_LAUNCHER:
|
||||
#endif
|
||||
total += cg.snap->ps.ammo[i] * 1000;
|
||||
break;
|
||||
default:
|
||||
total += cg.snap->ps.ammo[i] * 200;
|
||||
break;
|
||||
}
|
||||
if ( total >= 5000 ) {
|
||||
cg.lowAmmoWarning = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
previous = cg.lowAmmoWarning;
|
||||
|
||||
if ( total == 0 ) {
|
||||
cg.lowAmmoWarning = 2;
|
||||
} else {
|
||||
cg.lowAmmoWarning = 1;
|
||||
}
|
||||
|
||||
// play a sound on transitions
|
||||
if ( cg.lowAmmoWarning != previous ) {
|
||||
trap_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CG_DamageFeedback
|
||||
==============
|
||||
*/
|
||||
void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) {
|
||||
float left, front, up;
|
||||
float kick;
|
||||
int health;
|
||||
float scale;
|
||||
vec3_t dir;
|
||||
vec3_t angles;
|
||||
float dist;
|
||||
float yaw, pitch;
|
||||
|
||||
// show the attacking player's head and name in corner
|
||||
cg.attackerTime = cg.time;
|
||||
|
||||
// the lower on health you are, the greater the view kick will be
|
||||
health = cg.snap->ps.stats[STAT_HEALTH];
|
||||
if ( health < 40 ) {
|
||||
scale = 1;
|
||||
} else {
|
||||
scale = 40.0 / health;
|
||||
}
|
||||
kick = damage * scale;
|
||||
|
||||
if (kick < 5)
|
||||
kick = 5;
|
||||
if (kick > 10)
|
||||
kick = 10;
|
||||
|
||||
// if yaw and pitch are both 255, make the damage always centered (falling, etc)
|
||||
if ( yawByte == 255 && pitchByte == 255 ) {
|
||||
cg.damageX = 0;
|
||||
cg.damageY = 0;
|
||||
cg.v_dmg_roll = 0;
|
||||
cg.v_dmg_pitch = -kick;
|
||||
} else {
|
||||
// positional
|
||||
pitch = pitchByte / 255.0 * 360;
|
||||
yaw = yawByte / 255.0 * 360;
|
||||
|
||||
angles[PITCH] = pitch;
|
||||
angles[YAW] = yaw;
|
||||
angles[ROLL] = 0;
|
||||
|
||||
AngleVectors( angles, dir, NULL, NULL );
|
||||
VectorSubtract( vec3_origin, dir, dir );
|
||||
|
||||
front = DotProduct (dir, cg.refdef.viewaxis[0] );
|
||||
left = DotProduct (dir, cg.refdef.viewaxis[1] );
|
||||
up = DotProduct (dir, cg.refdef.viewaxis[2] );
|
||||
|
||||
dir[0] = front;
|
||||
dir[1] = left;
|
||||
dir[2] = 0;
|
||||
dist = VectorLength( dir );
|
||||
if ( dist < 0.1 ) {
|
||||
dist = 0.1f;
|
||||
}
|
||||
|
||||
cg.v_dmg_roll = kick * left;
|
||||
|
||||
cg.v_dmg_pitch = -kick * front;
|
||||
|
||||
if ( front <= 0.1 ) {
|
||||
front = 0.1f;
|
||||
}
|
||||
cg.damageX = -left / front;
|
||||
cg.damageY = up / dist;
|
||||
}
|
||||
|
||||
// clamp the position
|
||||
if ( cg.damageX > 1.0 ) {
|
||||
cg.damageX = 1.0;
|
||||
}
|
||||
if ( cg.damageX < - 1.0 ) {
|
||||
cg.damageX = -1.0;
|
||||
}
|
||||
|
||||
if ( cg.damageY > 1.0 ) {
|
||||
cg.damageY = 1.0;
|
||||
}
|
||||
if ( cg.damageY < - 1.0 ) {
|
||||
cg.damageY = -1.0;
|
||||
}
|
||||
|
||||
// don't let the screen flashes vary as much
|
||||
if ( kick > 10 ) {
|
||||
kick = 10;
|
||||
}
|
||||
cg.damageValue = kick;
|
||||
cg.v_dmg_time = cg.time + DAMAGE_TIME;
|
||||
cg.damageTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CG_Respawn
|
||||
|
||||
A respawn happened this snapshot
|
||||
================
|
||||
*/
|
||||
void CG_Respawn( void ) {
|
||||
// no error decay on player movement
|
||||
cg.thisFrameTeleport = qtrue;
|
||||
|
||||
// display weapons available
|
||||
cg.weaponSelectTime = cg.time;
|
||||
|
||||
// select the weapon the server says we are using
|
||||
cg.weaponSelect = cg.snap->ps.weapon;
|
||||
}
|
||||
|
||||
extern char *eventnames[];
|
||||
|
||||
/*
|
||||
==============
|
||||
CG_CheckPlayerstateEvents
|
||||
==============
|
||||
*/
|
||||
void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {
|
||||
int i;
|
||||
int event;
|
||||
centity_t *cent;
|
||||
|
||||
if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) {
|
||||
cent = &cg_entities[ ps->clientNum ];
|
||||
cent->currentState.event = ps->externalEvent;
|
||||
cent->currentState.eventParm = ps->externalEventParm;
|
||||
CG_EntityEvent( cent, cent->lerpOrigin );
|
||||
}
|
||||
|
||||
cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ];
|
||||
// go through the predictable events buffer
|
||||
for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) {
|
||||
// if we have a new predictable event
|
||||
if ( i >= ops->eventSequence
|
||||
// or the server told us to play another event instead of a predicted event we already issued
|
||||
// or something the server told us changed our prediction causing a different event
|
||||
|| (i > ops->eventSequence - MAX_PS_EVENTS && ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]) ) {
|
||||
|
||||
event = ps->events[ i & (MAX_PS_EVENTS-1) ];
|
||||
cent->currentState.event = event;
|
||||
cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ];
|
||||
CG_EntityEvent( cent, cent->lerpOrigin );
|
||||
|
||||
cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event;
|
||||
|
||||
cg.eventSequence++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_CheckChangedPredictableEvents
|
||||
==================
|
||||
*/
|
||||
void CG_CheckChangedPredictableEvents( playerState_t *ps ) {
|
||||
int i;
|
||||
int event;
|
||||
centity_t *cent;
|
||||
|
||||
cent = &cg.predictedPlayerEntity;
|
||||
for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) {
|
||||
//
|
||||
if (i >= cg.eventSequence) {
|
||||
continue;
|
||||
}
|
||||
// if this event is not further back in than the maximum predictable events we remember
|
||||
if (i > cg.eventSequence - MAX_PREDICTED_EVENTS) {
|
||||
// if the new playerstate event is different from a previously predicted one
|
||||
if ( ps->events[i & (MAX_PS_EVENTS-1)] != cg.predictableEvents[i & (MAX_PREDICTED_EVENTS-1) ] ) {
|
||||
|
||||
event = ps->events[ i & (MAX_PS_EVENTS-1) ];
|
||||
cent->currentState.event = event;
|
||||
cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ];
|
||||
CG_EntityEvent( cent, cent->lerpOrigin );
|
||||
|
||||
cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event;
|
||||
|
||||
if ( cg_showmiss.integer ) {
|
||||
CG_Printf("WARNING: changed predicted event\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
pushReward
|
||||
==================
|
||||
*/
|
||||
static void pushReward(sfxHandle_t sfx, qhandle_t shader, int rewardCount) {
|
||||
if (cg.rewardStack < (MAX_REWARDSTACK-1)) {
|
||||
cg.rewardStack++;
|
||||
cg.rewardSound[cg.rewardStack] = sfx;
|
||||
cg.rewardShader[cg.rewardStack] = shader;
|
||||
cg.rewardCount[cg.rewardStack] = rewardCount;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_CheckLocalSounds
|
||||
==================
|
||||
*/
|
||||
void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
|
||||
int highScore, health, armor, reward;
|
||||
sfxHandle_t sfx;
|
||||
|
||||
// don't play the sounds if the player just changed teams
|
||||
if ( ps->persistant[PERS_TEAM] != ops->persistant[PERS_TEAM] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// hit changes
|
||||
if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) {
|
||||
armor = ps->persistant[PERS_ATTACKEE_ARMOR] & 0xff;
|
||||
health = ps->persistant[PERS_ATTACKEE_ARMOR] >> 8;
|
||||
#ifdef MISSIONPACK
|
||||
if (armor > 50 ) {
|
||||
trap_S_StartLocalSound( cgs.media.hitSoundHighArmor, CHAN_LOCAL_SOUND );
|
||||
} else if (armor || health > 100) {
|
||||
trap_S_StartLocalSound( cgs.media.hitSoundLowArmor, CHAN_LOCAL_SOUND );
|
||||
} else {
|
||||
trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND );
|
||||
}
|
||||
#else
|
||||
trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND );
|
||||
#endif
|
||||
} else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) {
|
||||
trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND );
|
||||
}
|
||||
|
||||
// health changes of more than -1 should make pain sounds
|
||||
if ( ps->stats[STAT_HEALTH] < ops->stats[STAT_HEALTH] - 1 ) {
|
||||
if ( ps->stats[STAT_HEALTH] > 0 ) {
|
||||
CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[STAT_HEALTH] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if we are going into the intermission, don't start any voices
|
||||
if ( cg.intermissionStarted ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reward sounds
|
||||
reward = qfalse;
|
||||
if (ps->persistant[PERS_CAPTURES] != ops->persistant[PERS_CAPTURES]) {
|
||||
pushReward(cgs.media.captureAwardSound, cgs.media.medalCapture, ps->persistant[PERS_CAPTURES]);
|
||||
reward = qtrue;
|
||||
//Com_Printf("capture\n");
|
||||
}
|
||||
if (ps->persistant[PERS_IMPRESSIVE_COUNT] != ops->persistant[PERS_IMPRESSIVE_COUNT]) {
|
||||
#ifdef MISSIONPACK
|
||||
if (ps->persistant[PERS_IMPRESSIVE_COUNT] == 1) {
|
||||
sfx = cgs.media.firstImpressiveSound;
|
||||
} else {
|
||||
sfx = cgs.media.impressiveSound;
|
||||
}
|
||||
#else
|
||||
sfx = cgs.media.impressiveSound;
|
||||
#endif
|
||||
pushReward(sfx, cgs.media.medalImpressive, ps->persistant[PERS_IMPRESSIVE_COUNT]);
|
||||
reward = qtrue;
|
||||
//Com_Printf("impressive\n");
|
||||
}
|
||||
if (ps->persistant[PERS_EXCELLENT_COUNT] != ops->persistant[PERS_EXCELLENT_COUNT]) {
|
||||
#ifdef MISSIONPACK
|
||||
if (ps->persistant[PERS_EXCELLENT_COUNT] == 1) {
|
||||
sfx = cgs.media.firstExcellentSound;
|
||||
} else {
|
||||
sfx = cgs.media.excellentSound;
|
||||
}
|
||||
#else
|
||||
sfx = cgs.media.excellentSound;
|
||||
#endif
|
||||
pushReward(sfx, cgs.media.medalExcellent, ps->persistant[PERS_EXCELLENT_COUNT]);
|
||||
reward = qtrue;
|
||||
//Com_Printf("excellent\n");
|
||||
}
|
||||
if (ps->persistant[PERS_GAUNTLET_FRAG_COUNT] != ops->persistant[PERS_GAUNTLET_FRAG_COUNT]) {
|
||||
#ifdef MISSIONPACK
|
||||
if (ops->persistant[PERS_GAUNTLET_FRAG_COUNT] == 1) {
|
||||
sfx = cgs.media.firstHumiliationSound;
|
||||
} else {
|
||||
sfx = cgs.media.humiliationSound;
|
||||
}
|
||||
#else
|
||||
sfx = cgs.media.humiliationSound;
|
||||
#endif
|
||||
pushReward(sfx, cgs.media.medalGauntlet, ps->persistant[PERS_GAUNTLET_FRAG_COUNT]);
|
||||
reward = qtrue;
|
||||
//Com_Printf("guantlet frag\n");
|
||||
}
|
||||
if (ps->persistant[PERS_DEFEND_COUNT] != ops->persistant[PERS_DEFEND_COUNT]) {
|
||||
pushReward(cgs.media.defendSound, cgs.media.medalDefend, ps->persistant[PERS_DEFEND_COUNT]);
|
||||
reward = qtrue;
|
||||
//Com_Printf("defend\n");
|
||||
}
|
||||
if (ps->persistant[PERS_ASSIST_COUNT] != ops->persistant[PERS_ASSIST_COUNT]) {
|
||||
pushReward(cgs.media.assistSound, cgs.media.medalAssist, ps->persistant[PERS_ASSIST_COUNT]);
|
||||
reward = qtrue;
|
||||
//Com_Printf("assist\n");
|
||||
}
|
||||
// if any of the player event bits changed
|
||||
if (ps->persistant[PERS_PLAYEREVENTS] != ops->persistant[PERS_PLAYEREVENTS]) {
|
||||
if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD) !=
|
||||
(ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD)) {
|
||||
trap_S_StartLocalSound( cgs.media.deniedSound, CHAN_ANNOUNCER );
|
||||
}
|
||||
else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD) !=
|
||||
(ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD)) {
|
||||
trap_S_StartLocalSound( cgs.media.humiliationSound, CHAN_ANNOUNCER );
|
||||
}
|
||||
else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT) !=
|
||||
(ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT)) {
|
||||
trap_S_StartLocalSound( cgs.media.holyShitSound, CHAN_ANNOUNCER );
|
||||
}
|
||||
reward = qtrue;
|
||||
}
|
||||
|
||||
// check for flag pickup
|
||||
if ( cgs.gametype >= GT_TEAM ) {
|
||||
if ((ps->powerups[PW_REDFLAG] != ops->powerups[PW_REDFLAG] && ps->powerups[PW_REDFLAG]) ||
|
||||
(ps->powerups[PW_BLUEFLAG] != ops->powerups[PW_BLUEFLAG] && ps->powerups[PW_BLUEFLAG]) ||
|
||||
(ps->powerups[PW_NEUTRALFLAG] != ops->powerups[PW_NEUTRALFLAG] && ps->powerups[PW_NEUTRALFLAG]) )
|
||||
{
|
||||
trap_S_StartLocalSound( cgs.media.youHaveFlagSound, CHAN_ANNOUNCER );
|
||||
}
|
||||
}
|
||||
|
||||
// lead changes
|
||||
if (!reward) {
|
||||
//
|
||||
if ( !cg.warmup ) {
|
||||
// never play lead changes during warmup
|
||||
if ( ps->persistant[PERS_RANK] != ops->persistant[PERS_RANK] ) {
|
||||
if ( cgs.gametype < GT_TEAM) {
|
||||
if ( ps->persistant[PERS_RANK] == 0 ) {
|
||||
CG_AddBufferedSound(cgs.media.takenLeadSound);
|
||||
} else if ( ps->persistant[PERS_RANK] == RANK_TIED_FLAG ) {
|
||||
CG_AddBufferedSound(cgs.media.tiedLeadSound);
|
||||
} else if ( ( ops->persistant[PERS_RANK] & ~RANK_TIED_FLAG ) == 0 ) {
|
||||
CG_AddBufferedSound(cgs.media.lostLeadSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// timelimit warnings
|
||||
if ( cgs.timelimit > 0 ) {
|
||||
int msec;
|
||||
|
||||
msec = cg.time - cgs.levelStartTime;
|
||||
if ( !( cg.timelimitWarnings & 4 ) && msec > ( cgs.timelimit * 60 + 2 ) * 1000 ) {
|
||||
cg.timelimitWarnings |= 1 | 2 | 4;
|
||||
trap_S_StartLocalSound( cgs.media.suddenDeathSound, CHAN_ANNOUNCER );
|
||||
}
|
||||
else if ( !( cg.timelimitWarnings & 2 ) && msec > (cgs.timelimit - 1) * 60 * 1000 ) {
|
||||
cg.timelimitWarnings |= 1 | 2;
|
||||
trap_S_StartLocalSound( cgs.media.oneMinuteSound, CHAN_ANNOUNCER );
|
||||
}
|
||||
else if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && msec > (cgs.timelimit - 5) * 60 * 1000 ) {
|
||||
cg.timelimitWarnings |= 1;
|
||||
trap_S_StartLocalSound( cgs.media.fiveMinuteSound, CHAN_ANNOUNCER );
|
||||
}
|
||||
}
|
||||
|
||||
// fraglimit warnings
|
||||
if ( cgs.fraglimit > 0 && cgs.gametype < GT_CTF) {
|
||||
highScore = cgs.scores1;
|
||||
if ( !( cg.fraglimitWarnings & 4 ) && highScore == (cgs.fraglimit - 1) ) {
|
||||
cg.fraglimitWarnings |= 1 | 2 | 4;
|
||||
CG_AddBufferedSound(cgs.media.oneFragSound);
|
||||
}
|
||||
else if ( cgs.fraglimit > 2 && !( cg.fraglimitWarnings & 2 ) && highScore == (cgs.fraglimit - 2) ) {
|
||||
cg.fraglimitWarnings |= 1 | 2;
|
||||
CG_AddBufferedSound(cgs.media.twoFragSound);
|
||||
}
|
||||
else if ( cgs.fraglimit > 3 && !( cg.fraglimitWarnings & 1 ) && highScore == (cgs.fraglimit - 3) ) {
|
||||
cg.fraglimitWarnings |= 1;
|
||||
CG_AddBufferedSound(cgs.media.threeFragSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_TransitionPlayerState
|
||||
|
||||
===============
|
||||
*/
|
||||
void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) {
|
||||
// check for changing follow mode
|
||||
if ( ps->clientNum != ops->clientNum ) {
|
||||
cg.thisFrameTeleport = qtrue;
|
||||
// make sure we don't get any unwanted transition effects
|
||||
*ops = *ps;
|
||||
}
|
||||
|
||||
// damage events (player is getting wounded)
|
||||
if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) {
|
||||
CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount );
|
||||
}
|
||||
|
||||
// respawning
|
||||
if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) {
|
||||
CG_Respawn();
|
||||
}
|
||||
|
||||
if ( cg.mapRestart ) {
|
||||
CG_Respawn();
|
||||
cg.mapRestart = qfalse;
|
||||
}
|
||||
|
||||
if ( cg.snap->ps.pm_type != PM_INTERMISSION
|
||||
&& ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) {
|
||||
CG_CheckLocalSounds( ps, ops );
|
||||
}
|
||||
|
||||
// check for going low on ammo
|
||||
CG_CheckAmmo();
|
||||
|
||||
// run events
|
||||
CG_CheckPlayerstateEvents( ps, ops );
|
||||
|
||||
// smooth the ducking viewheight change
|
||||
if ( ps->viewheight != ops->viewheight ) {
|
||||
cg.duckChange = ps->viewheight - ops->viewheight;
|
||||
cg.duckTime = cg.time;
|
||||
}
|
||||
}
|
||||
|
628
code/cgame/cg_predict.c
Normal file
628
code/cgame/cg_predict.c
Normal file
|
@ -0,0 +1,628 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_predict.c -- this file generates cg.predictedPlayerState by either
|
||||
// interpolating between snapshots from the server or locally predicting
|
||||
// ahead the client's movement.
|
||||
// It also handles local physics interaction, like fragments bouncing off walls
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
static pmove_t cg_pmove;
|
||||
|
||||
static int cg_numSolidEntities;
|
||||
static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
|
||||
static int cg_numTriggerEntities;
|
||||
static centity_t *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT];
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_BuildSolidList
|
||||
|
||||
When a new cg.snap has been set, this function builds a sublist
|
||||
of the entities that are actually solid, to make for more
|
||||
efficient collision detection
|
||||
====================
|
||||
*/
|
||||
void CG_BuildSolidList( void ) {
|
||||
int i;
|
||||
centity_t *cent;
|
||||
snapshot_t *snap;
|
||||
entityState_t *ent;
|
||||
|
||||
cg_numSolidEntities = 0;
|
||||
cg_numTriggerEntities = 0;
|
||||
|
||||
if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
|
||||
snap = cg.nextSnap;
|
||||
} else {
|
||||
snap = cg.snap;
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < snap->numEntities ; i++ ) {
|
||||
cent = &cg_entities[ snap->entities[ i ].number ];
|
||||
ent = ¢->currentState;
|
||||
|
||||
if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) {
|
||||
cg_triggerEntities[cg_numTriggerEntities] = cent;
|
||||
cg_numTriggerEntities++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( cent->nextState.solid ) {
|
||||
cg_solidEntities[cg_numSolidEntities] = cent;
|
||||
cg_numSolidEntities++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_ClipMoveToEntities
|
||||
|
||||
====================
|
||||
*/
|
||||
static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
|
||||
int skipNumber, int mask, trace_t *tr ) {
|
||||
int i, x, zd, zu;
|
||||
trace_t trace;
|
||||
entityState_t *ent;
|
||||
clipHandle_t cmodel;
|
||||
vec3_t bmins, bmaxs;
|
||||
vec3_t origin, angles;
|
||||
centity_t *cent;
|
||||
|
||||
for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
|
||||
cent = cg_solidEntities[ i ];
|
||||
ent = ¢->currentState;
|
||||
|
||||
if ( ent->number == skipNumber ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ent->solid == SOLID_BMODEL ) {
|
||||
// special value for bmodel
|
||||
cmodel = trap_CM_InlineModel( ent->modelindex );
|
||||
VectorCopy( cent->lerpAngles, angles );
|
||||
BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin );
|
||||
} else {
|
||||
// encoded bbox
|
||||
x = (ent->solid & 255);
|
||||
zd = ((ent->solid>>8) & 255);
|
||||
zu = ((ent->solid>>16) & 255) - 32;
|
||||
|
||||
bmins[0] = bmins[1] = -x;
|
||||
bmaxs[0] = bmaxs[1] = x;
|
||||
bmins[2] = -zd;
|
||||
bmaxs[2] = zu;
|
||||
|
||||
cmodel = trap_CM_TempBoxModel( bmins, bmaxs );
|
||||
VectorCopy( vec3_origin, angles );
|
||||
VectorCopy( cent->lerpOrigin, origin );
|
||||
}
|
||||
|
||||
|
||||
trap_CM_TransformedBoxTrace ( &trace, start, end,
|
||||
mins, maxs, cmodel, mask, origin, angles);
|
||||
|
||||
if (trace.allsolid || trace.fraction < tr->fraction) {
|
||||
trace.entityNum = ent->number;
|
||||
*tr = trace;
|
||||
} else if (trace.startsolid) {
|
||||
tr->startsolid = qtrue;
|
||||
}
|
||||
if ( tr->allsolid ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_Trace
|
||||
================
|
||||
*/
|
||||
void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
|
||||
int skipNumber, int mask ) {
|
||||
trace_t t;
|
||||
|
||||
trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
|
||||
t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
|
||||
// check all other solid models
|
||||
CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t);
|
||||
|
||||
*result = t;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CG_PointContents
|
||||
================
|
||||
*/
|
||||
int CG_PointContents( const vec3_t point, int passEntityNum ) {
|
||||
int i;
|
||||
entityState_t *ent;
|
||||
centity_t *cent;
|
||||
clipHandle_t cmodel;
|
||||
int contents;
|
||||
|
||||
contents = trap_CM_PointContents (point, 0);
|
||||
|
||||
for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
|
||||
cent = cg_solidEntities[ i ];
|
||||
|
||||
ent = ¢->currentState;
|
||||
|
||||
if ( ent->number == passEntityNum ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ent->solid != SOLID_BMODEL) { // special value for bmodel
|
||||
continue;
|
||||
}
|
||||
|
||||
cmodel = trap_CM_InlineModel( ent->modelindex );
|
||||
if ( !cmodel ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
CG_InterpolatePlayerState
|
||||
|
||||
Generates cg.predictedPlayerState by interpolating between
|
||||
cg.snap->player_state and cg.nextFrame->player_state
|
||||
========================
|
||||
*/
|
||||
static void CG_InterpolatePlayerState( qboolean grabAngles ) {
|
||||
float f;
|
||||
int i;
|
||||
playerState_t *out;
|
||||
snapshot_t *prev, *next;
|
||||
|
||||
out = &cg.predictedPlayerState;
|
||||
prev = cg.snap;
|
||||
next = cg.nextSnap;
|
||||
|
||||
*out = cg.snap->ps;
|
||||
|
||||
// if we are still allowing local input, short circuit the view angles
|
||||
if ( grabAngles ) {
|
||||
usercmd_t cmd;
|
||||
int cmdNum;
|
||||
|
||||
cmdNum = trap_GetCurrentCmdNumber();
|
||||
trap_GetUserCmd( cmdNum, &cmd );
|
||||
|
||||
PM_UpdateViewAngles( out, &cmd );
|
||||
}
|
||||
|
||||
// if the next frame is a teleport, we can't lerp to it
|
||||
if ( cg.nextFrameTeleport ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !next || next->serverTime <= prev->serverTime ) {
|
||||
return;
|
||||
}
|
||||
|
||||
f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
|
||||
|
||||
i = next->ps.bobCycle;
|
||||
if ( i < prev->ps.bobCycle ) {
|
||||
i += 256; // handle wraparound
|
||||
}
|
||||
out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );
|
||||
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] );
|
||||
if ( !grabAngles ) {
|
||||
out->viewangles[i] = LerpAngle(
|
||||
prev->ps.viewangles[i], next->ps.viewangles[i], f );
|
||||
}
|
||||
out->velocity[i] = prev->ps.velocity[i] +
|
||||
f * (next->ps.velocity[i] - prev->ps.velocity[i] );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_TouchItem
|
||||
===================
|
||||
*/
|
||||
static void CG_TouchItem( centity_t *cent ) {
|
||||
gitem_t *item;
|
||||
|
||||
if ( !cg_predictItems.integer ) {
|
||||
return;
|
||||
}
|
||||
if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, ¢->currentState, cg.time ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// never pick an item up twice in a prediction
|
||||
if ( cent->miscTime == cg.time ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !BG_CanItemBeGrabbed( cgs.gametype, ¢->currentState, &cg.predictedPlayerState ) ) {
|
||||
return; // can't hold it
|
||||
}
|
||||
|
||||
item = &bg_itemlist[ cent->currentState.modelindex ];
|
||||
|
||||
// Special case for flags.
|
||||
// We don't predict touching our own flag
|
||||
#ifdef MISSIONPACK
|
||||
if( cgs.gametype == GT_1FCTF ) {
|
||||
if( item->giTag != PW_NEUTRALFLAG ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if( cgs.gametype == GT_CTF || cgs.gametype == GT_HARVESTER ) {
|
||||
#else
|
||||
if( cgs.gametype == GT_CTF ) {
|
||||
#endif
|
||||
if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED &&
|
||||
item->giTag == PW_REDFLAG)
|
||||
return;
|
||||
if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE &&
|
||||
item->giTag == PW_BLUEFLAG)
|
||||
return;
|
||||
}
|
||||
|
||||
// grab it
|
||||
BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState);
|
||||
|
||||
// remove it from the frame so it won't be drawn
|
||||
cent->currentState.eFlags |= EF_NODRAW;
|
||||
|
||||
// don't touch it again this prediction
|
||||
cent->miscTime = cg.time;
|
||||
|
||||
// if its a weapon, give them some predicted ammo so the autoswitch will work
|
||||
if ( item->giType == IT_WEAPON ) {
|
||||
cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag;
|
||||
if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) {
|
||||
cg.predictedPlayerState.ammo[ item->giTag ] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=========================
|
||||
CG_TouchTriggerPrediction
|
||||
|
||||
Predict push triggers and items
|
||||
=========================
|
||||
*/
|
||||
static void CG_TouchTriggerPrediction( void ) {
|
||||
int i;
|
||||
trace_t trace;
|
||||
entityState_t *ent;
|
||||
clipHandle_t cmodel;
|
||||
centity_t *cent;
|
||||
qboolean spectator;
|
||||
|
||||
// dead clients don't activate triggers
|
||||
if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR );
|
||||
|
||||
if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) {
|
||||
cent = cg_triggerEntities[ i ];
|
||||
ent = ¢->currentState;
|
||||
|
||||
if ( ent->eType == ET_ITEM && !spectator ) {
|
||||
CG_TouchItem( cent );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ent->solid != SOLID_BMODEL ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cmodel = trap_CM_InlineModel( ent->modelindex );
|
||||
if ( !cmodel ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin,
|
||||
cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );
|
||||
|
||||
if ( !trace.startsolid ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ent->eType == ET_TELEPORT_TRIGGER ) {
|
||||
cg.hyperspace = qtrue;
|
||||
} else if ( ent->eType == ET_PUSH_TRIGGER ) {
|
||||
BG_TouchJumpPad( &cg.predictedPlayerState, ent );
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't touch a jump pad this pmove frame
|
||||
if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) {
|
||||
cg.predictedPlayerState.jumppad_frame = 0;
|
||||
cg.predictedPlayerState.jumppad_ent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_PredictPlayerState
|
||||
|
||||
Generates cg.predictedPlayerState for the current cg.time
|
||||
cg.predictedPlayerState is guaranteed to be valid after exiting.
|
||||
|
||||
For demo playback, this will be an interpolation between two valid
|
||||
playerState_t.
|
||||
|
||||
For normal gameplay, it will be the result of predicted usercmd_t on
|
||||
top of the most recent playerState_t received from the server.
|
||||
|
||||
Each new snapshot will usually have one or more new usercmd over the last,
|
||||
but we simulate all unacknowledged commands each time, not just the new ones.
|
||||
This means that on an internet connection, quite a few pmoves may be issued
|
||||
each frame.
|
||||
|
||||
OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t
|
||||
differs from the predicted one. Would require saving all intermediate
|
||||
playerState_t during prediction.
|
||||
|
||||
We detect prediction errors and allow them to be decayed off over several frames
|
||||
to ease the jerk.
|
||||
=================
|
||||
*/
|
||||
void CG_PredictPlayerState( void ) {
|
||||
int cmdNum, current;
|
||||
playerState_t oldPlayerState;
|
||||
qboolean moved;
|
||||
usercmd_t oldestCmd;
|
||||
usercmd_t latestCmd;
|
||||
|
||||
cg.hyperspace = qfalse; // will be set if touching a trigger_teleport
|
||||
|
||||
// if this is the first frame we must guarantee
|
||||
// predictedPlayerState is valid even if there is some
|
||||
// other error condition
|
||||
if ( !cg.validPPS ) {
|
||||
cg.validPPS = qtrue;
|
||||
cg.predictedPlayerState = cg.snap->ps;
|
||||
}
|
||||
|
||||
|
||||
// demo playback just copies the moves
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
|
||||
CG_InterpolatePlayerState( qfalse );
|
||||
return;
|
||||
}
|
||||
|
||||
// non-predicting local movement will grab the latest angles
|
||||
if ( cg_nopredict.integer || cg_synchronousClients.integer ) {
|
||||
CG_InterpolatePlayerState( qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare for pmove
|
||||
cg_pmove.ps = &cg.predictedPlayerState;
|
||||
cg_pmove.trace = CG_Trace;
|
||||
cg_pmove.pointcontents = CG_PointContents;
|
||||
if ( cg_pmove.ps->pm_type == PM_DEAD ) {
|
||||
cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
|
||||
}
|
||||
else {
|
||||
cg_pmove.tracemask = MASK_PLAYERSOLID;
|
||||
}
|
||||
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
|
||||
cg_pmove.tracemask &= ~CONTENTS_BODY; // spectators can fly through bodies
|
||||
}
|
||||
cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;
|
||||
|
||||
// save the state before the pmove so we can detect transitions
|
||||
oldPlayerState = cg.predictedPlayerState;
|
||||
|
||||
current = trap_GetCurrentCmdNumber();
|
||||
|
||||
// if we don't have the commands right after the snapshot, we
|
||||
// can't accurately predict a current position, so just freeze at
|
||||
// the last good position we had
|
||||
cmdNum = current - CMD_BACKUP + 1;
|
||||
trap_GetUserCmd( cmdNum, &oldestCmd );
|
||||
if ( oldestCmd.serverTime > cg.snap->ps.commandTime
|
||||
&& oldestCmd.serverTime < cg.time ) { // special check for map_restart
|
||||
if ( cg_showmiss.integer ) {
|
||||
CG_Printf ("exceeded PACKET_BACKUP on commands\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get the latest command so we can know which commands are from previous map_restarts
|
||||
trap_GetUserCmd( current, &latestCmd );
|
||||
|
||||
// get the most recent information we have, even if
|
||||
// the server time is beyond our current cg.time,
|
||||
// because predicted player positions are going to
|
||||
// be ahead of everything else anyway
|
||||
if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
|
||||
cg.predictedPlayerState = cg.nextSnap->ps;
|
||||
cg.physicsTime = cg.nextSnap->serverTime;
|
||||
} else {
|
||||
cg.predictedPlayerState = cg.snap->ps;
|
||||
cg.physicsTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
if ( pmove_msec.integer < 8 ) {
|
||||
trap_Cvar_Set("pmove_msec", "8");
|
||||
}
|
||||
else if (pmove_msec.integer > 33) {
|
||||
trap_Cvar_Set("pmove_msec", "33");
|
||||
}
|
||||
|
||||
cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer;
|
||||
cg_pmove.pmove_msec = pmove_msec.integer;
|
||||
|
||||
// run cmds
|
||||
moved = qfalse;
|
||||
for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) {
|
||||
// get the command
|
||||
trap_GetUserCmd( cmdNum, &cg_pmove.cmd );
|
||||
|
||||
if ( cg_pmove.pmove_fixed ) {
|
||||
PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd );
|
||||
}
|
||||
|
||||
// don't do anything if the time is before the snapshot player time
|
||||
if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't do anything if the command was from a previous map_restart
|
||||
if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for a prediction error from last frame
|
||||
// on a lan, this will often be the exact value
|
||||
// from the snapshot, but on a wan we will have
|
||||
// to predict several commands to get to the point
|
||||
// we want to compare
|
||||
if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) {
|
||||
vec3_t delta;
|
||||
float len;
|
||||
|
||||
if ( cg.thisFrameTeleport ) {
|
||||
// a teleport will not cause an error decay
|
||||
VectorClear( cg.predictedError );
|
||||
if ( cg_showmiss.integer ) {
|
||||
CG_Printf( "PredictionTeleport\n" );
|
||||
}
|
||||
cg.thisFrameTeleport = qfalse;
|
||||
} else {
|
||||
vec3_t adjusted;
|
||||
CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
|
||||
cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted );
|
||||
|
||||
if ( cg_showmiss.integer ) {
|
||||
if (!VectorCompare( oldPlayerState.origin, adjusted )) {
|
||||
CG_Printf("prediction error\n");
|
||||
}
|
||||
}
|
||||
VectorSubtract( oldPlayerState.origin, adjusted, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len > 0.1 ) {
|
||||
if ( cg_showmiss.integer ) {
|
||||
CG_Printf("Prediction miss: %f\n", len);
|
||||
}
|
||||
if ( cg_errorDecay.integer ) {
|
||||
int t;
|
||||
float f;
|
||||
|
||||
t = cg.time - cg.predictedErrorTime;
|
||||
f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
|
||||
if ( f < 0 ) {
|
||||
f = 0;
|
||||
}
|
||||
if ( f > 0 && cg_showmiss.integer ) {
|
||||
CG_Printf("Double prediction decay: %f\n", f);
|
||||
}
|
||||
VectorScale( cg.predictedError, f, cg.predictedError );
|
||||
} else {
|
||||
VectorClear( cg.predictedError );
|
||||
}
|
||||
VectorAdd( delta, cg.predictedError, cg.predictedError );
|
||||
cg.predictedErrorTime = cg.oldTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't predict gauntlet firing, which is only supposed to happen
|
||||
// when it actually inflicts damage
|
||||
cg_pmove.gauntletHit = qfalse;
|
||||
|
||||
if ( cg_pmove.pmove_fixed ) {
|
||||
cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
|
||||
}
|
||||
|
||||
Pmove (&cg_pmove);
|
||||
|
||||
moved = qtrue;
|
||||
|
||||
// add push trigger movement effects
|
||||
CG_TouchTriggerPrediction();
|
||||
|
||||
// check for predictable events that changed from previous predictions
|
||||
//CG_CheckChangedPredictableEvents(&cg.predictedPlayerState);
|
||||
}
|
||||
|
||||
if ( cg_showmiss.integer > 1 ) {
|
||||
CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time );
|
||||
}
|
||||
|
||||
if ( !moved ) {
|
||||
if ( cg_showmiss.integer ) {
|
||||
CG_Printf( "not moved\n" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// adjust for the movement of the groundentity
|
||||
CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
|
||||
cg.predictedPlayerState.groundEntityNum,
|
||||
cg.physicsTime, cg.time, cg.predictedPlayerState.origin );
|
||||
|
||||
if ( cg_showmiss.integer ) {
|
||||
if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) {
|
||||
CG_Printf("WARNING: dropped event\n");
|
||||
}
|
||||
}
|
||||
|
||||
// fire events and other transition triggered things
|
||||
CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState );
|
||||
|
||||
if ( cg_showmiss.integer ) {
|
||||
if (cg.eventSequence > cg.predictedPlayerState.eventSequence) {
|
||||
CG_Printf("WARNING: double event\n");
|
||||
cg.eventSequence = cg.predictedPlayerState.eventSequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
238
code/cgame/cg_public.h
Normal file
238
code/cgame/cg_public.h
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
|
||||
|
||||
#define CMD_BACKUP 64
|
||||
#define CMD_MASK (CMD_BACKUP - 1)
|
||||
// allow a lot of command backups for very fast systems
|
||||
// multiple commands may be combined into a single packet, so this
|
||||
// needs to be larger than PACKET_BACKUP
|
||||
|
||||
|
||||
#define MAX_ENTITIES_IN_SNAPSHOT 256
|
||||
|
||||
// snapshots are a view of the server at a given time
|
||||
|
||||
// Snapshots are generated at regular time intervals by the server,
|
||||
// but they may not be sent if a client's rate level is exceeded, or
|
||||
// they may be dropped by the network.
|
||||
typedef struct {
|
||||
int snapFlags; // SNAPFLAG_RATE_DELAYED, etc
|
||||
int ping;
|
||||
|
||||
int serverTime; // server time the message is valid for (in msec)
|
||||
|
||||
byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
|
||||
|
||||
playerState_t ps; // complete information about the current player at this time
|
||||
|
||||
int numEntities; // all of the entities that need to be presented
|
||||
entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot
|
||||
|
||||
int numServerCommands; // text based server commands to execute when this
|
||||
int serverCommandSequence; // snapshot becomes current
|
||||
} snapshot_t;
|
||||
|
||||
enum {
|
||||
CGAME_EVENT_NONE,
|
||||
CGAME_EVENT_TEAMMENU,
|
||||
CGAME_EVENT_SCOREBOARD,
|
||||
CGAME_EVENT_EDITHUD
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
==================================================================
|
||||
|
||||
functions imported from the main executable
|
||||
|
||||
==================================================================
|
||||
*/
|
||||
|
||||
#define CGAME_IMPORT_API_VERSION 4
|
||||
|
||||
typedef enum {
|
||||
CG_PRINT,
|
||||
CG_ERROR,
|
||||
CG_MILLISECONDS,
|
||||
CG_CVAR_REGISTER,
|
||||
CG_CVAR_UPDATE,
|
||||
CG_CVAR_SET,
|
||||
CG_CVAR_VARIABLESTRINGBUFFER,
|
||||
CG_ARGC,
|
||||
CG_ARGV,
|
||||
CG_ARGS,
|
||||
CG_FS_FOPENFILE,
|
||||
CG_FS_READ,
|
||||
CG_FS_WRITE,
|
||||
CG_FS_FCLOSEFILE,
|
||||
CG_SENDCONSOLECOMMAND,
|
||||
CG_ADDCOMMAND,
|
||||
CG_SENDCLIENTCOMMAND,
|
||||
CG_UPDATESCREEN,
|
||||
CG_CM_LOADMAP,
|
||||
CG_CM_NUMINLINEMODELS,
|
||||
CG_CM_INLINEMODEL,
|
||||
CG_CM_LOADMODEL,
|
||||
CG_CM_TEMPBOXMODEL,
|
||||
CG_CM_POINTCONTENTS,
|
||||
CG_CM_TRANSFORMEDPOINTCONTENTS,
|
||||
CG_CM_BOXTRACE,
|
||||
CG_CM_TRANSFORMEDBOXTRACE,
|
||||
CG_CM_MARKFRAGMENTS,
|
||||
CG_S_STARTSOUND,
|
||||
CG_S_STARTLOCALSOUND,
|
||||
CG_S_CLEARLOOPINGSOUNDS,
|
||||
CG_S_ADDLOOPINGSOUND,
|
||||
CG_S_UPDATEENTITYPOSITION,
|
||||
CG_S_RESPATIALIZE,
|
||||
CG_S_REGISTERSOUND,
|
||||
CG_S_STARTBACKGROUNDTRACK,
|
||||
CG_R_LOADWORLDMAP,
|
||||
CG_R_REGISTERMODEL,
|
||||
CG_R_REGISTERSKIN,
|
||||
CG_R_REGISTERSHADER,
|
||||
CG_R_CLEARSCENE,
|
||||
CG_R_ADDREFENTITYTOSCENE,
|
||||
CG_R_ADDPOLYTOSCENE,
|
||||
CG_R_ADDLIGHTTOSCENE,
|
||||
CG_R_RENDERSCENE,
|
||||
CG_R_SETCOLOR,
|
||||
CG_R_DRAWSTRETCHPIC,
|
||||
CG_R_MODELBOUNDS,
|
||||
CG_R_LERPTAG,
|
||||
CG_GETGLCONFIG,
|
||||
CG_GETGAMESTATE,
|
||||
CG_GETCURRENTSNAPSHOTNUMBER,
|
||||
CG_GETSNAPSHOT,
|
||||
CG_GETSERVERCOMMAND,
|
||||
CG_GETCURRENTCMDNUMBER,
|
||||
CG_GETUSERCMD,
|
||||
CG_SETUSERCMDVALUE,
|
||||
CG_R_REGISTERSHADERNOMIP,
|
||||
CG_MEMORY_REMAINING,
|
||||
CG_R_REGISTERFONT,
|
||||
CG_KEY_ISDOWN,
|
||||
CG_KEY_GETCATCHER,
|
||||
CG_KEY_SETCATCHER,
|
||||
CG_KEY_GETKEY,
|
||||
CG_PC_ADD_GLOBAL_DEFINE,
|
||||
CG_PC_LOAD_SOURCE,
|
||||
CG_PC_FREE_SOURCE,
|
||||
CG_PC_READ_TOKEN,
|
||||
CG_PC_SOURCE_FILE_AND_LINE,
|
||||
CG_S_STOPBACKGROUNDTRACK,
|
||||
CG_REAL_TIME,
|
||||
CG_SNAPVECTOR,
|
||||
CG_REMOVECOMMAND,
|
||||
CG_R_LIGHTFORPOINT,
|
||||
CG_CIN_PLAYCINEMATIC,
|
||||
CG_CIN_STOPCINEMATIC,
|
||||
CG_CIN_RUNCINEMATIC,
|
||||
CG_CIN_DRAWCINEMATIC,
|
||||
CG_CIN_SETEXTENTS,
|
||||
CG_R_REMAP_SHADER,
|
||||
CG_S_ADDREALLOOPINGSOUND,
|
||||
CG_S_STOPLOOPINGSOUND,
|
||||
|
||||
CG_CM_TEMPCAPSULEMODEL,
|
||||
CG_CM_CAPSULETRACE,
|
||||
CG_CM_TRANSFORMEDCAPSULETRACE,
|
||||
CG_R_ADDADDITIVELIGHTTOSCENE,
|
||||
CG_GET_ENTITY_TOKEN,
|
||||
CG_R_ADDPOLYSTOSCENE,
|
||||
CG_R_INPVS,
|
||||
// 1.32
|
||||
CG_FS_SEEK,
|
||||
|
||||
/*
|
||||
CG_LOADCAMERA,
|
||||
CG_STARTCAMERA,
|
||||
CG_GETCAMERAINFO,
|
||||
*/
|
||||
|
||||
CG_MEMSET = 100,
|
||||
CG_MEMCPY,
|
||||
CG_STRNCPY,
|
||||
CG_SIN,
|
||||
CG_COS,
|
||||
CG_ATAN2,
|
||||
CG_SQRT,
|
||||
CG_FLOOR,
|
||||
CG_CEIL,
|
||||
CG_TESTPRINTINT,
|
||||
CG_TESTPRINTFLOAT,
|
||||
CG_ACOS
|
||||
} cgameImport_t;
|
||||
|
||||
|
||||
/*
|
||||
==================================================================
|
||||
|
||||
functions exported to the main executable
|
||||
|
||||
==================================================================
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
CG_INIT,
|
||||
// void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
|
||||
// called when the level loads or when the renderer is restarted
|
||||
// all media should be registered at this time
|
||||
// cgame will display loading status by calling SCR_Update, which
|
||||
// will call CG_DrawInformation during the loading process
|
||||
// reliableCommandSequence will be 0 on fresh loads, but higher for
|
||||
// demos, tourney restarts, or vid_restarts
|
||||
|
||||
CG_SHUTDOWN,
|
||||
// void (*CG_Shutdown)( void );
|
||||
// oportunity to flush and close any open files
|
||||
|
||||
CG_CONSOLE_COMMAND,
|
||||
// qboolean (*CG_ConsoleCommand)( void );
|
||||
// a console command has been issued locally that is not recognized by the
|
||||
// main game system.
|
||||
// use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the
|
||||
// command is not known to the game
|
||||
|
||||
CG_DRAW_ACTIVE_FRAME,
|
||||
// void (*CG_DrawActiveFrame)( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback );
|
||||
// Generates and draws a game scene and status information at the given time.
|
||||
// If demoPlayback is set, local movement prediction will not be enabled
|
||||
|
||||
CG_CROSSHAIR_PLAYER,
|
||||
// int (*CG_CrosshairPlayer)( void );
|
||||
|
||||
CG_LAST_ATTACKER,
|
||||
// int (*CG_LastAttacker)( void );
|
||||
|
||||
CG_KEY_EVENT,
|
||||
// void (*CG_KeyEvent)( int key, qboolean down );
|
||||
|
||||
CG_MOUSE_EVENT,
|
||||
// void (*CG_MouseEvent)( int dx, int dy );
|
||||
CG_EVENT_HANDLING
|
||||
// void (*CG_EventHandling)(int type);
|
||||
} cgameExport_t;
|
||||
|
||||
//----------------------------------------------
|
534
code/cgame/cg_scoreboard.c
Normal file
534
code/cgame/cg_scoreboard.c
Normal file
|
@ -0,0 +1,534 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_scoreboard -- draw the scoreboard on top of the game screen
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
#define SCOREBOARD_X (0)
|
||||
|
||||
#define SB_HEADER 86
|
||||
#define SB_TOP (SB_HEADER+32)
|
||||
|
||||
// Where the status bar starts, so we don't overwrite it
|
||||
#define SB_STATUSBAR 420
|
||||
|
||||
#define SB_NORMAL_HEIGHT 40
|
||||
#define SB_INTER_HEIGHT 16 // interleaved height
|
||||
|
||||
#define SB_MAXCLIENTS_NORMAL ((SB_STATUSBAR - SB_TOP) / SB_NORMAL_HEIGHT)
|
||||
#define SB_MAXCLIENTS_INTER ((SB_STATUSBAR - SB_TOP) / SB_INTER_HEIGHT - 1)
|
||||
|
||||
// Used when interleaved
|
||||
|
||||
|
||||
|
||||
#define SB_LEFT_BOTICON_X (SCOREBOARD_X+0)
|
||||
#define SB_LEFT_HEAD_X (SCOREBOARD_X+32)
|
||||
#define SB_RIGHT_BOTICON_X (SCOREBOARD_X+64)
|
||||
#define SB_RIGHT_HEAD_X (SCOREBOARD_X+96)
|
||||
// Normal
|
||||
#define SB_BOTICON_X (SCOREBOARD_X+32)
|
||||
#define SB_HEAD_X (SCOREBOARD_X+64)
|
||||
|
||||
#define SB_SCORELINE_X 112
|
||||
|
||||
#define SB_RATING_WIDTH (6 * BIGCHAR_WIDTH) // width 6
|
||||
#define SB_SCORE_X (SB_SCORELINE_X + BIGCHAR_WIDTH) // width 6
|
||||
#define SB_RATING_X (SB_SCORELINE_X + 6 * BIGCHAR_WIDTH) // width 6
|
||||
#define SB_PING_X (SB_SCORELINE_X + 12 * BIGCHAR_WIDTH + 8) // width 5
|
||||
#define SB_TIME_X (SB_SCORELINE_X + 17 * BIGCHAR_WIDTH + 8) // width 5
|
||||
#define SB_NAME_X (SB_SCORELINE_X + 22 * BIGCHAR_WIDTH) // width 15
|
||||
|
||||
// The new and improved score board
|
||||
//
|
||||
// In cases where the number of clients is high, the score board heads are interleaved
|
||||
// here's the layout
|
||||
|
||||
//
|
||||
// 0 32 80 112 144 240 320 400 <-- pixel position
|
||||
// bot head bot head score ping time name
|
||||
//
|
||||
// wins/losses are drawn on bot icon now
|
||||
|
||||
static qboolean localClient; // true if local client has been displayed
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_DrawScoreboard
|
||||
=================
|
||||
*/
|
||||
static void CG_DrawClientScore( int y, score_t *score, float *color, float fade, qboolean largeFormat ) {
|
||||
char string[1024];
|
||||
vec3_t headAngles;
|
||||
clientInfo_t *ci;
|
||||
int iconx, headx;
|
||||
|
||||
if ( score->client < 0 || score->client >= cgs.maxclients ) {
|
||||
Com_Printf( "Bad score->client: %i\n", score->client );
|
||||
return;
|
||||
}
|
||||
|
||||
ci = &cgs.clientinfo[score->client];
|
||||
|
||||
iconx = SB_BOTICON_X + (SB_RATING_WIDTH / 2);
|
||||
headx = SB_HEAD_X + (SB_RATING_WIDTH / 2);
|
||||
|
||||
// draw the handicap or bot skill marker (unless player has flag)
|
||||
if ( ci->powerups & ( 1 << PW_NEUTRALFLAG ) ) {
|
||||
if( largeFormat ) {
|
||||
CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_FREE, qfalse );
|
||||
}
|
||||
else {
|
||||
CG_DrawFlagModel( iconx, y, 16, 16, TEAM_FREE, qfalse );
|
||||
}
|
||||
} else if ( ci->powerups & ( 1 << PW_REDFLAG ) ) {
|
||||
if( largeFormat ) {
|
||||
CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_RED, qfalse );
|
||||
}
|
||||
else {
|
||||
CG_DrawFlagModel( iconx, y, 16, 16, TEAM_RED, qfalse );
|
||||
}
|
||||
} else if ( ci->powerups & ( 1 << PW_BLUEFLAG ) ) {
|
||||
if( largeFormat ) {
|
||||
CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_BLUE, qfalse );
|
||||
}
|
||||
else {
|
||||
CG_DrawFlagModel( iconx, y, 16, 16, TEAM_BLUE, qfalse );
|
||||
}
|
||||
} else {
|
||||
if ( ci->botSkill > 0 && ci->botSkill <= 5 ) {
|
||||
if ( cg_drawIcons.integer ) {
|
||||
if( largeFormat ) {
|
||||
CG_DrawPic( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, cgs.media.botSkillShaders[ ci->botSkill - 1 ] );
|
||||
}
|
||||
else {
|
||||
CG_DrawPic( iconx, y, 16, 16, cgs.media.botSkillShaders[ ci->botSkill - 1 ] );
|
||||
}
|
||||
}
|
||||
} else if ( ci->handicap < 100 ) {
|
||||
Com_sprintf( string, sizeof( string ), "%i", ci->handicap );
|
||||
if ( cgs.gametype == GT_TOURNAMENT )
|
||||
CG_DrawSmallStringColor( iconx, y - SMALLCHAR_HEIGHT/2, string, color );
|
||||
else
|
||||
CG_DrawSmallStringColor( iconx, y, string, color );
|
||||
}
|
||||
|
||||
// draw the wins / losses
|
||||
if ( cgs.gametype == GT_TOURNAMENT ) {
|
||||
Com_sprintf( string, sizeof( string ), "%i/%i", ci->wins, ci->losses );
|
||||
if( ci->handicap < 100 && !ci->botSkill ) {
|
||||
CG_DrawSmallStringColor( iconx, y + SMALLCHAR_HEIGHT/2, string, color );
|
||||
}
|
||||
else {
|
||||
CG_DrawSmallStringColor( iconx, y, string, color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draw the face
|
||||
VectorClear( headAngles );
|
||||
headAngles[YAW] = 180;
|
||||
if( largeFormat ) {
|
||||
CG_DrawHead( headx, y - ( ICON_SIZE - BIGCHAR_HEIGHT ) / 2, ICON_SIZE, ICON_SIZE,
|
||||
score->client, headAngles );
|
||||
}
|
||||
else {
|
||||
CG_DrawHead( headx, y, 16, 16, score->client, headAngles );
|
||||
}
|
||||
|
||||
#ifdef MISSIONPACK
|
||||
// draw the team task
|
||||
if ( ci->teamTask != TEAMTASK_NONE ) {
|
||||
if ( ci->teamTask == TEAMTASK_OFFENSE ) {
|
||||
CG_DrawPic( headx + 48, y, 16, 16, cgs.media.assaultShader );
|
||||
}
|
||||
else if ( ci->teamTask == TEAMTASK_DEFENSE ) {
|
||||
CG_DrawPic( headx + 48, y, 16, 16, cgs.media.defendShader );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// draw the score line
|
||||
if ( score->ping == -1 ) {
|
||||
Com_sprintf(string, sizeof(string),
|
||||
" connecting %s", ci->name);
|
||||
} else if ( ci->team == TEAM_SPECTATOR ) {
|
||||
Com_sprintf(string, sizeof(string),
|
||||
" SPECT %3i %4i %s", score->ping, score->time, ci->name);
|
||||
} else {
|
||||
Com_sprintf(string, sizeof(string),
|
||||
"%5i %4i %4i %s", score->score, score->ping, score->time, ci->name);
|
||||
}
|
||||
|
||||
// highlight your position
|
||||
if ( score->client == cg.snap->ps.clientNum ) {
|
||||
float hcolor[4];
|
||||
int rank;
|
||||
|
||||
localClient = qtrue;
|
||||
|
||||
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR
|
||||
|| cgs.gametype >= GT_TEAM ) {
|
||||
rank = -1;
|
||||
} else {
|
||||
rank = cg.snap->ps.persistant[PERS_RANK] & ~RANK_TIED_FLAG;
|
||||
}
|
||||
if ( rank == 0 ) {
|
||||
hcolor[0] = 0;
|
||||
hcolor[1] = 0;
|
||||
hcolor[2] = 0.7f;
|
||||
} else if ( rank == 1 ) {
|
||||
hcolor[0] = 0.7f;
|
||||
hcolor[1] = 0;
|
||||
hcolor[2] = 0;
|
||||
} else if ( rank == 2 ) {
|
||||
hcolor[0] = 0.7f;
|
||||
hcolor[1] = 0.7f;
|
||||
hcolor[2] = 0;
|
||||
} else {
|
||||
hcolor[0] = 0.7f;
|
||||
hcolor[1] = 0.7f;
|
||||
hcolor[2] = 0.7f;
|
||||
}
|
||||
|
||||
hcolor[3] = fade * 0.7;
|
||||
CG_FillRect( SB_SCORELINE_X + BIGCHAR_WIDTH + (SB_RATING_WIDTH / 2), y,
|
||||
640 - SB_SCORELINE_X - BIGCHAR_WIDTH, BIGCHAR_HEIGHT+1, hcolor );
|
||||
}
|
||||
|
||||
CG_DrawBigString( SB_SCORELINE_X + (SB_RATING_WIDTH / 2), y, string, fade );
|
||||
|
||||
// add the "ready" marker for intermission exiting
|
||||
if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << score->client ) ) {
|
||||
CG_DrawBigStringColor( iconx, y, "READY", color );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_TeamScoreboard
|
||||
=================
|
||||
*/
|
||||
static int CG_TeamScoreboard( int y, team_t team, float fade, int maxClients, int lineHeight ) {
|
||||
int i;
|
||||
score_t *score;
|
||||
float color[4];
|
||||
int count;
|
||||
clientInfo_t *ci;
|
||||
|
||||
color[0] = color[1] = color[2] = 1.0;
|
||||
color[3] = fade;
|
||||
|
||||
count = 0;
|
||||
for ( i = 0 ; i < cg.numScores && count < maxClients ; i++ ) {
|
||||
score = &cg.scores[i];
|
||||
ci = &cgs.clientinfo[ score->client ];
|
||||
|
||||
if ( team != ci->team ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CG_DrawClientScore( y + lineHeight * count, score, color, fade, lineHeight == SB_NORMAL_HEIGHT );
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_DrawScoreboard
|
||||
|
||||
Draw the normal in-game scoreboard
|
||||
=================
|
||||
*/
|
||||
qboolean CG_DrawOldScoreboard( void ) {
|
||||
int x, y, w, i, n1, n2;
|
||||
float fade;
|
||||
float *fadeColor;
|
||||
char *s;
|
||||
int maxClients;
|
||||
int lineHeight;
|
||||
int topBorderSize, bottomBorderSize;
|
||||
|
||||
// don't draw amuthing if the menu or console is up
|
||||
if ( cg_paused.integer ) {
|
||||
cg.deferredPlayerLoading = 0;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
|
||||
cg.deferredPlayerLoading = 0;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// don't draw scoreboard during death while warmup up
|
||||
if ( cg.warmup && !cg.showScores ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD ||
|
||||
cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
|
||||
fade = 1.0;
|
||||
fadeColor = colorWhite;
|
||||
} else {
|
||||
fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME );
|
||||
|
||||
if ( !fadeColor ) {
|
||||
// next time scoreboard comes up, don't print killer
|
||||
cg.deferredPlayerLoading = 0;
|
||||
cg.killerName[0] = 0;
|
||||
return qfalse;
|
||||
}
|
||||
fade = *fadeColor;
|
||||
}
|
||||
|
||||
|
||||
// fragged by ... line
|
||||
if ( cg.killerName[0] ) {
|
||||
s = va("Fragged by %s", cg.killerName );
|
||||
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
|
||||
x = ( SCREEN_WIDTH - w ) / 2;
|
||||
y = 40;
|
||||
CG_DrawBigString( x, y, s, fade );
|
||||
}
|
||||
|
||||
// current rank
|
||||
if ( cgs.gametype < GT_TEAM) {
|
||||
if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) {
|
||||
s = va("%s place with %i",
|
||||
CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ),
|
||||
cg.snap->ps.persistant[PERS_SCORE] );
|
||||
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
|
||||
x = ( SCREEN_WIDTH - w ) / 2;
|
||||
y = 60;
|
||||
CG_DrawBigString( x, y, s, fade );
|
||||
}
|
||||
} else {
|
||||
if ( cg.teamScores[0] == cg.teamScores[1] ) {
|
||||
s = va("Teams are tied at %i", cg.teamScores[0] );
|
||||
} else if ( cg.teamScores[0] >= cg.teamScores[1] ) {
|
||||
s = va("Red leads %i to %i",cg.teamScores[0], cg.teamScores[1] );
|
||||
} else {
|
||||
s = va("Blue leads %i to %i",cg.teamScores[1], cg.teamScores[0] );
|
||||
}
|
||||
|
||||
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
|
||||
x = ( SCREEN_WIDTH - w ) / 2;
|
||||
y = 60;
|
||||
CG_DrawBigString( x, y, s, fade );
|
||||
}
|
||||
|
||||
// scoreboard
|
||||
y = SB_HEADER;
|
||||
|
||||
CG_DrawPic( SB_SCORE_X + (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardScore );
|
||||
CG_DrawPic( SB_PING_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardPing );
|
||||
CG_DrawPic( SB_TIME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardTime );
|
||||
CG_DrawPic( SB_NAME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardName );
|
||||
|
||||
y = SB_TOP;
|
||||
|
||||
// If there are more than SB_MAXCLIENTS_NORMAL, use the interleaved scores
|
||||
if ( cg.numScores > SB_MAXCLIENTS_NORMAL ) {
|
||||
maxClients = SB_MAXCLIENTS_INTER;
|
||||
lineHeight = SB_INTER_HEIGHT;
|
||||
topBorderSize = 8;
|
||||
bottomBorderSize = 16;
|
||||
} else {
|
||||
maxClients = SB_MAXCLIENTS_NORMAL;
|
||||
lineHeight = SB_NORMAL_HEIGHT;
|
||||
topBorderSize = 16;
|
||||
bottomBorderSize = 16;
|
||||
}
|
||||
|
||||
localClient = qfalse;
|
||||
|
||||
if ( cgs.gametype >= GT_TEAM ) {
|
||||
//
|
||||
// teamplay scoreboard
|
||||
//
|
||||
y += lineHeight/2;
|
||||
|
||||
if ( cg.teamScores[0] >= cg.teamScores[1] ) {
|
||||
n1 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight );
|
||||
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED );
|
||||
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
|
||||
maxClients -= n1;
|
||||
n2 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight );
|
||||
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE );
|
||||
y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
|
||||
maxClients -= n2;
|
||||
} else {
|
||||
n1 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight );
|
||||
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE );
|
||||
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
|
||||
maxClients -= n1;
|
||||
n2 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight );
|
||||
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED );
|
||||
y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
|
||||
maxClients -= n2;
|
||||
}
|
||||
n1 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients, lineHeight );
|
||||
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
|
||||
|
||||
} else {
|
||||
//
|
||||
// free for all scoreboard
|
||||
//
|
||||
n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight );
|
||||
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
|
||||
n2 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients - n1, lineHeight );
|
||||
y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
|
||||
}
|
||||
|
||||
if (!localClient) {
|
||||
// draw local client at the bottom
|
||||
for ( i = 0 ; i < cg.numScores ; i++ ) {
|
||||
if ( cg.scores[i].client == cg.snap->ps.clientNum ) {
|
||||
CG_DrawClientScore( y, &cg.scores[i], fadeColor, fade, lineHeight == SB_NORMAL_HEIGHT );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load any models that have been deferred
|
||||
if ( ++cg.deferredPlayerLoading > 10 ) {
|
||||
CG_LoadDeferredPlayers();
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
|
||||
/*
|
||||
================
|
||||
CG_CenterGiantLine
|
||||
================
|
||||
*/
|
||||
static void CG_CenterGiantLine( float y, const char *string ) {
|
||||
float x;
|
||||
vec4_t color;
|
||||
|
||||
color[0] = 1;
|
||||
color[1] = 1;
|
||||
color[2] = 1;
|
||||
color[3] = 1;
|
||||
|
||||
x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( string ) );
|
||||
|
||||
CG_DrawStringExt( x, y, string, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_DrawTourneyScoreboard
|
||||
|
||||
Draw the oversize scoreboard for tournements
|
||||
=================
|
||||
*/
|
||||
void CG_DrawOldTourneyScoreboard( void ) {
|
||||
const char *s;
|
||||
vec4_t color;
|
||||
int min, tens, ones;
|
||||
clientInfo_t *ci;
|
||||
int y;
|
||||
int i;
|
||||
|
||||
// request more scores regularly
|
||||
if ( cg.scoresRequestTime + 2000 < cg.time ) {
|
||||
cg.scoresRequestTime = cg.time;
|
||||
trap_SendClientCommand( "score" );
|
||||
}
|
||||
|
||||
color[0] = 1;
|
||||
color[1] = 1;
|
||||
color[2] = 1;
|
||||
color[3] = 1;
|
||||
|
||||
// draw the dialog background
|
||||
color[0] = color[1] = color[2] = 0;
|
||||
color[3] = 1;
|
||||
CG_FillRect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, color );
|
||||
|
||||
// print the mesage of the day
|
||||
s = CG_ConfigString( CS_MOTD );
|
||||
if ( !s[0] ) {
|
||||
s = "Scoreboard";
|
||||
}
|
||||
|
||||
// print optional title
|
||||
CG_CenterGiantLine( 8, s );
|
||||
|
||||
// print server time
|
||||
ones = cg.time / 1000;
|
||||
min = ones / 60;
|
||||
ones %= 60;
|
||||
tens = ones / 10;
|
||||
ones %= 10;
|
||||
s = va("%i:%i%i", min, tens, ones );
|
||||
|
||||
CG_CenterGiantLine( 64, s );
|
||||
|
||||
|
||||
// print the two scores
|
||||
|
||||
y = 160;
|
||||
if ( cgs.gametype >= GT_TEAM ) {
|
||||
//
|
||||
// teamplay scoreboard
|
||||
//
|
||||
CG_DrawStringExt( 8, y, "Red Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
|
||||
s = va("%i", cg.teamScores[0] );
|
||||
CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
|
||||
|
||||
y += 64;
|
||||
|
||||
CG_DrawStringExt( 8, y, "Blue Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
|
||||
s = va("%i", cg.teamScores[1] );
|
||||
CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
|
||||
} else {
|
||||
//
|
||||
// free for all scoreboard
|
||||
//
|
||||
for ( i = 0 ; i < MAX_CLIENTS ; i++ ) {
|
||||
ci = &cgs.clientinfo[i];
|
||||
if ( !ci->infoValid ) {
|
||||
continue;
|
||||
}
|
||||
if ( ci->team != TEAM_FREE ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CG_DrawStringExt( 8, y, ci->name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
|
||||
s = va("%i", ci->score );
|
||||
CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
|
||||
y += 64;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
1098
code/cgame/cg_servercmds.c
Normal file
1098
code/cgame/cg_servercmds.c
Normal file
File diff suppressed because it is too large
Load diff
403
code/cgame/cg_snapshot.c
Normal file
403
code/cgame/cg_snapshot.c
Normal file
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_snapshot.c -- things that happen on snapshot transition,
|
||||
// not necessarily every single rendered frame
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_ResetEntity
|
||||
==================
|
||||
*/
|
||||
static void CG_ResetEntity( centity_t *cent ) {
|
||||
// if the previous snapshot this entity was updated in is at least
|
||||
// an event window back in time then we can reset the previous event
|
||||
if ( cent->snapShotTime < cg.time - EVENT_VALID_MSEC ) {
|
||||
cent->previousEvent = 0;
|
||||
}
|
||||
|
||||
cent->trailTime = cg.snap->serverTime;
|
||||
|
||||
VectorCopy (cent->currentState.origin, cent->lerpOrigin);
|
||||
VectorCopy (cent->currentState.angles, cent->lerpAngles);
|
||||
if ( cent->currentState.eType == ET_PLAYER ) {
|
||||
CG_ResetPlayerEntity( cent );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_TransitionEntity
|
||||
|
||||
cent->nextState is moved to cent->currentState and events are fired
|
||||
===============
|
||||
*/
|
||||
static void CG_TransitionEntity( centity_t *cent ) {
|
||||
cent->currentState = cent->nextState;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
// reset if the entity wasn't in the last frame or was teleported
|
||||
if ( !cent->interpolate ) {
|
||||
CG_ResetEntity( cent );
|
||||
}
|
||||
|
||||
// clear the next state. if will be set by the next CG_SetNextSnap
|
||||
cent->interpolate = qfalse;
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_SetInitialSnapshot
|
||||
|
||||
This will only happen on the very first snapshot, or
|
||||
on tourney restarts. All other times will use
|
||||
CG_TransitionSnapshot instead.
|
||||
|
||||
FIXME: Also called by map_restart?
|
||||
==================
|
||||
*/
|
||||
void CG_SetInitialSnapshot( snapshot_t *snap ) {
|
||||
int i;
|
||||
centity_t *cent;
|
||||
entityState_t *state;
|
||||
|
||||
cg.snap = snap;
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList();
|
||||
|
||||
CG_ExecuteNewServerCommands( snap->serverCommandSequence );
|
||||
|
||||
// set our local weapon selection pointer to
|
||||
// what the server has indicated the current weapon is
|
||||
CG_Respawn();
|
||||
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
state = &cg.snap->entities[ i ];
|
||||
cent = &cg_entities[ state->number ];
|
||||
|
||||
memcpy(¢->currentState, state, sizeof(entityState_t));
|
||||
//cent->currentState = *state;
|
||||
cent->interpolate = qfalse;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
CG_ResetEntity( cent );
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_TransitionSnapshot
|
||||
|
||||
The transition point from snap to nextSnap has passed
|
||||
===================
|
||||
*/
|
||||
static void CG_TransitionSnapshot( void ) {
|
||||
centity_t *cent;
|
||||
snapshot_t *oldFrame;
|
||||
int i;
|
||||
|
||||
if ( !cg.snap ) {
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
|
||||
}
|
||||
if ( !cg.nextSnap ) {
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
|
||||
}
|
||||
|
||||
// execute any server string commands before transitioning entities
|
||||
CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
|
||||
|
||||
// if we had a map_restart, set everthing with initial
|
||||
if ( !cg.snap ) {
|
||||
}
|
||||
|
||||
// clear the currentValid flag for all entities in the existing snapshot
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
cent->currentValid = qfalse;
|
||||
}
|
||||
|
||||
// move nextSnap to snap and do the transitions
|
||||
oldFrame = cg.snap;
|
||||
cg.snap = cg.nextSnap;
|
||||
|
||||
BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;
|
||||
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
CG_TransitionEntity( cent );
|
||||
|
||||
// remember time of snapshot this entity was last updated in
|
||||
cent->snapShotTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
cg.nextSnap = NULL;
|
||||
|
||||
// check for playerstate transition events
|
||||
if ( oldFrame ) {
|
||||
playerState_t *ops, *ps;
|
||||
|
||||
ops = &oldFrame->ps;
|
||||
ps = &cg.snap->ps;
|
||||
// teleporting checks are irrespective of prediction
|
||||
if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
|
||||
cg.thisFrameTeleport = qtrue; // will be cleared by prediction code
|
||||
}
|
||||
|
||||
// if we are not doing client side movement prediction for any
|
||||
// reason, then the client events and view changes will be issued now
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)
|
||||
|| cg_nopredict.integer || cg_synchronousClients.integer ) {
|
||||
CG_TransitionPlayerState( ps, ops );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_SetNextSnap
|
||||
|
||||
A new snapshot has just been read in from the client system.
|
||||
===================
|
||||
*/
|
||||
static void CG_SetNextSnap( snapshot_t *snap ) {
|
||||
int num;
|
||||
entityState_t *es;
|
||||
centity_t *cent;
|
||||
|
||||
cg.nextSnap = snap;
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
|
||||
|
||||
// check for extrapolation errors
|
||||
for ( num = 0 ; num < snap->numEntities ; num++ ) {
|
||||
es = &snap->entities[num];
|
||||
cent = &cg_entities[ es->number ];
|
||||
|
||||
memcpy(¢->nextState, es, sizeof(entityState_t));
|
||||
//cent->nextState = *es;
|
||||
|
||||
// if this frame is a teleport, or the entity wasn't in the
|
||||
// previous frame, don't interpolate
|
||||
if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) {
|
||||
cent->interpolate = qfalse;
|
||||
} else {
|
||||
cent->interpolate = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
// if the next frame is a teleport for the playerstate, we
|
||||
// can't interpolate during demos
|
||||
if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
} else {
|
||||
cg.nextFrameTeleport = qfalse;
|
||||
}
|
||||
|
||||
// if changing follow mode, don't interpolate
|
||||
if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// if changing server restarts, don't interpolate
|
||||
if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
CG_ReadNextSnapshot
|
||||
|
||||
This is the only place new snapshots are requested
|
||||
This may increment cgs.processedSnapshotNum multiple
|
||||
times if the client system fails to return a
|
||||
valid snapshot.
|
||||
========================
|
||||
*/
|
||||
static snapshot_t *CG_ReadNextSnapshot( void ) {
|
||||
qboolean r;
|
||||
snapshot_t *dest;
|
||||
|
||||
if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) {
|
||||
CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
|
||||
cg.latestSnapshotNum, cgs.processedSnapshotNum );
|
||||
}
|
||||
|
||||
while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) {
|
||||
// decide which of the two slots to load it into
|
||||
if ( cg.snap == &cg.activeSnapshots[0] ) {
|
||||
dest = &cg.activeSnapshots[1];
|
||||
} else {
|
||||
dest = &cg.activeSnapshots[0];
|
||||
}
|
||||
|
||||
// try to read the snapshot from the client system
|
||||
cgs.processedSnapshotNum++;
|
||||
r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
|
||||
|
||||
// FIXME: why would trap_GetSnapshot return a snapshot with the same server time
|
||||
if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) {
|
||||
//continue;
|
||||
}
|
||||
|
||||
// if it succeeded, return
|
||||
if ( r ) {
|
||||
CG_AddLagometerSnapshotInfo( dest );
|
||||
return dest;
|
||||
}
|
||||
|
||||
// a GetSnapshot will return failure if the snapshot
|
||||
// never arrived, or is so old that its entities
|
||||
// have been shoved off the end of the circular
|
||||
// buffer in the client system.
|
||||
|
||||
// record as a dropped packet
|
||||
CG_AddLagometerSnapshotInfo( NULL );
|
||||
|
||||
// If there are additional snapshots, continue trying to
|
||||
// read them.
|
||||
}
|
||||
|
||||
// nothing left to read
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
CG_ProcessSnapshots
|
||||
|
||||
We are trying to set up a renderable view, so determine
|
||||
what the simulated time is, and try to get snapshots
|
||||
both before and after that time if available.
|
||||
|
||||
If we don't have a valid cg.snap after exiting this function,
|
||||
then a 3D game view cannot be rendered. This should only happen
|
||||
right after the initial connection. After cg.snap has been valid
|
||||
once, it will never turn invalid.
|
||||
|
||||
Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
|
||||
hasn't arrived yet (it becomes an extrapolating situation instead
|
||||
of an interpolating one)
|
||||
|
||||
============
|
||||
*/
|
||||
void CG_ProcessSnapshots( void ) {
|
||||
snapshot_t *snap;
|
||||
int n;
|
||||
|
||||
// see what the latest snapshot the client system has is
|
||||
trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
|
||||
if ( n != cg.latestSnapshotNum ) {
|
||||
if ( n < cg.latestSnapshotNum ) {
|
||||
// this should never happen
|
||||
CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
|
||||
}
|
||||
cg.latestSnapshotNum = n;
|
||||
}
|
||||
|
||||
// If we have yet to receive a snapshot, check for it.
|
||||
// Once we have gotten the first snapshot, cg.snap will
|
||||
// always have valid data for the rest of the game
|
||||
while ( !cg.snap ) {
|
||||
snap = CG_ReadNextSnapshot();
|
||||
if ( !snap ) {
|
||||
// we can't continue until we get a snapshot
|
||||
return;
|
||||
}
|
||||
|
||||
// set our weapon selection to what
|
||||
// the playerstate is currently using
|
||||
if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
|
||||
CG_SetInitialSnapshot( snap );
|
||||
}
|
||||
}
|
||||
|
||||
// loop until we either have a valid nextSnap with a serverTime
|
||||
// greater than cg.time to interpolate towards, or we run
|
||||
// out of available snapshots
|
||||
do {
|
||||
// if we don't have a nextframe, try and read a new one in
|
||||
if ( !cg.nextSnap ) {
|
||||
snap = CG_ReadNextSnapshot();
|
||||
|
||||
// if we still don't have a nextframe, we will just have to
|
||||
// extrapolate
|
||||
if ( !snap ) {
|
||||
break;
|
||||
}
|
||||
|
||||
CG_SetNextSnap( snap );
|
||||
|
||||
|
||||
// if time went backwards, we have a level restart
|
||||
if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
|
||||
CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
|
||||
}
|
||||
}
|
||||
|
||||
// if our time is < nextFrame's, we have a nice interpolating state
|
||||
if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// we have passed the transition from nextFrame to frame
|
||||
CG_TransitionSnapshot();
|
||||
} while ( 1 );
|
||||
|
||||
// assert our valid conditions upon exiting
|
||||
if ( cg.snap == NULL ) {
|
||||
CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
|
||||
}
|
||||
if ( cg.time < cg.snap->serverTime ) {
|
||||
// this can happen right after a vid_restart
|
||||
cg.time = cg.snap->serverTime;
|
||||
}
|
||||
if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) {
|
||||
CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
|
||||
}
|
||||
|
||||
}
|
||||
|
106
code/cgame/cg_syscalls.asm
Normal file
106
code/cgame/cg_syscalls.asm
Normal file
|
@ -0,0 +1,106 @@
|
|||
code
|
||||
|
||||
equ trap_Print -1
|
||||
equ trap_Error -2
|
||||
equ trap_Milliseconds -3
|
||||
equ trap_Cvar_Register -4
|
||||
equ trap_Cvar_Update -5
|
||||
equ trap_Cvar_Set -6
|
||||
equ trap_Cvar_VariableStringBuffer -7
|
||||
equ trap_Argc -8
|
||||
equ trap_Argv -9
|
||||
equ trap_Args -10
|
||||
equ trap_FS_FOpenFile -11
|
||||
equ trap_FS_Read -12
|
||||
equ trap_FS_Write -13
|
||||
equ trap_FS_FCloseFile -14
|
||||
equ trap_SendConsoleCommand -15
|
||||
equ trap_AddCommand -16
|
||||
equ trap_SendClientCommand -17
|
||||
equ trap_UpdateScreen -18
|
||||
equ trap_CM_LoadMap -19
|
||||
equ trap_CM_NumInlineModels -20
|
||||
equ trap_CM_InlineModel -21
|
||||
equ trap_CM_LoadModel -22
|
||||
equ trap_CM_TempBoxModel -23
|
||||
equ trap_CM_PointContents -24
|
||||
equ trap_CM_TransformedPointContents -25
|
||||
equ trap_CM_BoxTrace -26
|
||||
equ trap_CM_TransformedBoxTrace -27
|
||||
equ trap_CM_MarkFragments -28
|
||||
equ trap_S_StartSound -29
|
||||
equ trap_S_StartLocalSound -30
|
||||
equ trap_S_ClearLoopingSounds -31
|
||||
equ trap_S_AddLoopingSound -32
|
||||
equ trap_S_UpdateEntityPosition -33
|
||||
equ trap_S_Respatialize -34
|
||||
equ trap_S_RegisterSound -35
|
||||
equ trap_S_StartBackgroundTrack -36
|
||||
equ trap_R_LoadWorldMap -37
|
||||
equ trap_R_RegisterModel -38
|
||||
equ trap_R_RegisterSkin -39
|
||||
equ trap_R_RegisterShader -40
|
||||
equ trap_R_ClearScene -41
|
||||
equ trap_R_AddRefEntityToScene -42
|
||||
equ trap_R_AddPolyToScene -43
|
||||
equ trap_R_AddLightToScene -44
|
||||
equ trap_R_RenderScene -45
|
||||
equ trap_R_SetColor -46
|
||||
equ trap_R_DrawStretchPic -47
|
||||
equ trap_R_ModelBounds -48
|
||||
equ trap_R_LerpTag -49
|
||||
equ trap_GetGlconfig -50
|
||||
equ trap_GetGameState -51
|
||||
equ trap_GetCurrentSnapshotNumber -52
|
||||
equ trap_GetSnapshot -53
|
||||
equ trap_GetServerCommand -54
|
||||
equ trap_GetCurrentCmdNumber -55
|
||||
equ trap_GetUserCmd -56
|
||||
equ trap_SetUserCmdValue -57
|
||||
equ trap_R_RegisterShaderNoMip -58
|
||||
equ trap_MemoryRemaining -59
|
||||
equ trap_R_RegisterFont -60
|
||||
equ trap_Key_IsDown -61
|
||||
equ trap_Key_GetCatcher -62
|
||||
equ trap_Key_SetCatcher -63
|
||||
equ trap_Key_GetKey -64
|
||||
equ trap_PC_AddGlobalDefine -65
|
||||
equ trap_PC_LoadSource -66
|
||||
equ trap_PC_FreeSource -67
|
||||
equ trap_PC_ReadToken -68
|
||||
equ trap_PC_SourceFileAndLine -69
|
||||
equ trap_S_StopBackgroundTrack -70
|
||||
equ trap_RealTime -71
|
||||
equ trap_SnapVector -72
|
||||
equ trap_RemoveCommand -73
|
||||
equ trap_R_LightForPoint -74
|
||||
equ trap_CIN_PlayCinematic -75
|
||||
equ trap_CIN_StopCinematic -76
|
||||
equ trap_CIN_RunCinematic -77
|
||||
equ trap_CIN_DrawCinematic -78
|
||||
equ trap_CIN_SetExtents -79
|
||||
equ trap_R_RemapShader -80
|
||||
equ trap_S_AddRealLoopingSound -81
|
||||
equ trap_S_StopLoopingSound -82
|
||||
equ trap_CM_TempCapsuleModel -83
|
||||
equ trap_CM_CapsuleTrace -84
|
||||
equ trap_CM_TransformedCapsuleTrace -85
|
||||
equ trap_R_AddAdditiveLightToScene -86
|
||||
equ trap_GetEntityToken -87
|
||||
equ trap_R_AddPolysToScene -88
|
||||
equ trap_R_inPVS -89
|
||||
equ trap_FS_Seek -90
|
||||
|
||||
equ memset -101
|
||||
equ memcpy -102
|
||||
equ strncpy -103
|
||||
equ sin -104
|
||||
equ cos -105
|
||||
equ atan2 -106
|
||||
equ sqrt -107
|
||||
equ floor -108
|
||||
equ ceil -109
|
||||
equ testPrintInt -110
|
||||
equ testPrintFloat -111
|
||||
equ acos -112
|
||||
|
445
code/cgame/cg_syscalls.c
Normal file
445
code/cgame/cg_syscalls.c
Normal file
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_syscalls.c -- this file is only included when building a dll
|
||||
// cg_syscalls.asm is included instead when building a qvm
|
||||
#ifdef Q3_VM
|
||||
#error "Do not use in VM build"
|
||||
#endif
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
static int (QDECL *syscall)( int arg, ... ) = (int (QDECL *)( int, ...))-1;
|
||||
|
||||
|
||||
void dllEntry( int (QDECL *syscallptr)( int arg,... ) ) {
|
||||
syscall = syscallptr;
|
||||
}
|
||||
|
||||
|
||||
int PASSFLOAT( float x ) {
|
||||
float floatTemp;
|
||||
floatTemp = x;
|
||||
return *(int *)&floatTemp;
|
||||
}
|
||||
|
||||
void trap_Print( const char *fmt ) {
|
||||
syscall( CG_PRINT, fmt );
|
||||
}
|
||||
|
||||
void trap_Error( const char *fmt ) {
|
||||
syscall( CG_ERROR, fmt );
|
||||
}
|
||||
|
||||
int trap_Milliseconds( void ) {
|
||||
return syscall( CG_MILLISECONDS );
|
||||
}
|
||||
|
||||
void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) {
|
||||
syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags );
|
||||
}
|
||||
|
||||
void trap_Cvar_Update( vmCvar_t *vmCvar ) {
|
||||
syscall( CG_CVAR_UPDATE, vmCvar );
|
||||
}
|
||||
|
||||
void trap_Cvar_Set( const char *var_name, const char *value ) {
|
||||
syscall( CG_CVAR_SET, var_name, value );
|
||||
}
|
||||
|
||||
void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) {
|
||||
syscall( CG_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize );
|
||||
}
|
||||
|
||||
int trap_Argc( void ) {
|
||||
return syscall( CG_ARGC );
|
||||
}
|
||||
|
||||
void trap_Argv( int n, char *buffer, int bufferLength ) {
|
||||
syscall( CG_ARGV, n, buffer, bufferLength );
|
||||
}
|
||||
|
||||
void trap_Args( char *buffer, int bufferLength ) {
|
||||
syscall( CG_ARGS, buffer, bufferLength );
|
||||
}
|
||||
|
||||
int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) {
|
||||
return syscall( CG_FS_FOPENFILE, qpath, f, mode );
|
||||
}
|
||||
|
||||
void trap_FS_Read( void *buffer, int len, fileHandle_t f ) {
|
||||
syscall( CG_FS_READ, buffer, len, f );
|
||||
}
|
||||
|
||||
void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) {
|
||||
syscall( CG_FS_WRITE, buffer, len, f );
|
||||
}
|
||||
|
||||
void trap_FS_FCloseFile( fileHandle_t f ) {
|
||||
syscall( CG_FS_FCLOSEFILE, f );
|
||||
}
|
||||
|
||||
int trap_FS_Seek( fileHandle_t f, long offset, int origin ) {
|
||||
return syscall( CG_FS_SEEK, f, offset, origin );
|
||||
}
|
||||
|
||||
void trap_SendConsoleCommand( const char *text ) {
|
||||
syscall( CG_SENDCONSOLECOMMAND, text );
|
||||
}
|
||||
|
||||
void trap_AddCommand( const char *cmdName ) {
|
||||
syscall( CG_ADDCOMMAND, cmdName );
|
||||
}
|
||||
|
||||
void trap_RemoveCommand( const char *cmdName ) {
|
||||
syscall( CG_REMOVECOMMAND, cmdName );
|
||||
}
|
||||
|
||||
void trap_SendClientCommand( const char *s ) {
|
||||
syscall( CG_SENDCLIENTCOMMAND, s );
|
||||
}
|
||||
|
||||
void trap_UpdateScreen( void ) {
|
||||
syscall( CG_UPDATESCREEN );
|
||||
}
|
||||
|
||||
void trap_CM_LoadMap( const char *mapname ) {
|
||||
syscall( CG_CM_LOADMAP, mapname );
|
||||
}
|
||||
|
||||
int trap_CM_NumInlineModels( void ) {
|
||||
return syscall( CG_CM_NUMINLINEMODELS );
|
||||
}
|
||||
|
||||
clipHandle_t trap_CM_InlineModel( int index ) {
|
||||
return syscall( CG_CM_INLINEMODEL, index );
|
||||
}
|
||||
|
||||
clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ) {
|
||||
return syscall( CG_CM_TEMPBOXMODEL, mins, maxs );
|
||||
}
|
||||
|
||||
clipHandle_t trap_CM_TempCapsuleModel( const vec3_t mins, const vec3_t maxs ) {
|
||||
return syscall( CG_CM_TEMPCAPSULEMODEL, mins, maxs );
|
||||
}
|
||||
|
||||
int trap_CM_PointContents( const vec3_t p, clipHandle_t model ) {
|
||||
return syscall( CG_CM_POINTCONTENTS, p, model );
|
||||
}
|
||||
|
||||
int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) {
|
||||
return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles );
|
||||
}
|
||||
|
||||
void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask ) {
|
||||
syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask );
|
||||
}
|
||||
|
||||
void trap_CM_CapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask ) {
|
||||
syscall( CG_CM_CAPSULETRACE, results, start, end, mins, maxs, model, brushmask );
|
||||
}
|
||||
|
||||
void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask,
|
||||
const vec3_t origin, const vec3_t angles ) {
|
||||
syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles );
|
||||
}
|
||||
|
||||
void trap_CM_TransformedCapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end,
|
||||
const vec3_t mins, const vec3_t maxs,
|
||||
clipHandle_t model, int brushmask,
|
||||
const vec3_t origin, const vec3_t angles ) {
|
||||
syscall( CG_CM_TRANSFORMEDCAPSULETRACE, results, start, end, mins, maxs, model, brushmask, origin, angles );
|
||||
}
|
||||
|
||||
int trap_CM_MarkFragments( int numPoints, const vec3_t *points,
|
||||
const vec3_t projection,
|
||||
int maxPoints, vec3_t pointBuffer,
|
||||
int maxFragments, markFragment_t *fragmentBuffer ) {
|
||||
return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints, pointBuffer, maxFragments, fragmentBuffer );
|
||||
}
|
||||
|
||||
void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) {
|
||||
syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx );
|
||||
}
|
||||
|
||||
void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) {
|
||||
syscall( CG_S_STARTLOCALSOUND, sfx, channelNum );
|
||||
}
|
||||
|
||||
void trap_S_ClearLoopingSounds( qboolean killall ) {
|
||||
syscall( CG_S_CLEARLOOPINGSOUNDS, killall );
|
||||
}
|
||||
|
||||
void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) {
|
||||
syscall( CG_S_ADDLOOPINGSOUND, entityNum, origin, velocity, sfx );
|
||||
}
|
||||
|
||||
void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) {
|
||||
syscall( CG_S_ADDREALLOOPINGSOUND, entityNum, origin, velocity, sfx );
|
||||
}
|
||||
|
||||
void trap_S_StopLoopingSound( int entityNum ) {
|
||||
syscall( CG_S_STOPLOOPINGSOUND, entityNum );
|
||||
}
|
||||
|
||||
void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
|
||||
syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin );
|
||||
}
|
||||
|
||||
void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ) {
|
||||
syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater );
|
||||
}
|
||||
|
||||
sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) {
|
||||
return syscall( CG_S_REGISTERSOUND, sample, compressed );
|
||||
}
|
||||
|
||||
void trap_S_StartBackgroundTrack( const char *intro, const char *loop ) {
|
||||
syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop );
|
||||
}
|
||||
|
||||
void trap_R_LoadWorldMap( const char *mapname ) {
|
||||
syscall( CG_R_LOADWORLDMAP, mapname );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterModel( const char *name ) {
|
||||
return syscall( CG_R_REGISTERMODEL, name );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterSkin( const char *name ) {
|
||||
return syscall( CG_R_REGISTERSKIN, name );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterShader( const char *name ) {
|
||||
return syscall( CG_R_REGISTERSHADER, name );
|
||||
}
|
||||
|
||||
qhandle_t trap_R_RegisterShaderNoMip( const char *name ) {
|
||||
return syscall( CG_R_REGISTERSHADERNOMIP, name );
|
||||
}
|
||||
|
||||
void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) {
|
||||
syscall(CG_R_REGISTERFONT, fontName, pointSize, font );
|
||||
}
|
||||
|
||||
void trap_R_ClearScene( void ) {
|
||||
syscall( CG_R_CLEARSCENE );
|
||||
}
|
||||
|
||||
void trap_R_AddRefEntityToScene( const refEntity_t *re ) {
|
||||
syscall( CG_R_ADDREFENTITYTOSCENE, re );
|
||||
}
|
||||
|
||||
void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) {
|
||||
syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts );
|
||||
}
|
||||
|
||||
void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ) {
|
||||
syscall( CG_R_ADDPOLYSTOSCENE, hShader, numVerts, verts, num );
|
||||
}
|
||||
|
||||
int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) {
|
||||
return syscall( CG_R_LIGHTFORPOINT, point, ambientLight, directedLight, lightDir );
|
||||
}
|
||||
|
||||
void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
|
||||
syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) );
|
||||
}
|
||||
|
||||
void trap_R_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
|
||||
syscall( CG_R_ADDADDITIVELIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) );
|
||||
}
|
||||
|
||||
void trap_R_RenderScene( const refdef_t *fd ) {
|
||||
syscall( CG_R_RENDERSCENE, fd );
|
||||
}
|
||||
|
||||
void trap_R_SetColor( const float *rgba ) {
|
||||
syscall( CG_R_SETCOLOR, rgba );
|
||||
}
|
||||
|
||||
void trap_R_DrawStretchPic( float x, float y, float w, float h,
|
||||
float s1, float t1, float s2, float t2, qhandle_t hShader ) {
|
||||
syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader );
|
||||
}
|
||||
|
||||
void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) {
|
||||
syscall( CG_R_MODELBOUNDS, model, mins, maxs );
|
||||
}
|
||||
|
||||
int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame,
|
||||
float frac, const char *tagName ) {
|
||||
return syscall( CG_R_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName );
|
||||
}
|
||||
|
||||
void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) {
|
||||
syscall( CG_R_REMAP_SHADER, oldShader, newShader, timeOffset );
|
||||
}
|
||||
|
||||
void trap_GetGlconfig( glconfig_t *glconfig ) {
|
||||
syscall( CG_GETGLCONFIG, glconfig );
|
||||
}
|
||||
|
||||
void trap_GetGameState( gameState_t *gamestate ) {
|
||||
syscall( CG_GETGAMESTATE, gamestate );
|
||||
}
|
||||
|
||||
void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) {
|
||||
syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime );
|
||||
}
|
||||
|
||||
qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) {
|
||||
return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot );
|
||||
}
|
||||
|
||||
qboolean trap_GetServerCommand( int serverCommandNumber ) {
|
||||
return syscall( CG_GETSERVERCOMMAND, serverCommandNumber );
|
||||
}
|
||||
|
||||
int trap_GetCurrentCmdNumber( void ) {
|
||||
return syscall( CG_GETCURRENTCMDNUMBER );
|
||||
}
|
||||
|
||||
qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) {
|
||||
return syscall( CG_GETUSERCMD, cmdNumber, ucmd );
|
||||
}
|
||||
|
||||
void trap_SetUserCmdValue( int stateValue, float sensitivityScale ) {
|
||||
syscall( CG_SETUSERCMDVALUE, stateValue, PASSFLOAT(sensitivityScale) );
|
||||
}
|
||||
|
||||
void testPrintInt( char *string, int i ) {
|
||||
syscall( CG_TESTPRINTINT, string, i );
|
||||
}
|
||||
|
||||
void testPrintFloat( char *string, float f ) {
|
||||
syscall( CG_TESTPRINTFLOAT, string, PASSFLOAT(f) );
|
||||
}
|
||||
|
||||
int trap_MemoryRemaining( void ) {
|
||||
return syscall( CG_MEMORY_REMAINING );
|
||||
}
|
||||
|
||||
qboolean trap_Key_IsDown( int keynum ) {
|
||||
return syscall( CG_KEY_ISDOWN, keynum );
|
||||
}
|
||||
|
||||
int trap_Key_GetCatcher( void ) {
|
||||
return syscall( CG_KEY_GETCATCHER );
|
||||
}
|
||||
|
||||
void trap_Key_SetCatcher( int catcher ) {
|
||||
syscall( CG_KEY_SETCATCHER, catcher );
|
||||
}
|
||||
|
||||
int trap_Key_GetKey( const char *binding ) {
|
||||
return syscall( CG_KEY_GETKEY, binding );
|
||||
}
|
||||
|
||||
int trap_PC_AddGlobalDefine( char *define ) {
|
||||
return syscall( CG_PC_ADD_GLOBAL_DEFINE, define );
|
||||
}
|
||||
|
||||
int trap_PC_LoadSource( const char *filename ) {
|
||||
return syscall( CG_PC_LOAD_SOURCE, filename );
|
||||
}
|
||||
|
||||
int trap_PC_FreeSource( int handle ) {
|
||||
return syscall( CG_PC_FREE_SOURCE, handle );
|
||||
}
|
||||
|
||||
int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) {
|
||||
return syscall( CG_PC_READ_TOKEN, handle, pc_token );
|
||||
}
|
||||
|
||||
int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) {
|
||||
return syscall( CG_PC_SOURCE_FILE_AND_LINE, handle, filename, line );
|
||||
}
|
||||
|
||||
void trap_S_StopBackgroundTrack( void ) {
|
||||
syscall( CG_S_STOPBACKGROUNDTRACK );
|
||||
}
|
||||
|
||||
int trap_RealTime(qtime_t *qtime) {
|
||||
return syscall( CG_REAL_TIME, qtime );
|
||||
}
|
||||
|
||||
void trap_SnapVector( float *v ) {
|
||||
syscall( CG_SNAPVECTOR, v );
|
||||
}
|
||||
|
||||
// this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate)
|
||||
int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits) {
|
||||
return syscall(CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits);
|
||||
}
|
||||
|
||||
// stops playing the cinematic and ends it. should always return FMV_EOF
|
||||
// cinematics must be stopped in reverse order of when they are started
|
||||
e_status trap_CIN_StopCinematic(int handle) {
|
||||
return syscall(CG_CIN_STOPCINEMATIC, handle);
|
||||
}
|
||||
|
||||
|
||||
// will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached.
|
||||
e_status trap_CIN_RunCinematic (int handle) {
|
||||
return syscall(CG_CIN_RUNCINEMATIC, handle);
|
||||
}
|
||||
|
||||
|
||||
// draws the current frame
|
||||
void trap_CIN_DrawCinematic (int handle) {
|
||||
syscall(CG_CIN_DRAWCINEMATIC, handle);
|
||||
}
|
||||
|
||||
|
||||
// allows you to resize the animation dynamically
|
||||
void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) {
|
||||
syscall(CG_CIN_SETEXTENTS, handle, x, y, w, h);
|
||||
}
|
||||
|
||||
/*
|
||||
qboolean trap_loadCamera( const char *name ) {
|
||||
return syscall( CG_LOADCAMERA, name );
|
||||
}
|
||||
|
||||
void trap_startCamera(int time) {
|
||||
syscall(CG_STARTCAMERA, time);
|
||||
}
|
||||
|
||||
qboolean trap_getCameraInfo( int time, vec3_t *origin, vec3_t *angles) {
|
||||
return syscall( CG_GETCAMERAINFO, time, origin, angles );
|
||||
}
|
||||
*/
|
||||
|
||||
qboolean trap_GetEntityToken( char *buffer, int bufferSize ) {
|
||||
return syscall( CG_GET_ENTITY_TOKEN, buffer, bufferSize );
|
||||
}
|
||||
|
||||
qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 ) {
|
||||
return syscall( CG_R_INPVS, p1, p2 );
|
||||
}
|
876
code/cgame/cg_view.c
Normal file
876
code/cgame/cg_view.c
Normal file
|
@ -0,0 +1,876 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_view.c -- setup all the parameters (position, angle, etc)
|
||||
// for a 3D rendering
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
MODEL TESTING
|
||||
|
||||
The viewthing and gun positioning tools from Q2 have been integrated and
|
||||
enhanced into a single model testing facility.
|
||||
|
||||
Model viewing can begin with either "testmodel <modelname>" or "testgun <modelname>".
|
||||
|
||||
The names must be the full pathname after the basedir, like
|
||||
"models/weapons/v_launch/tris.md3" or "players/male/tris.md3"
|
||||
|
||||
Testmodel will create a fake entity 100 units in front of the current view
|
||||
position, directly facing the viewer. It will remain immobile, so you can
|
||||
move around it to view it from different angles.
|
||||
|
||||
Testgun will cause the model to follow the player around and supress the real
|
||||
view weapon model. The default frame 0 of most guns is completely off screen,
|
||||
so you will probably have to cycle a couple frames to see it.
|
||||
|
||||
"nextframe", "prevframe", "nextskin", and "prevskin" commands will change the
|
||||
frame or skin of the testmodel. These are bound to F5, F6, F7, and F8 in
|
||||
q3default.cfg.
|
||||
|
||||
If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let
|
||||
you adjust the positioning.
|
||||
|
||||
Note that none of the model testing features update while the game is paused, so
|
||||
it may be convenient to test with deathmatch set to 1 so that bringing down the
|
||||
console doesn't pause the game.
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_TestModel_f
|
||||
|
||||
Creates an entity in front of the current position, which
|
||||
can then be moved around
|
||||
=================
|
||||
*/
|
||||
void CG_TestModel_f (void) {
|
||||
vec3_t angles;
|
||||
|
||||
memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
|
||||
if ( trap_Argc() < 2 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
|
||||
cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
|
||||
|
||||
if ( trap_Argc() == 3 ) {
|
||||
cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
|
||||
cg.testModelEntity.frame = 1;
|
||||
cg.testModelEntity.oldframe = 0;
|
||||
}
|
||||
if (! cg.testModelEntity.hModel ) {
|
||||
CG_Printf( "Can't register model\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
|
||||
|
||||
angles[PITCH] = 0;
|
||||
angles[YAW] = 180 + cg.refdefViewAngles[1];
|
||||
angles[ROLL] = 0;
|
||||
|
||||
AnglesToAxis( angles, cg.testModelEntity.axis );
|
||||
cg.testGun = qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_TestGun_f
|
||||
|
||||
Replaces the current view weapon with the given model
|
||||
=================
|
||||
*/
|
||||
void CG_TestGun_f (void) {
|
||||
CG_TestModel_f();
|
||||
cg.testGun = qtrue;
|
||||
cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON;
|
||||
}
|
||||
|
||||
|
||||
void CG_TestModelNextFrame_f (void) {
|
||||
cg.testModelEntity.frame++;
|
||||
CG_Printf( "frame %i\n", cg.testModelEntity.frame );
|
||||
}
|
||||
|
||||
void CG_TestModelPrevFrame_f (void) {
|
||||
cg.testModelEntity.frame--;
|
||||
if ( cg.testModelEntity.frame < 0 ) {
|
||||
cg.testModelEntity.frame = 0;
|
||||
}
|
||||
CG_Printf( "frame %i\n", cg.testModelEntity.frame );
|
||||
}
|
||||
|
||||
void CG_TestModelNextSkin_f (void) {
|
||||
cg.testModelEntity.skinNum++;
|
||||
CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
|
||||
}
|
||||
|
||||
void CG_TestModelPrevSkin_f (void) {
|
||||
cg.testModelEntity.skinNum--;
|
||||
if ( cg.testModelEntity.skinNum < 0 ) {
|
||||
cg.testModelEntity.skinNum = 0;
|
||||
}
|
||||
CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
|
||||
}
|
||||
|
||||
static void CG_AddTestModel (void) {
|
||||
int i;
|
||||
|
||||
// re-register the model, because the level may have changed
|
||||
cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
|
||||
if (! cg.testModelEntity.hModel ) {
|
||||
CG_Printf ("Can't register model\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// if testing a gun, set the origin reletive to the view origin
|
||||
if ( cg.testGun ) {
|
||||
VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
|
||||
VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] );
|
||||
VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] );
|
||||
VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] );
|
||||
|
||||
// allow the position to be adjusted
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value;
|
||||
cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value;
|
||||
cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value;
|
||||
}
|
||||
}
|
||||
|
||||
trap_R_AddRefEntityToScene( &cg.testModelEntity );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_CalcVrect
|
||||
|
||||
Sets the coordinates of the rendered window
|
||||
=================
|
||||
*/
|
||||
static void CG_CalcVrect (void) {
|
||||
int size;
|
||||
|
||||
// the intermission should allways be full screen
|
||||
if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
|
||||
size = 100;
|
||||
} else {
|
||||
// bound normal viewsize
|
||||
if (cg_viewsize.integer < 30) {
|
||||
trap_Cvar_Set ("cg_viewsize","30");
|
||||
size = 30;
|
||||
} else if (cg_viewsize.integer > 100) {
|
||||
trap_Cvar_Set ("cg_viewsize","100");
|
||||
size = 100;
|
||||
} else {
|
||||
size = cg_viewsize.integer;
|
||||
}
|
||||
|
||||
}
|
||||
cg.refdef.width = cgs.glconfig.vidWidth*size/100;
|
||||
cg.refdef.width &= ~1;
|
||||
|
||||
cg.refdef.height = cgs.glconfig.vidHeight*size/100;
|
||||
cg.refdef.height &= ~1;
|
||||
|
||||
cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2;
|
||||
cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_OffsetThirdPersonView
|
||||
|
||||
===============
|
||||
*/
|
||||
#define FOCUS_DISTANCE 512
|
||||
static void CG_OffsetThirdPersonView( void ) {
|
||||
vec3_t forward, right, up;
|
||||
vec3_t view;
|
||||
vec3_t focusAngles;
|
||||
trace_t trace;
|
||||
static vec3_t mins = { -4, -4, -4 };
|
||||
static vec3_t maxs = { 4, 4, 4 };
|
||||
vec3_t focusPoint;
|
||||
float focusDist;
|
||||
float forwardScale, sideScale;
|
||||
|
||||
cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight;
|
||||
|
||||
VectorCopy( cg.refdefViewAngles, focusAngles );
|
||||
|
||||
// if dead, look at killer
|
||||
if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
|
||||
focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
|
||||
cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
|
||||
}
|
||||
|
||||
if ( focusAngles[PITCH] > 45 ) {
|
||||
focusAngles[PITCH] = 45; // don't go too far overhead
|
||||
}
|
||||
AngleVectors( focusAngles, forward, NULL, NULL );
|
||||
|
||||
VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
|
||||
|
||||
VectorCopy( cg.refdef.vieworg, view );
|
||||
|
||||
view[2] += 8;
|
||||
|
||||
cg.refdefViewAngles[PITCH] *= 0.5;
|
||||
|
||||
AngleVectors( cg.refdefViewAngles, forward, right, up );
|
||||
|
||||
forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
|
||||
sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
|
||||
VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
|
||||
VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
|
||||
|
||||
// trace a ray from the origin to the viewpoint to make sure the view isn't
|
||||
// in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
|
||||
|
||||
if (!cg_cameraMode.integer) {
|
||||
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
|
||||
|
||||
if ( trace.fraction != 1.0 ) {
|
||||
VectorCopy( trace.endpos, view );
|
||||
view[2] += (1.0 - trace.fraction) * 32;
|
||||
// try another trace to this position, because a tunnel may have the ceiling
|
||||
// close enogh that this is poking out
|
||||
|
||||
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
|
||||
VectorCopy( trace.endpos, view );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VectorCopy( view, cg.refdef.vieworg );
|
||||
|
||||
// select pitch to look at focus point from vieword
|
||||
VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
|
||||
focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
|
||||
if ( focusDist < 1 ) {
|
||||
focusDist = 1; // should never happen
|
||||
}
|
||||
cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
|
||||
cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value;
|
||||
}
|
||||
|
||||
|
||||
// this causes a compiler bug on mac MrC compiler
|
||||
static void CG_StepOffset( void ) {
|
||||
int timeDelta;
|
||||
|
||||
// smooth out stair climbing
|
||||
timeDelta = cg.time - cg.stepTime;
|
||||
if ( timeDelta < STEP_TIME ) {
|
||||
cg.refdef.vieworg[2] -= cg.stepChange
|
||||
* (STEP_TIME - timeDelta) / STEP_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_OffsetFirstPersonView
|
||||
|
||||
===============
|
||||
*/
|
||||
static void CG_OffsetFirstPersonView( void ) {
|
||||
float *origin;
|
||||
float *angles;
|
||||
float bob;
|
||||
float ratio;
|
||||
float delta;
|
||||
float speed;
|
||||
float f;
|
||||
vec3_t predictedVelocity;
|
||||
int timeDelta;
|
||||
|
||||
if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
|
||||
return;
|
||||
}
|
||||
|
||||
origin = cg.refdef.vieworg;
|
||||
angles = cg.refdefViewAngles;
|
||||
|
||||
// if dead, fix the angle and don't add any kick
|
||||
if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
|
||||
angles[ROLL] = 40;
|
||||
angles[PITCH] = -15;
|
||||
angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
|
||||
origin[2] += cg.predictedPlayerState.viewheight;
|
||||
return;
|
||||
}
|
||||
|
||||
// add angles based on weapon kick
|
||||
VectorAdd (angles, cg.kick_angles, angles);
|
||||
|
||||
// add angles based on damage kick
|
||||
if ( cg.damageTime ) {
|
||||
ratio = cg.time - cg.damageTime;
|
||||
if ( ratio < DAMAGE_DEFLECT_TIME ) {
|
||||
ratio /= DAMAGE_DEFLECT_TIME;
|
||||
angles[PITCH] += ratio * cg.v_dmg_pitch;
|
||||
angles[ROLL] += ratio * cg.v_dmg_roll;
|
||||
} else {
|
||||
ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
|
||||
if ( ratio > 0 ) {
|
||||
angles[PITCH] += ratio * cg.v_dmg_pitch;
|
||||
angles[ROLL] += ratio * cg.v_dmg_roll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add pitch based on fall kick
|
||||
#if 0
|
||||
ratio = ( cg.time - cg.landTime) / FALL_TIME;
|
||||
if (ratio < 0)
|
||||
ratio = 0;
|
||||
angles[PITCH] += ratio * cg.fall_value;
|
||||
#endif
|
||||
|
||||
// add angles based on velocity
|
||||
VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );
|
||||
|
||||
delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
|
||||
angles[PITCH] += delta * cg_runpitch.value;
|
||||
|
||||
delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
|
||||
angles[ROLL] -= delta * cg_runroll.value;
|
||||
|
||||
// add angles based on bob
|
||||
|
||||
// make sure the bob is visible even at low speeds
|
||||
speed = cg.xyspeed > 200 ? cg.xyspeed : 200;
|
||||
|
||||
delta = cg.bobfracsin * cg_bobpitch.value * speed;
|
||||
if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
|
||||
delta *= 3; // crouching
|
||||
angles[PITCH] += delta;
|
||||
delta = cg.bobfracsin * cg_bobroll.value * speed;
|
||||
if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
|
||||
delta *= 3; // crouching accentuates roll
|
||||
if (cg.bobcycle & 1)
|
||||
delta = -delta;
|
||||
angles[ROLL] += delta;
|
||||
|
||||
//===================================
|
||||
|
||||
// add view height
|
||||
origin[2] += cg.predictedPlayerState.viewheight;
|
||||
|
||||
// smooth out duck height changes
|
||||
timeDelta = cg.time - cg.duckTime;
|
||||
if ( timeDelta < DUCK_TIME) {
|
||||
cg.refdef.vieworg[2] -= cg.duckChange
|
||||
* (DUCK_TIME - timeDelta) / DUCK_TIME;
|
||||
}
|
||||
|
||||
// add bob height
|
||||
bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
|
||||
if (bob > 6) {
|
||||
bob = 6;
|
||||
}
|
||||
|
||||
origin[2] += bob;
|
||||
|
||||
|
||||
// add fall height
|
||||
delta = cg.time - cg.landTime;
|
||||
if ( delta < LAND_DEFLECT_TIME ) {
|
||||
f = delta / LAND_DEFLECT_TIME;
|
||||
cg.refdef.vieworg[2] += cg.landChange * f;
|
||||
} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
|
||||
delta -= LAND_DEFLECT_TIME;
|
||||
f = 1.0 - ( delta / LAND_RETURN_TIME );
|
||||
cg.refdef.vieworg[2] += cg.landChange * f;
|
||||
}
|
||||
|
||||
// add step offset
|
||||
CG_StepOffset();
|
||||
|
||||
// add kick offset
|
||||
|
||||
VectorAdd (origin, cg.kick_origin, origin);
|
||||
|
||||
// pivot the eye based on a neck length
|
||||
#if 0
|
||||
{
|
||||
#define NECK_LENGTH 8
|
||||
vec3_t forward, up;
|
||||
|
||||
cg.refdef.vieworg[2] -= NECK_LENGTH;
|
||||
AngleVectors( cg.refdefViewAngles, forward, NULL, up );
|
||||
VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg );
|
||||
VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void CG_ZoomDown_f( void ) {
|
||||
if ( cg.zoomed ) {
|
||||
return;
|
||||
}
|
||||
cg.zoomed = qtrue;
|
||||
cg.zoomTime = cg.time;
|
||||
}
|
||||
|
||||
void CG_ZoomUp_f( void ) {
|
||||
if ( !cg.zoomed ) {
|
||||
return;
|
||||
}
|
||||
cg.zoomed = qfalse;
|
||||
cg.zoomTime = cg.time;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
CG_CalcFov
|
||||
|
||||
Fixed fov at intermissions, otherwise account for fov variable and zooms.
|
||||
====================
|
||||
*/
|
||||
#define WAVE_AMPLITUDE 1
|
||||
#define WAVE_FREQUENCY 0.4
|
||||
|
||||
static int CG_CalcFov( void ) {
|
||||
float x;
|
||||
float phase;
|
||||
float v;
|
||||
int contents;
|
||||
float fov_x, fov_y;
|
||||
float zoomFov;
|
||||
float f;
|
||||
int inwater;
|
||||
|
||||
if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
|
||||
// if in intermission, use a fixed value
|
||||
fov_x = 90;
|
||||
} else {
|
||||
// user selectable
|
||||
if ( cgs.dmflags & DF_FIXED_FOV ) {
|
||||
// dmflag to prevent wide fov for all clients
|
||||
fov_x = 90;
|
||||
} else {
|
||||
fov_x = cg_fov.value;
|
||||
if ( fov_x < 1 ) {
|
||||
fov_x = 1;
|
||||
} else if ( fov_x > 160 ) {
|
||||
fov_x = 160;
|
||||
}
|
||||
}
|
||||
|
||||
// account for zooms
|
||||
zoomFov = cg_zoomFov.value;
|
||||
if ( zoomFov < 1 ) {
|
||||
zoomFov = 1;
|
||||
} else if ( zoomFov > 160 ) {
|
||||
zoomFov = 160;
|
||||
}
|
||||
|
||||
if ( cg.zoomed ) {
|
||||
f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
|
||||
if ( f > 1.0 ) {
|
||||
fov_x = zoomFov;
|
||||
} else {
|
||||
fov_x = fov_x + f * ( zoomFov - fov_x );
|
||||
}
|
||||
} else {
|
||||
f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
|
||||
if ( f > 1.0 ) {
|
||||
fov_x = fov_x;
|
||||
} else {
|
||||
fov_x = zoomFov + f * ( fov_x - zoomFov );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x = cg.refdef.width / tan( fov_x / 360 * M_PI );
|
||||
fov_y = atan2( cg.refdef.height, x );
|
||||
fov_y = fov_y * 360 / M_PI;
|
||||
|
||||
// warp if underwater
|
||||
contents = CG_PointContents( cg.refdef.vieworg, -1 );
|
||||
if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){
|
||||
phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
|
||||
v = WAVE_AMPLITUDE * sin( phase );
|
||||
fov_x += v;
|
||||
fov_y -= v;
|
||||
inwater = qtrue;
|
||||
}
|
||||
else {
|
||||
inwater = qfalse;
|
||||
}
|
||||
|
||||
|
||||
// set it
|
||||
cg.refdef.fov_x = fov_x;
|
||||
cg.refdef.fov_y = fov_y;
|
||||
|
||||
if ( !cg.zoomed ) {
|
||||
cg.zoomSensitivity = 1;
|
||||
} else {
|
||||
cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
|
||||
}
|
||||
|
||||
return inwater;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_DamageBlendBlob
|
||||
|
||||
===============
|
||||
*/
|
||||
static void CG_DamageBlendBlob( void ) {
|
||||
int t;
|
||||
int maxTime;
|
||||
refEntity_t ent;
|
||||
|
||||
if ( !cg.damageValue ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//if (cg.cameraMode) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
// ragePro systems can't fade blends, so don't obscure the screen
|
||||
if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) {
|
||||
return;
|
||||
}
|
||||
|
||||
maxTime = DAMAGE_TIME;
|
||||
t = cg.time - cg.damageTime;
|
||||
if ( t <= 0 || t >= maxTime ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
memset( &ent, 0, sizeof( ent ) );
|
||||
ent.reType = RT_SPRITE;
|
||||
ent.renderfx = RF_FIRST_PERSON;
|
||||
|
||||
VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin );
|
||||
VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin );
|
||||
VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin );
|
||||
|
||||
ent.radius = cg.damageValue * 3;
|
||||
ent.customShader = cgs.media.viewBloodShader;
|
||||
ent.shaderRGBA[0] = 255;
|
||||
ent.shaderRGBA[1] = 255;
|
||||
ent.shaderRGBA[2] = 255;
|
||||
ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) );
|
||||
trap_R_AddRefEntityToScene( &ent );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_CalcViewValues
|
||||
|
||||
Sets cg.refdef view values
|
||||
===============
|
||||
*/
|
||||
static int CG_CalcViewValues( void ) {
|
||||
playerState_t *ps;
|
||||
|
||||
memset( &cg.refdef, 0, sizeof( cg.refdef ) );
|
||||
|
||||
// strings for in game rendering
|
||||
// Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) );
|
||||
// Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) );
|
||||
|
||||
// calculate size of 3D view
|
||||
CG_CalcVrect();
|
||||
|
||||
ps = &cg.predictedPlayerState;
|
||||
/*
|
||||
if (cg.cameraMode) {
|
||||
vec3_t origin, angles;
|
||||
if (trap_getCameraInfo(cg.time, &origin, &angles)) {
|
||||
VectorCopy(origin, cg.refdef.vieworg);
|
||||
angles[ROLL] = 0;
|
||||
VectorCopy(angles, cg.refdefViewAngles);
|
||||
AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
|
||||
return CG_CalcFov();
|
||||
} else {
|
||||
cg.cameraMode = qfalse;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// intermission view
|
||||
if ( ps->pm_type == PM_INTERMISSION ) {
|
||||
VectorCopy( ps->origin, cg.refdef.vieworg );
|
||||
VectorCopy( ps->viewangles, cg.refdefViewAngles );
|
||||
AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
|
||||
return CG_CalcFov();
|
||||
}
|
||||
|
||||
cg.bobcycle = ( ps->bobCycle & 128 ) >> 7;
|
||||
cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) );
|
||||
cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] +
|
||||
ps->velocity[1] * ps->velocity[1] );
|
||||
|
||||
|
||||
VectorCopy( ps->origin, cg.refdef.vieworg );
|
||||
VectorCopy( ps->viewangles, cg.refdefViewAngles );
|
||||
|
||||
if (cg_cameraOrbit.integer) {
|
||||
if (cg.time > cg.nextOrbitTime) {
|
||||
cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer;
|
||||
cg_thirdPersonAngle.value += cg_cameraOrbit.value;
|
||||
}
|
||||
}
|
||||
// add error decay
|
||||
if ( cg_errorDecay.value > 0 ) {
|
||||
int t;
|
||||
float f;
|
||||
|
||||
t = cg.time - cg.predictedErrorTime;
|
||||
f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
|
||||
if ( f > 0 && f < 1 ) {
|
||||
VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg );
|
||||
} else {
|
||||
cg.predictedErrorTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( cg.renderingThirdPerson ) {
|
||||
// back away from character
|
||||
CG_OffsetThirdPersonView();
|
||||
} else {
|
||||
// offset for local bobbing and kicks
|
||||
CG_OffsetFirstPersonView();
|
||||
}
|
||||
|
||||
// position eye reletive to origin
|
||||
AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
|
||||
|
||||
if ( cg.hyperspace ) {
|
||||
cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE;
|
||||
}
|
||||
|
||||
// field of view
|
||||
return CG_CalcFov();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
CG_PowerupTimerSounds
|
||||
=====================
|
||||
*/
|
||||
static void CG_PowerupTimerSounds( void ) {
|
||||
int i;
|
||||
int t;
|
||||
|
||||
// powerup timers going away
|
||||
for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
|
||||
t = cg.snap->ps.powerups[i];
|
||||
if ( t <= cg.time ) {
|
||||
continue;
|
||||
}
|
||||
if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
|
||||
continue;
|
||||
}
|
||||
if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) {
|
||||
trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CG_AddBufferedSound
|
||||
=====================
|
||||
*/
|
||||
void CG_AddBufferedSound( sfxHandle_t sfx ) {
|
||||
if ( !sfx )
|
||||
return;
|
||||
cg.soundBuffer[cg.soundBufferIn] = sfx;
|
||||
cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER;
|
||||
if (cg.soundBufferIn == cg.soundBufferOut) {
|
||||
cg.soundBufferOut++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CG_PlayBufferedSounds
|
||||
=====================
|
||||
*/
|
||||
static void CG_PlayBufferedSounds( void ) {
|
||||
if ( cg.soundTime < cg.time ) {
|
||||
if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) {
|
||||
trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER);
|
||||
cg.soundBuffer[cg.soundBufferOut] = 0;
|
||||
cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER;
|
||||
cg.soundTime = cg.time + 750;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
CG_DrawActiveFrame
|
||||
|
||||
Generates and draws a game scene and status information at the given time.
|
||||
=================
|
||||
*/
|
||||
void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) {
|
||||
int inwater;
|
||||
|
||||
cg.time = serverTime;
|
||||
cg.demoPlayback = demoPlayback;
|
||||
|
||||
// update cvars
|
||||
CG_UpdateCvars();
|
||||
|
||||
// if we are only updating the screen as a loading
|
||||
// pacifier, don't even try to read snapshots
|
||||
if ( cg.infoScreenText[0] != 0 ) {
|
||||
CG_DrawInformation();
|
||||
return;
|
||||
}
|
||||
|
||||
// any looped sounds will be respecified as entities
|
||||
// are added to the render list
|
||||
trap_S_ClearLoopingSounds(qfalse);
|
||||
|
||||
// clear all the render lists
|
||||
trap_R_ClearScene();
|
||||
|
||||
// set up cg.snap and possibly cg.nextSnap
|
||||
CG_ProcessSnapshots();
|
||||
|
||||
// if we haven't received any snapshots yet, all
|
||||
// we can draw is the information screen
|
||||
if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
|
||||
CG_DrawInformation();
|
||||
return;
|
||||
}
|
||||
|
||||
// let the client system know what our weapon and zoom settings are
|
||||
trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity );
|
||||
|
||||
// this counter will be bumped for every valid scene we generate
|
||||
cg.clientFrame++;
|
||||
|
||||
// update cg.predictedPlayerState
|
||||
CG_PredictPlayerState();
|
||||
|
||||
// decide on third person view
|
||||
cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
|
||||
|
||||
// build cg.refdef
|
||||
inwater = CG_CalcViewValues();
|
||||
|
||||
// first person blend blobs, done after AnglesToAxis
|
||||
if ( !cg.renderingThirdPerson ) {
|
||||
CG_DamageBlendBlob();
|
||||
}
|
||||
|
||||
// build the render lists
|
||||
if ( !cg.hyperspace ) {
|
||||
CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct
|
||||
CG_AddMarks();
|
||||
CG_AddParticles ();
|
||||
CG_AddLocalEntities();
|
||||
}
|
||||
CG_AddViewWeapon( &cg.predictedPlayerState );
|
||||
|
||||
// add buffered sounds
|
||||
CG_PlayBufferedSounds();
|
||||
|
||||
// play buffered voice chats
|
||||
CG_PlayBufferedVoiceChats();
|
||||
|
||||
// finish up the rest of the refdef
|
||||
if ( cg.testModelEntity.hModel ) {
|
||||
CG_AddTestModel();
|
||||
}
|
||||
cg.refdef.time = cg.time;
|
||||
memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
|
||||
|
||||
// warning sounds when powerup is wearing off
|
||||
CG_PowerupTimerSounds();
|
||||
|
||||
// update audio positions
|
||||
trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater );
|
||||
|
||||
// make sure the lagometerSample and frame timing isn't done twice when in stereo
|
||||
if ( stereoView != STEREO_RIGHT ) {
|
||||
cg.frametime = cg.time - cg.oldTime;
|
||||
if ( cg.frametime < 0 ) {
|
||||
cg.frametime = 0;
|
||||
}
|
||||
cg.oldTime = cg.time;
|
||||
CG_AddLagometerFrameInfo();
|
||||
}
|
||||
if (cg_timescale.value != cg_timescaleFadeEnd.value) {
|
||||
if (cg_timescale.value < cg_timescaleFadeEnd.value) {
|
||||
cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
|
||||
if (cg_timescale.value > cg_timescaleFadeEnd.value)
|
||||
cg_timescale.value = cg_timescaleFadeEnd.value;
|
||||
}
|
||||
else {
|
||||
cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
|
||||
if (cg_timescale.value < cg_timescaleFadeEnd.value)
|
||||
cg_timescale.value = cg_timescaleFadeEnd.value;
|
||||
}
|
||||
if (cg_timescaleFadeSpeed.value) {
|
||||
trap_Cvar_Set("timescale", va("%f", cg_timescale.value));
|
||||
}
|
||||
}
|
||||
|
||||
// actually issue the rendering calls
|
||||
CG_DrawActive( stereoView );
|
||||
|
||||
if ( cg_stats.integer ) {
|
||||
CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
2277
code/cgame/cg_weapons.c
Normal file
2277
code/cgame/cg_weapons.c
Normal file
File diff suppressed because it is too large
Load diff
63
code/cgame/cgame.bat
Normal file
63
code/cgame/cgame.bat
Normal file
|
@ -0,0 +1,63 @@
|
|||
rem make sure we have a safe environement
|
||||
set LIBRARY=
|
||||
set INCLUDE=
|
||||
|
||||
mkdir vm
|
||||
cd vm
|
||||
set cc=lcc -DQ3_VM -DCGAME -S -Wf-target=bytecode -Wf-g -I..\..\cgame -I..\..\game -I..\..\ui %1
|
||||
|
||||
%cc% ../../game/bg_misc.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/bg_pmove.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/bg_slidemove.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/bg_lib.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/q_math.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/q_shared.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_consolecmds.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_draw.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_drawtools.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_effects.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_ents.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_event.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_info.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_localents.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_main.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_marks.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_players.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_playerstate.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_predict.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_scoreboard.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_servercmds.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_snapshot.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_view.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_weapons.c
|
||||
@if errorlevel 1 goto quit
|
||||
|
||||
|
||||
|
||||
|
||||
q3asm -f ../cgame
|
||||
:quit
|
||||
cd ..
|
3
code/cgame/cgame.def
Normal file
3
code/cgame/cgame.def
Normal file
|
@ -0,0 +1,3 @@
|
|||
EXPORTS
|
||||
vmMain
|
||||
dllEntry
|
124
code/cgame/cgame.plg
Normal file
124
code/cgame/cgame.plg
Normal file
|
@ -0,0 +1,124 @@
|
|||
<html>
|
||||
<body>
|
||||
<pre>
|
||||
<h1>Build Log</h1>
|
||||
<h3>
|
||||
--------------------Configuration: cgame - Win32 Release--------------------
|
||||
</h3>
|
||||
<h3>Command Lines</h3>
|
||||
Creating temporary file "C:\WINNT\Profiles\ADMINI~1\LOCALS~1\Temp\RSP4BE.tmp" with contents
|
||||
[
|
||||
/nologo /G6 /ML /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fp"Release/cgame.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c
|
||||
"D:\quake3\MissionPack\code\game\bg_misc.c"
|
||||
"D:\quake3\MissionPack\code\game\bg_pmove.c"
|
||||
"D:\quake3\MissionPack\code\game\bg_slidemove.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_consolecmds.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_draw.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_drawtools.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_effects.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_ents.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_event.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_info.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_localents.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_main.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_marks.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_players.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_playerstate.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_predict.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_rankings.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_scoreboard.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_servercmds.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_snapshot.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_syscalls.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_view.c"
|
||||
"D:\quake3\MissionPack\code\cgame\cg_weapons.c"
|
||||
"D:\quake3\MissionPack\code\game\q_math.c"
|
||||
"D:\quake3\MissionPack\code\game\q_shared.c"
|
||||
"D:\quake3\MissionPack\code\ui\ui_shared.c"
|
||||
]
|
||||
Creating command line "cl.exe @C:\WINNT\Profiles\ADMINI~1\LOCALS~1\Temp\RSP4BE.tmp"
|
||||
Creating temporary file "C:\WINNT\Profiles\ADMINI~1\LOCALS~1\Temp\RSP4BF.tmp" with contents
|
||||
[
|
||||
/nologo /base:"0x30000000" /subsystem:windows /dll /incremental:no /pdb:"Release/cgamex86.pdb" /map:"Release/cgamex86.map" /machine:I386 /def:".\cgame.def" /out:"../Release/cgamex86.dll" /implib:"Release/cgamex86.lib"
|
||||
.\Release\bg_misc.obj
|
||||
.\Release\bg_pmove.obj
|
||||
.\Release\bg_slidemove.obj
|
||||
.\Release\cg_consolecmds.obj
|
||||
.\Release\cg_draw.obj
|
||||
.\Release\cg_drawtools.obj
|
||||
.\Release\cg_effects.obj
|
||||
.\Release\cg_ents.obj
|
||||
.\Release\cg_event.obj
|
||||
.\Release\cg_info.obj
|
||||
.\Release\cg_localents.obj
|
||||
.\Release\cg_main.obj
|
||||
.\Release\cg_marks.obj
|
||||
.\Release\cg_players.obj
|
||||
.\Release\cg_playerstate.obj
|
||||
.\Release\cg_predict.obj
|
||||
.\Release\cg_rankings.obj
|
||||
.\Release\cg_scoreboard.obj
|
||||
.\Release\cg_servercmds.obj
|
||||
.\Release\cg_snapshot.obj
|
||||
.\Release\cg_syscalls.obj
|
||||
.\Release\cg_view.obj
|
||||
.\Release\cg_weapons.obj
|
||||
.\Release\q_math.obj
|
||||
.\Release\q_shared.obj
|
||||
.\Release\ui_shared.obj
|
||||
]
|
||||
Creating command line "link.exe @C:\WINNT\Profiles\ADMINI~1\LOCALS~1\Temp\RSP4BF.tmp"
|
||||
<h3>Output Window</h3>
|
||||
Compiling...
|
||||
bg_misc.c
|
||||
bg_pmove.c
|
||||
D:\quake3\MissionPack\code\game\bg_pmove.c(987) : warning C4189: 'shit' : local variable is initialized but not referenced
|
||||
D:\quake3\MissionPack\code\game\bg_pmove.c(2001) : warning C4505: 'PM_InvulnerabilityMove' : unreferenced local function has been removed
|
||||
D:\quake3\MissionPack\code\game\bg_pmove.c(519) : see declaration of 'PM_InvulnerabilityMove'
|
||||
bg_slidemove.c
|
||||
cg_consolecmds.c
|
||||
cg_draw.c
|
||||
cg_drawtools.c
|
||||
cg_effects.c
|
||||
cg_ents.c
|
||||
cg_event.c
|
||||
cg_info.c
|
||||
cg_localents.c
|
||||
cg_main.c
|
||||
D:\quake3\MissionPack\code\cgame\cg_main.c(1819) : warning C4505: 'CG_Cvar_Get' : unreferenced local function has been removed
|
||||
D:\quake3\MissionPack\code\cgame\cg_main.c(1513) : see declaration of 'CG_Cvar_Get'
|
||||
cg_marks.c
|
||||
cg_players.c
|
||||
D:\quake3\MissionPack\code\cgame\cg_players.c(2209) : warning C4505: 'CG_PlayerTokens' : unreferenced local function has been removed
|
||||
D:\quake3\MissionPack\code\cgame\cg_players.c(1371) : see declaration of 'CG_PlayerTokens'
|
||||
cg_playerstate.c
|
||||
cg_predict.c
|
||||
cg_rankings.c
|
||||
cg_scoreboard.c
|
||||
cg_servercmds.c
|
||||
cg_snapshot.c
|
||||
cg_syscalls.c
|
||||
cg_view.c
|
||||
cg_weapons.c
|
||||
q_math.c
|
||||
q_shared.c
|
||||
ui_shared.c
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(2223) : warning C4189: 'parent' : local variable is initialized but not referenced
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(3501) : warning C4189: 'collision' : local variable is initialized but not referenced
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(4622) : warning C4505: 'Controls_SetDefaults' : unreferenced local function has been removed
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(2595) : see declaration of 'Controls_SetDefaults'
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(1540) : warning C4701: local variable 'value' may be used without having been initialized
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(1566) : warning C4701: local variable 'value' may be used without having been initialized
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(1912) : warning C4702: unreachable code
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(4013) : warning C4702: unreachable code
|
||||
D:\quake3\MissionPack\code\ui\ui_shared.c(4056) : warning C4702: unreachable code
|
||||
Linking...
|
||||
Creating library Release/cgamex86.lib and object Release/cgamex86.exp
|
||||
|
||||
|
||||
|
||||
<h3>Results</h3>
|
||||
cgamex86.dll - 0 error(s), 12 warning(s)
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
26
code/cgame/cgame.q3asm
Normal file
26
code/cgame/cgame.q3asm
Normal file
|
@ -0,0 +1,26 @@
|
|||
-o "\quake3\baseq3\vm\cgame"
|
||||
cg_main
|
||||
..\cg_syscalls
|
||||
cg_consolecmds
|
||||
cg_draw
|
||||
cg_drawtools
|
||||
cg_effects
|
||||
cg_ents
|
||||
cg_event
|
||||
cg_info
|
||||
cg_localents
|
||||
cg_marks
|
||||
cg_players
|
||||
cg_playerstate
|
||||
cg_predict
|
||||
cg_scoreboard
|
||||
cg_servercmds
|
||||
cg_snapshot
|
||||
cg_view
|
||||
cg_weapons
|
||||
bg_slidemove
|
||||
bg_pmove
|
||||
bg_lib
|
||||
bg_misc
|
||||
q_math
|
||||
q_shared
|
36
code/cgame/cgame.sh
Normal file
36
code/cgame/cgame.sh
Normal file
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
|
||||
mkdir -p vm
|
||||
cd vm
|
||||
|
||||
CC="q3lcc -DQ3_VM -DCGAME -S -Wf-target=bytecode -Wf-g -I../../cgame -I../../game -I../../q3_ui"
|
||||
|
||||
$CC ../cg_syscalls.c
|
||||
$CC ../../game/bg_misc.c
|
||||
$CC ../../game/bg_pmove.c
|
||||
$CC ../../game/bg_slidemove.c
|
||||
$CC ../../game/bg_lib.c
|
||||
$CC ../../game/q_math.c
|
||||
$CC ../../game/q_shared.c
|
||||
$CC ../cg_consolecmds.c
|
||||
$CC ../cg_draw.c
|
||||
$CC ../cg_drawtools.c
|
||||
$CC ../cg_effects.c
|
||||
$CC ../cg_ents.c
|
||||
$CC ../cg_event.c
|
||||
$CC ../cg_info.c
|
||||
$CC ../cg_localents.c
|
||||
$CC ../cg_main.c
|
||||
$CC ../cg_marks.c
|
||||
$CC ../cg_players.c
|
||||
$CC ../cg_playerstate.c
|
||||
$CC ../cg_predict.c
|
||||
$CC ../cg_scoreboard.c
|
||||
$CC ../cg_servercmds.c
|
||||
$CC ../cg_snapshot.c
|
||||
$CC ../cg_view.c
|
||||
$CC ../cg_weapons.c
|
||||
|
||||
q3asm -f ../cgame
|
||||
|
||||
cd ..
|
1221
code/cgame/cgame.vcproj
Normal file
1221
code/cgame/cgame.vcproj
Normal file
File diff suppressed because it is too large
Load diff
65
code/cgame/cgame_ta.bat
Normal file
65
code/cgame/cgame_ta.bat
Normal file
|
@ -0,0 +1,65 @@
|
|||
rem make sure we have a safe environement
|
||||
set LIBRARY=
|
||||
set INCLUDE=
|
||||
|
||||
mkdir vm
|
||||
cd vm
|
||||
set cc=lcc -DQ3_VM -DMISSIONPACK -DCGAME -S -Wf-target=bytecode -Wf-g -I..\..\cgame -I..\..\game -I..\..\ui %1
|
||||
|
||||
%cc% ../../game/bg_misc.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/bg_pmove.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/bg_slidemove.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/bg_lib.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/q_math.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../game/q_shared.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_consolecmds.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_draw.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_drawtools.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_effects.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_ents.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_event.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_info.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_localents.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_main.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_marks.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_players.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_playerstate.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_predict.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_scoreboard.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_servercmds.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_snapshot.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_view.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_weapons.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../../ui/ui_shared.c
|
||||
@if errorlevel 1 goto quit
|
||||
%cc% ../cg_newdraw.c
|
||||
@if errorlevel 1 goto quit
|
||||
|
||||
|
||||
q3asm -f ../cgame_ta
|
||||
:quit
|
||||
cd ..
|
28
code/cgame/cgame_ta.q3asm
Normal file
28
code/cgame/cgame_ta.q3asm
Normal file
|
@ -0,0 +1,28 @@
|
|||
-o "\quake3\missionpack\vm\cgame"
|
||||
cg_main
|
||||
..\cg_syscalls
|
||||
cg_consolecmds
|
||||
cg_draw
|
||||
cg_drawtools
|
||||
cg_effects
|
||||
cg_ents
|
||||
cg_event
|
||||
cg_info
|
||||
cg_localents
|
||||
cg_marks
|
||||
cg_players
|
||||
cg_playerstate
|
||||
cg_predict
|
||||
cg_scoreboard
|
||||
cg_servercmds
|
||||
cg_snapshot
|
||||
cg_view
|
||||
cg_weapons
|
||||
bg_slidemove
|
||||
bg_pmove
|
||||
bg_lib
|
||||
bg_misc
|
||||
q_math
|
||||
q_shared
|
||||
ui_shared
|
||||
cg_newdraw
|
38
code/cgame/cgame_ta.sh
Normal file
38
code/cgame/cgame_ta.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#!/bin/sh
|
||||
|
||||
mkdir -p vm
|
||||
cd vm
|
||||
|
||||
CC="q3lcc -DQ3_VM -DCGAME -DMISSIONPACK -S -Wf-target=bytecode -Wf-g -I../../cgame -I../../game -I../../ui"
|
||||
|
||||
$CC ../cg_syscalls.c
|
||||
$CC ../../game/bg_misc.c
|
||||
$CC ../../game/bg_pmove.c
|
||||
$CC ../../game/bg_slidemove.c
|
||||
$CC ../../game/bg_lib.c
|
||||
$CC ../../game/q_math.c
|
||||
$CC ../../game/q_shared.c
|
||||
$CC ../cg_consolecmds.c
|
||||
$CC ../cg_draw.c
|
||||
$CC ../cg_drawtools.c
|
||||
$CC ../cg_effects.c
|
||||
$CC ../cg_ents.c
|
||||
$CC ../cg_event.c
|
||||
$CC ../cg_info.c
|
||||
$CC ../cg_localents.c
|
||||
$CC ../cg_main.c
|
||||
$CC ../cg_marks.c
|
||||
$CC ../cg_players.c
|
||||
$CC ../cg_playerstate.c
|
||||
$CC ../cg_predict.c
|
||||
$CC ../cg_scoreboard.c
|
||||
$CC ../cg_servercmds.c
|
||||
$CC ../cg_snapshot.c
|
||||
$CC ../cg_view.c
|
||||
$CC ../cg_weapons.c
|
||||
$CC ../../ui/ui_shared.c
|
||||
$CC ../cg_newdraw.c
|
||||
|
||||
q3asm -f ../cgame_ta
|
||||
|
||||
cd ..
|
229
code/cgame/tr_types.h
Normal file
229
code/cgame/tr_types.h
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
#ifndef __TR_TYPES_H
|
||||
#define __TR_TYPES_H
|
||||
|
||||
|
||||
#define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces
|
||||
#define MAX_ENTITIES 1023 // can't be increased without changing drawsurf bit packing
|
||||
|
||||
// renderfx flags
|
||||
#define RF_MINLIGHT 1 // allways have some light (viewmodel, some items)
|
||||
#define RF_THIRD_PERSON 2 // don't draw through eyes, only mirrors (player bodies, chat sprites)
|
||||
#define RF_FIRST_PERSON 4 // only draw through eyes (view weapon, damage blood blob)
|
||||
#define RF_DEPTHHACK 8 // for view weapon Z crunching
|
||||
#define RF_NOSHADOW 64 // don't add stencil shadows
|
||||
|
||||
#define RF_LIGHTING_ORIGIN 128 // use refEntity->lightingOrigin instead of refEntity->origin
|
||||
// for lighting. This allows entities to sink into the floor
|
||||
// with their origin going solid, and allows all parts of a
|
||||
// player to get the same lighting
|
||||
#define RF_SHADOW_PLANE 256 // use refEntity->shadowPlane
|
||||
#define RF_WRAP_FRAMES 512 // mod the model frames by the maxframes to allow continuous
|
||||
// animation without needing to know the frame count
|
||||
|
||||
// refdef flags
|
||||
#define RDF_NOWORLDMODEL 1 // used for player configuration screen
|
||||
#define RDF_HYPERSPACE 4 // teleportation effect
|
||||
|
||||
typedef struct {
|
||||
vec3_t xyz;
|
||||
float st[2];
|
||||
byte modulate[4];
|
||||
} polyVert_t;
|
||||
|
||||
typedef struct poly_s {
|
||||
qhandle_t hShader;
|
||||
int numVerts;
|
||||
polyVert_t *verts;
|
||||
} poly_t;
|
||||
|
||||
typedef enum {
|
||||
RT_MODEL,
|
||||
RT_POLY,
|
||||
RT_SPRITE,
|
||||
RT_BEAM,
|
||||
RT_RAIL_CORE,
|
||||
RT_RAIL_RINGS,
|
||||
RT_LIGHTNING,
|
||||
RT_PORTALSURFACE, // doesn't draw anything, just info for portals
|
||||
|
||||
RT_MAX_REF_ENTITY_TYPE
|
||||
} refEntityType_t;
|
||||
|
||||
typedef struct {
|
||||
refEntityType_t reType;
|
||||
int renderfx;
|
||||
|
||||
qhandle_t hModel; // opaque type outside refresh
|
||||
|
||||
// most recent data
|
||||
vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN)
|
||||
float shadowPlane; // projection shadows go here, stencils go slightly lower
|
||||
|
||||
vec3_t axis[3]; // rotation vectors
|
||||
qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale
|
||||
float origin[3]; // also used as MODEL_BEAM's "from"
|
||||
int frame; // also used as MODEL_BEAM's diameter
|
||||
|
||||
// previous data for frame interpolation
|
||||
float oldorigin[3]; // also used as MODEL_BEAM's "to"
|
||||
int oldframe;
|
||||
float backlerp; // 0.0 = current, 1.0 = old
|
||||
|
||||
// texturing
|
||||
int skinNum; // inline skin index
|
||||
qhandle_t customSkin; // NULL for default skin
|
||||
qhandle_t customShader; // use one image for the entire thing
|
||||
|
||||
// misc
|
||||
byte shaderRGBA[4]; // colors used by rgbgen entity shaders
|
||||
float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers
|
||||
float shaderTime; // subtracted from refdef time to control effect start times
|
||||
|
||||
// extra sprite information
|
||||
float radius;
|
||||
float rotation;
|
||||
} refEntity_t;
|
||||
|
||||
|
||||
#define MAX_RENDER_STRINGS 8
|
||||
#define MAX_RENDER_STRING_LENGTH 32
|
||||
|
||||
typedef struct {
|
||||
int x, y, width, height;
|
||||
float fov_x, fov_y;
|
||||
vec3_t vieworg;
|
||||
vec3_t viewaxis[3]; // transformation matrix
|
||||
|
||||
// time in milliseconds for shader effects and other time dependent rendering issues
|
||||
int time;
|
||||
|
||||
int rdflags; // RDF_NOWORLDMODEL, etc
|
||||
|
||||
// 1 bits will prevent the associated area from rendering at all
|
||||
byte areamask[MAX_MAP_AREA_BYTES];
|
||||
|
||||
// text messages for deform text shaders
|
||||
char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH];
|
||||
} refdef_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
STEREO_CENTER,
|
||||
STEREO_LEFT,
|
||||
STEREO_RIGHT
|
||||
} stereoFrame_t;
|
||||
|
||||
|
||||
/*
|
||||
** glconfig_t
|
||||
**
|
||||
** Contains variables specific to the OpenGL configuration
|
||||
** being run right now. These are constant once the OpenGL
|
||||
** subsystem is initialized.
|
||||
*/
|
||||
typedef enum {
|
||||
TC_NONE,
|
||||
TC_S3TC
|
||||
} textureCompression_t;
|
||||
|
||||
typedef enum {
|
||||
GLDRV_ICD, // driver is integrated with window system
|
||||
// WARNING: there are tests that check for
|
||||
// > GLDRV_ICD for minidriverness, so this
|
||||
// should always be the lowest value in this
|
||||
// enum set
|
||||
GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver
|
||||
GLDRV_VOODOO // driver is a 3Dfx standalone driver
|
||||
} glDriverType_t;
|
||||
|
||||
typedef enum {
|
||||
GLHW_GENERIC, // where everthing works the way it should
|
||||
GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is
|
||||
// the hardware type then there can NOT exist a secondary
|
||||
// display adapter
|
||||
GLHW_RIVA128, // where you can't interpolate alpha
|
||||
GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures
|
||||
GLHW_PERMEDIA2 // where you don't have src*dst
|
||||
} glHardwareType_t;
|
||||
|
||||
typedef struct {
|
||||
char renderer_string[MAX_STRING_CHARS];
|
||||
char vendor_string[MAX_STRING_CHARS];
|
||||
char version_string[MAX_STRING_CHARS];
|
||||
char extensions_string[BIG_INFO_STRING];
|
||||
|
||||
int maxTextureSize; // queried from GL
|
||||
int maxActiveTextures; // multitexture ability
|
||||
|
||||
int colorBits, depthBits, stencilBits;
|
||||
|
||||
glDriverType_t driverType;
|
||||
glHardwareType_t hardwareType;
|
||||
|
||||
qboolean deviceSupportsGamma;
|
||||
textureCompression_t textureCompression;
|
||||
qboolean textureEnvAddAvailable;
|
||||
|
||||
int vidWidth, vidHeight;
|
||||
// aspect is the screen's physical width / height, which may be different
|
||||
// than scrWidth / scrHeight if the pixels are non-square
|
||||
// normal screens should be 4/3, but wide aspect monitors may be 16/9
|
||||
float windowAspect;
|
||||
|
||||
int displayFrequency;
|
||||
|
||||
// synonymous with "does rendering consume the entire screen?", therefore
|
||||
// a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that
|
||||
// used CDS.
|
||||
qboolean isFullscreen;
|
||||
qboolean stereoEnabled;
|
||||
qboolean smpActive; // dual processor
|
||||
} glconfig_t;
|
||||
|
||||
// FIXME: VM should be OS agnostic .. in theory
|
||||
|
||||
/*
|
||||
#ifdef Q3_VM
|
||||
|
||||
#define _3DFX_DRIVER_NAME "Voodoo"
|
||||
#define OPENGL_DRIVER_NAME "Default"
|
||||
|
||||
#elif defined(_WIN32)
|
||||
*/
|
||||
|
||||
#if defined(Q3_VM) || defined(_WIN32)
|
||||
|
||||
#define _3DFX_DRIVER_NAME "3dfxvgl"
|
||||
#define OPENGL_DRIVER_NAME "opengl32"
|
||||
|
||||
#else
|
||||
|
||||
#define _3DFX_DRIVER_NAME "libMesaVoodooGL.so"
|
||||
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=524
|
||||
#define OPENGL_DRIVER_NAME "libGL.so.1"
|
||||
|
||||
#endif // !defined _WIN32
|
||||
|
||||
#endif // __TR_TYPES_H
|
Loading…
Add table
Add a link
Reference in a new issue