mirror of
https://github.com/id-Software/Quake-III-Arena.git
synced 2026-05-14 09:59:07 +02:00
The Quake III Arena sources as originally released under the GPL license on August 20, 2005.
This commit is contained in:
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
//----------------------------------------------
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 ..
|
||||
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
vmMain
|
||||
dllEntry
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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 ..
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 ..
|
||||
@@ -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
|
||||
@@ -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 ..
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user