openmohaa/code/cgame/cg_main.c

938 lines
30 KiB
C
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
2023-04-30 00:02:16 +02:00
Copyright (C) 2023 the OpenMoHAA team
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
This file is part of OpenMoHAA source code.
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
OpenMoHAA source code is free software; you can redistribute it
2016-03-27 11:49:47 +02:00
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.
2023-04-30 00:02:16 +02:00
OpenMoHAA source code is distributed in the hope that it will be
2016-03-27 11:49:47 +02:00
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
2023-04-30 00:02:16 +02:00
along with OpenMoHAA source code; if not, write to the Free Software
2016-03-27 11:49:47 +02:00
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
2023-04-30 00:02:16 +02:00
// DESCRIPTION:
// Init functions for the cgame
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
#include "cg_local.h"
2023-05-01 00:11:26 +02:00
#include "cg_parsemsg.h"
#include "cg_archive.h"
#include "cg_radar.h"
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
#ifdef _WIN32
2023-07-05 21:24:23 +02:00
# include <windows.h>
2016-03-27 11:49:47 +02:00
#endif
2023-07-05 21:24:23 +02:00
clientGameImport_t cgi;
static clientGameExport_t cge;
2023-04-30 00:02:16 +02:00
2024-06-09 20:10:30 +02:00
cvar_t *paused;
cvar_t *developer;
cg_t cg;
cgs_t cgs;
target_game_e cg_target_game = TG_INVALID;
int cg_protocol;
centity_t cg_entities[MAX_GENTITIES];
2023-07-05 21:24:23 +02:00
cvar_t *cg_animSpeed;
cvar_t *cg_debugAnim;
cvar_t *cg_debugAnimWatch;
cvar_t *cg_errorDecay;
cvar_t *cg_nopredict;
cvar_t *cg_showmiss;
cvar_t *cg_addMarks;
cvar_t *cg_maxMarks;
cvar_t *cg_viewsize;
cvar_t *cg_3rd_person;
cvar_t *cg_drawviewmodel;
cvar_t *cg_synchronousClients;
cvar_t *cg_stereoSeparation;
cvar_t *cg_stats;
cvar_t *cg_lagometer;
cvar_t *r_lerpmodels;
cvar_t *cg_cameraheight;
cvar_t *cg_cameradist;
cvar_t *cg_cameraverticaldisplacement;
cvar_t *cg_camerascale;
cvar_t *cg_shadows;
cvar_t *cg_hidetempmodels;
cvar_t *cg_traceinfo;
cvar_t *cg_debugFootsteps;
cvar_t *cg_smoothClients;
cvar_t *cg_smoothClientsTime;
cvar_t *pmove_fixed;
cvar_t *pmove_msec;
cvar_t *cg_pmove_msec;
cvar_t *dm_playermodel;
cvar_t *dm_playergermanmodel;
cvar_t *cg_forceModel;
cvar_t *cg_animationviewmodel;
cvar_t *cg_hitmessages;
cvar_t *cg_acidtrip;
cvar_t *cg_hud;
cvar_t *cg_huddraw_force;
cvar_t *cg_drawsvlag;
2023-11-07 19:30:46 +01:00
cvar_t *cg_crosshair;
cvar_t *cg_crosshair_friend;
cvar_t *ui_crosshair;
2023-07-05 21:24:23 +02:00
cvar_t *vm_offset_max;
cvar_t *vm_offset_speed;
cvar_t *vm_sway_front;
cvar_t *vm_sway_side;
cvar_t *vm_sway_up;
cvar_t *vm_offset_air_front;
cvar_t *vm_offset_air_side;
cvar_t *vm_offset_air_up;
cvar_t *vm_offset_crouch_front;
cvar_t *vm_offset_crouch_side;
cvar_t *vm_offset_crouch_up;
cvar_t *vm_offset_rocketcrouch_front;
cvar_t *vm_offset_rocketcrouch_side;
cvar_t *vm_offset_rocketcrouch_up;
cvar_t *vm_offset_shotguncrouch_front;
cvar_t *vm_offset_shotguncrouch_side;
cvar_t *vm_offset_shotguncrouch_up;
cvar_t *vm_offset_vel_base;
cvar_t *vm_offset_vel_front;
cvar_t *vm_offset_vel_side;
cvar_t *vm_offset_vel_up;
cvar_t *vm_offset_upvel;
cvar_t *vm_lean_lower;
cvar_t *voiceChat;
cvar_t *cg_shadowscount;
cvar_t *cg_shadowdebug;
cvar_t *ui_timemessage;
2016-03-27 11:49:47 +02:00
/*
=================
CG_RegisterCvars
=================
*/
void CG_RegisterCvars(void)
{
2023-07-05 21:24:23 +02:00
cvar_t *temp;
cgi.Cvar_Get("g_subtitle", "0", CVAR_ARCHIVE);
2023-07-05 21:24:23 +02:00
cg_viewsize = cgi.Cvar_Get("viewsize", "100", CVAR_ARCHIVE);
cg_addMarks = cgi.Cvar_Get("cg_marks_add", "0", CVAR_ARCHIVE);
cg_maxMarks = cgi.Cvar_Get("cg_marks_max", "256", CVAR_ARCHIVE | CVAR_LATCH);
cg_animSpeed = cgi.Cvar_Get("cg_animspeed", "1", CVAR_CHEAT);
cg_debugAnim = cgi.Cvar_Get("cg_debuganim", "0", CVAR_CHEAT);
cg_debugAnimWatch = cgi.Cvar_Get("cg_debuganimwatch", "0", CVAR_CHEAT);
cg_errorDecay = cgi.Cvar_Get("cg_errordecay", "100", 0);
cg_nopredict = cgi.Cvar_Get("cg_nopredict", "0", 0);
cg_showmiss = cgi.Cvar_Get("cg_showmiss", "0", 0);
cg_stats = cgi.Cvar_Get("cg_stats", "0", 0);
cg_hidetempmodels = cgi.Cvar_Get("cg_hidetempmodels", "0", 0);
cg_synchronousClients = cgi.Cvar_Get("g_synchronousClients", "0", 0);
cg_stereoSeparation = cgi.Cvar_Get("cg_stereosep", "0.4", CVAR_ARCHIVE);
cg_lagometer = cgi.Cvar_Get("cg_lagometer", "0", 0);
paused = cgi.Cvar_Get("paused", "0", 0);
r_lerpmodels = cgi.Cvar_Get("r_lerpmodels", "1", 0);
cg_3rd_person = cgi.Cvar_Get("cg_3rd_person", "0", CVAR_CHEAT);
cg_drawviewmodel = cgi.Cvar_Get("cg_drawviewmodel", "2", CVAR_ARCHIVE);
cg_cameraheight = cgi.Cvar_Get("cg_cameraheight", "18", CVAR_ARCHIVE);
cg_cameradist = cgi.Cvar_Get("cg_cameradist", "120", CVAR_ARCHIVE);
cg_cameraverticaldisplacement = cgi.Cvar_Get("cg_cameraverticaldisplacement", "-2", CVAR_ARCHIVE);
2023-07-05 21:24:23 +02:00
cg_camerascale = cgi.Cvar_Get("cg_camerascale", "0.3", CVAR_ARCHIVE);
cg_traceinfo = cgi.Cvar_Get("cg_traceinfo", "0", CVAR_ARCHIVE);
cg_debugFootsteps = cgi.Cvar_Get("cg_debugfootsteps", "0", CVAR_CHEAT);
cg_smoothClients = cgi.Cvar_Get("cg_smoothClients", "1", CVAR_ARCHIVE);
cg_smoothClientsTime = cgi.Cvar_Get("cg_smoothClientsTime", "100", CVAR_ARCHIVE);
pmove_fixed = cgi.Cvar_Get("pmove_fixed", "0", 0);
pmove_msec = cgi.Cvar_Get("pmove_msec", "8", 0);
cg_pmove_msec = cgi.Cvar_Get("cg_pmove_msec", "8", 0);
cg_shadows = cgi.Cvar_Get("cg_shadows", "0", CVAR_ARCHIVE);
cg_shadowscount = cgi.Cvar_Get("cg_shadowscount", "8", 0);
cg_shadowdebug = cgi.Cvar_Get("cg_shadowdebug", "0", 0);
developer = cgi.Cvar_Get("developer", "0", 0);
dm_playermodel = cgi.Cvar_Get("dm_playermodel", "american_army", 3);
2024-02-21 20:12:05 +01:00
dm_playergermanmodel = cgi.Cvar_Get("dm_playergermanmodel", "german_wehrmacht_soldier", CVAR_ARCHIVE | CVAR_USERINFO);
2023-07-05 21:24:23 +02:00
cg_forceModel = cgi.Cvar_Get("cg_forceModel", "0", CVAR_ARCHIVE);
2024-02-21 20:12:05 +01:00
cg_animationviewmodel = cgi.Cvar_Get("cg_animationviewmodel", "0", CVAR_SYSTEMINFO);
2023-07-05 21:24:23 +02:00
cg_hitmessages = cgi.Cvar_Get("cg_hitmessages", "1", CVAR_ARCHIVE);
cg_acidtrip = cgi.Cvar_Get("cg_acidtrip", "0", CVAR_CHEAT);
cg_hud = cgi.Cvar_Get("cg_hud", "0", 0);
cg_huddraw_force = cgi.Cvar_Get("cg_huddraw_force", "0", CVAR_SAVEGAME);
cg_drawsvlag = cgi.Cvar_Get("cg_drawsvlag", "1", CVAR_ARCHIVE);
2023-11-07 19:30:46 +01:00
cg_crosshair = cgi.Cvar_Get("cg_crosshair", "textures/hud/crosshair", CVAR_ARCHIVE);
if (cg_protocol >= PROTOCOL_MOHTA_MIN) {
cg_crosshair_friend = cgi.Cvar_Get("cg_crosshair_friend", "textures/hud/crosshair_friend", CVAR_ARCHIVE);
} else {
// on 1.11 and below, fallback to standard crosshair
// as it doesn't have crosshair_friend texture
cg_crosshair_friend = cgi.Cvar_Get("cg_crosshair_friend", "textures/hud/crosshair", CVAR_ARCHIVE);
}
2023-11-07 19:30:46 +01:00
ui_crosshair = cgi.Cvar_Get("ui_crosshair", "1", CVAR_ARCHIVE);
2023-07-05 21:24:23 +02:00
vm_offset_max = cgi.Cvar_Get("vm_offset_max", "8.0", 0);
vm_offset_speed = cgi.Cvar_Get("vm_offset_speed", "8.0", 0);
vm_sway_front = cgi.Cvar_Get("vm_sway_front", "0.1", 0);
vm_sway_side = cgi.Cvar_Get("vm_sway_side", "0.005", 0);
vm_sway_up = cgi.Cvar_Get("vm_sway_up", "0.003", 0);
vm_offset_air_front = cgi.Cvar_Get("vm_offset_air_front", "-3.0", 0);
vm_offset_air_side = cgi.Cvar_Get("vm_offset_air_side", "1.5", 0);
vm_offset_air_up = cgi.Cvar_Get("vm_offset_air_up", "-6.0", 0);
vm_offset_crouch_front = cgi.Cvar_Get("vm_offset_crouch_front", "-0.5", 0);
vm_offset_crouch_side = cgi.Cvar_Get("vm_offset_crouch_side", "2.25", 0);
vm_offset_crouch_up = cgi.Cvar_Get("vm_offset_crouch_up", "0.2", 0);
vm_offset_rocketcrouch_front = cgi.Cvar_Get("vm_offset_rocketcrouch_front", "0", 0);
vm_offset_rocketcrouch_side = cgi.Cvar_Get("vm_offset_rocketcrouch_side", "0", 0);
vm_offset_rocketcrouch_up = cgi.Cvar_Get("vm_offset_rocketcrouch_up", "0", 0);
vm_offset_shotguncrouch_front = cgi.Cvar_Get("vm_offset_shotguncrouch_front", "-1", 0);
2023-07-05 21:24:23 +02:00
vm_offset_shotguncrouch_side = cgi.Cvar_Get("vm_offset_shotguncrouch_side", "2.5", 0);
vm_offset_shotguncrouch_up = cgi.Cvar_Get("vm_offset_shotguncrouch_up", "-1.1", 0);
vm_offset_vel_base = cgi.Cvar_Get("vm_offset_vel_base", "100", 0);
vm_offset_vel_front = cgi.Cvar_Get("vm_offset_vel_front", "-2.0", 0);
vm_offset_vel_side = cgi.Cvar_Get("vm_offset_vel_side", "1.5", 0);
vm_offset_vel_up = cgi.Cvar_Get("vm_offset_vel_up", "-4.0", 0);
vm_offset_upvel = cgi.Cvar_Get("vm_offset_upvel", "0.0025", 0);
vm_lean_lower = cgi.Cvar_Get("vm_lean_lower", "0.1", 0);
voiceChat = cgi.Cvar_Get("cg_voicechat", "1", 0);
ui_timemessage = cgi.Cvar_Get("ui_timemessage", "", 0);
// see if we are also running the server on this machine
2023-07-05 21:24:23 +02:00
temp = cgi.Cvar_Get("sv_running", "0", 0);
cgs.localServer = temp->integer;
}
2016-03-27 11:49:47 +02:00
/*
================
CG_RegisterSoundsForFile
2023-04-30 00:02:16 +02:00
Register the specified ubersound source file
================
2016-03-27 11:49:47 +02:00
*/
void CG_RegisterSoundsForFile(const char *name)
2023-05-01 00:11:26 +02:00
{
int startTime;
int endTime;
2023-05-01 00:11:26 +02:00
Com_Printf("\n\n-----------PARSING '%s'------------\n", name);
2023-05-01 00:11:26 +02:00
Com_Printf(
"Any SetCurrentTiki errors means that tiki wasn't prefetched and tiki-specific sounds for it won't work. To "
"fix prefetch the tiki. Ignore if you don't use that tiki on this level.\n"
2023-07-05 21:24:23 +02:00
);
2023-05-01 00:11:26 +02:00
startTime = cgi.Milliseconds();
CG_Command_ProcessFile(name, qfalse, NULL);
2023-05-01 00:11:26 +02:00
endTime = cgi.Milliseconds();
Com_Printf("Parse/Load time: %f seconds.\n", (float)(endTime - startTime) / 1000.0);
Com_Printf("-------------PARSING '%s' DONE---------------\n\n", name);
}
2023-05-01 00:11:26 +02:00
/*
=================
qsort_compare_strings
2023-05-01 00:11:26 +02:00
perform case-insensitive sorting
=================
*/
int qsort_compare_strings(const void *s1, const void *s2)
{
return Q_stricmp(*(const char **)s1, *(const char **)s2);
}
/*
=================
CG_RegisterSounds
Called during a precache command
=================
*/
void CG_RegisterSounds(void)
{
char **fileList;
int numFiles;
int i;
fileList = cgi.FS_ListFilteredFiles("ubersound/", "scr", "*.scr", qfalse, &numFiles, qtrue);
if (cg_target_game >= TG_MOHTA) {
// Fixed in 2.0
// The behavior has changed, all aliases get cleared
if (cgs.gametype != GT_SINGLE_PLAYER) {
cgi.Alias_Clear();
}
} else {
if (!cgs.localServer) {
cgi.Alias_Clear();
}
}
qsort(fileList, numFiles, sizeof(char *), &qsort_compare_strings);
for (i = 0; i < numFiles; i++) {
// Added in 2.0
// Since 2.0, all files in the ubersound folder
// are parsed
CG_RegisterSoundsForFile(va("ubersound/%s", fileList[i]));
}
2023-05-01 00:11:26 +02:00
cgi.FS_FreeFileList(fileList);
2016-03-27 11:49:47 +02:00
}
/*
================
CG_IsHandleUnique
Check if the model handle is unique
================
*/
static qboolean CG_IsHandleUnique(qhandle_t handle) {
int i;
int numRef;
numRef = 0;
for (i = 0; i < MAX_MODELS; i++) {
if (cgs.model_draw[i] == handle) {
numRef++;
if (numRef >= 2) {
return qfalse;
}
}
}
return qtrue;
}
2016-03-27 11:49:47 +02:00
/*
================
2023-04-30 00:02:16 +02:00
CG_ProcessConfigString
2016-03-27 11:49:47 +02:00
================
*/
void CG_ProcessConfigString(int num, qboolean modelOnly)
{
const char* str;
int i;
2023-07-05 21:24:23 +02:00
str = CG_ConfigString(num);
2023-07-05 21:24:23 +02:00
if (num >= CS_MODELS && num < CS_MODELS + MAX_MODELS) {
qhandle_t hOldModel = cgs.model_draw[num - CS_MODELS];
if (str && str[0] && !modelOnly) {
2023-07-05 21:24:23 +02:00
qhandle_t hModel = cgi.R_RegisterServerModel(str);
dtiki_t *tiki;
2023-07-05 21:24:23 +02:00
if (hModel != hOldModel) {
if (hOldModel) {
cgi.R_UnregisterServerModel(hOldModel);
}
2023-07-05 21:24:23 +02:00
cgs.model_draw[num - CS_MODELS] = hModel;
}
tiki = cgi.R_Model_GetHandle(hModel);
if (tiki) {
2023-05-01 00:11:26 +02:00
CG_ProcessCacheInitCommands(tiki);
}
2023-07-05 21:24:23 +02:00
} else {
// clear out the model
if (hOldModel && CG_IsHandleUnique(hOldModel)) {
// Handle uniqueness added in OPM
2023-07-05 21:24:23 +02:00
cgi.R_UnregisterServerModel(hOldModel);
}
cgs.model_draw[num - CS_MODELS] = 0;
}
}
if (!modelOnly) {
switch (num) {
case CS_RAIN_DENSITY:
cg.rain.density = atof(str);
return;
case CS_RAIN_SPEED:
cg.rain.speed = atof(str);
return;
case CS_RAIN_SPEEDVARY:
cg.rain.speed_vary = atoi(str);
return;
case CS_RAIN_SLANT:
cg.rain.slant = atoi(str);
return;
case CS_RAIN_LENGTH:
cg.rain.length = atof(str);
return;
case CS_RAIN_MINDIST:
cg.rain.min_dist = atof(str);
return;
case CS_RAIN_WIDTH:
cg.rain.width = atof(str);
return;
case CS_RAIN_SHADER:
2024-02-04 19:36:34 +01:00
Q_strncpyz(cg.rain.currentShader, str, sizeof(cg.rain.currentShader));
2024-03-02 14:23:32 +01:00
if (cg.rain.numshaders) {
// Fixed in OPM
// not sure why some maps set a digit at the end...
size_t len = strlen(cg.rain.currentShader);
2024-03-02 14:23:32 +01:00
if (isdigit(cg.rain.currentShader[len - 1])) {
cg.rain.currentShader[len - 1] = 0;
}
}
2024-02-04 19:36:34 +01:00
for (i = 0; i < cg.rain.numshaders; ++i) {
Com_sprintf(cg.rain.shader[i], sizeof(cg.rain.shader[i]), "%s%i", cg.rain.currentShader, i);
}
if (!cg.rain.numshaders) {
Q_strncpyz(cg.rain.shader[0], cg.rain.currentShader, sizeof(cg.rain.shader[0]));
}
return;
case CS_RAIN_NUMSHADERS:
cg.rain.numshaders = atoi(str);
if (cg.rain.numshaders) {
for (i = 0; i < cg.rain.numshaders; i++) {
2024-02-04 19:36:34 +01:00
Com_sprintf(cg.rain.shader[i], sizeof(cg.rain.shader[i]), "%s%i", cg.rain.currentShader, i);
}
2023-07-05 21:24:23 +02:00
}
return;
2024-02-04 19:25:03 +01:00
case CS_CURRENT_OBJECTIVE:
cg.ObjectivesCurrentIndex = atoi(str);
2024-02-04 19:36:34 +01:00
return;
2023-07-05 21:24:23 +02:00
}
if (num >= CS_OBJECTIVES && num < CS_OBJECTIVES + MAX_OBJECTIVES) {
cobjective_t *objective = &cg.Objectives[num - CS_OBJECTIVES];
objective->flags = atoi(Info_ValueForKey(str, "flags"));
2024-09-20 21:53:48 +02:00
Q_strncpyz(objective->text, Info_ValueForKey(str, "text"), sizeof(objective->text));
2023-05-22 01:44:50 +02:00
}
2023-07-05 21:24:23 +02:00
switch (num) {
case CS_MUSIC:
cgi.MUSIC_NewSoundtrack(str);
return;
case CS_WARMUP:
cg.matchStartTime = atoi(str);
return;
case CS_FOGINFO:
cg.farclipOverride = 0;
cg.farplaneColorOverride[0] = -1;
cg.farplaneColorOverride[1] = -1;
cg.farplaneColorOverride[2] = -1;
CG_ParseFogInfo(str);
return;
case CS_SKYINFO:
sscanf(str, "%f %d", &cg.sky_alpha, &cg.sky_portal);
return;
case CS_SERVERINFO:
CG_ParseServerinfo();
return;
case CS_LEVEL_START_TIME:
cgs.levelStartTime = atoi(str);
return;
case CS_VOTE_TIME:
cgs.voteTime = atoi(str);
cgs.voteRefreshed = qtrue;
break;
case CS_VOTE_STRING:
Q_strncpyz(cgs.voteString, str, sizeof(cgs.voteString));
break;
case CS_VOTE_YES:
cgs.numVotesYes = atoi(str);
cgs.voteRefreshed = qtrue;
break;
case CS_VOTE_NO:
cgs.numVotesNo = atoi(str);
cgs.voteRefreshed = qtrue;
break;
case CS_VOTE_UNDECIDED:
cgs.numUndecidedVotes = atoi(str);
cgs.voteRefreshed = qtrue;
break;
case CS_MATCHEND:
cgs.matchEndTime = atoi(str);
return;
}
if (num >= CS_SOUNDS && num < CS_SOUNDS + MAX_SOUNDS) {
size_t len = strlen(str);
if (len) {
qboolean streamed;
char buf[1024];
2024-09-20 21:53:48 +02:00
Q_strncpyz(buf, str, sizeof(buf));
streamed = buf[len - 1] != '0';
buf[len - 1] = 0;
if (buf[0] != '*') {
cgs.sound_precache[num - CS_SOUNDS] = cgi.S_RegisterSound(buf, streamed);
}
}
} else if (num >= CS_LIGHTSTYLES && num < CS_LIGHTSTYLES + MAX_LIGHTSTYLES) {
CG_SetLightStyle(num - CS_LIGHTSTYLES, str);
} else if (num >= CS_PLAYERS && num < CS_PLAYERS + MAX_CLIENTS) {
const char *value;
value = Info_ValueForKey(str, "name");
if (value) {
strncpy(cg.clientinfo[num - CS_PLAYERS].name, value, sizeof(cg.clientinfo[num - CS_PLAYERS].name));
} else {
strncpy(
cg.clientinfo[num - CS_PLAYERS].name, "UnnamedSoldier", sizeof(cg.clientinfo[num - CS_PLAYERS].name)
);
}
value = Info_ValueForKey(str, "team");
if (value) {
cg.clientinfo[num - CS_PLAYERS].team = atoi(value);
} else {
cg.clientinfo[num - CS_PLAYERS].team = TEAM_NONE;
}
2023-05-22 01:44:50 +02:00
}
}
}
2016-03-27 11:49:47 +02:00
//===================================================================================
/*
=================
2023-04-30 00:02:16 +02:00
CG_PrepRefresh
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
Call before entering a new level, or after changing renderers
2016-03-27 11:49:47 +02:00
This function may execute for a couple of minutes with a slow disk.
=================
*/
2023-07-05 21:24:23 +02:00
void CG_PrepRefresh(void)
{
int i;
memset(&cg.refdef, 0, sizeof(cg.refdef));
cgi.R_LoadWorldMap(cgs.mapname);
// register the inline models
cgs.numInlineModels = cgi.CM_NumInlineModels();
2023-07-05 21:24:23 +02:00
for (i = 1; i < cgs.numInlineModels; i++) {
char name[10];
vec3_t mins, maxs;
int j;
Com_sprintf(name, sizeof(name), "*%i", i);
cgs.inlineDrawModel[i] = cgi.R_RegisterModel(name);
cgi.R_ModelBounds(cgs.inlineDrawModel[i], mins, maxs);
2023-07-05 21:24:23 +02:00
for (j = 0; j < 3; j++) {
cgs.inlineModelMidpoints[i][j] = mins[j] + 0.5 * (maxs[j] - mins[j]);
}
}
// register media shaders
2023-07-05 21:24:23 +02:00
cgs.media.shadowMarkShader = cgi.R_RegisterShader("markShadow");
cgs.media.footShadowMarkShader = cgi.R_RegisterShader("footShadow");
cgs.media.wakeMarkShader = cgi.R_RegisterShader("ripple.spr");
cgs.media.lagometerShader = cgi.R_RegisterShaderNoMip("gfx/2d/blank");
cgs.media.levelExitShader = cgi.R_RegisterShaderNoMip("textures/menu/exit");
cgs.media.pausedShader = cgi.R_RegisterShaderNoMip("textures/menu/paused");
cgs.media.backTileShader = cgi.R_RegisterShader("gfx/2d/backtile");
cgs.media.zoomOverlayShader = cgi.R_RegisterShaderNoMip("textures/hud/zoomoverlay");
cgs.media.kar98TopOverlayShader = cgi.R_RegisterShaderNoMip("textures/hud/kartop.tga");
cgs.media.kar98BottomOverlayShader = cgi.R_RegisterShaderNoMip("textures/hud/karbottom.tga");
2023-07-05 21:24:23 +02:00
cgs.media.binocularsOverlayShader = cgi.R_RegisterShaderNoMip("textures/hud/binocularsoverlay");
cgs.media.hudDrawFont = cgi.R_LoadFont("verdana-14");
cgs.media.attackerFont = cgi.R_LoadFont("facfont-20");
2023-08-27 17:48:18 +02:00
cgs.media.objectiveFont = cgi.R_LoadFont("facfont-20"); // was courier-16 before 2.0
2023-07-05 21:24:23 +02:00
cgs.media.objectivesBackShader = cgi.R_RegisterShaderNoMip("textures/hud/objectives_backdrop");
cgs.media.checkedBoxShader = cgi.R_RegisterShaderNoMip("textures/objectives/filledbox");
cgs.media.uncheckedBoxShader = cgi.R_RegisterShaderNoMip("textures/objectives/emptybox");
// go through all the configstrings and process them
2023-07-05 21:24:23 +02:00
for (i = CS_SYSTEMINFO + 1; i < MAX_CONFIGSTRINGS; i++) {
CG_ProcessConfigString(i, qfalse);
}
}
2016-03-27 11:49:47 +02:00
//===========================================================================
/*
=================
CG_ConfigString
=================
*/
2023-07-05 21:24:23 +02:00
const char *CG_ConfigString(int index)
{
if (index < 0 || index >= MAX_CONFIGSTRINGS) {
cgi.Error(ERR_DROP, "CG_ConfigString: bad index: %i", index);
}
return cgs.gameState.stringData + cgs.gameState.stringOffsets[index];
2016-03-27 11:49:47 +02:00
}
//==================================================================
void CG_GetRendererConfig(void)
{
// get the rendering configuration from the client system
cgi.GetGlconfig(&cgs.glconfig);
cgs.screenXScale = cgs.glconfig.vidWidth / 640.0;
cgs.screenYScale = cgs.glconfig.vidHeight / 480.0;
2024-11-30 23:36:37 +01:00
cgi.UI_GetHighResolutionScale(cgs.uiHiResScale);
}
2016-03-27 11:49:47 +02:00
/*
2023-04-30 00:02:16 +02:00
======================
CG_GameStateReceived
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
Displays the info screen while loading media
======================
2016-03-27 11:49:47 +02:00
*/
2023-07-05 21:24:23 +02:00
void CG_GameStateReceived(void)
{
2023-07-05 21:24:23 +02:00
const char *s;
int checksum;
2016-03-27 11:49:47 +02:00
// clear everything
memset(&cg, 0, sizeof(cg));
memset(cg_entities, 0, sizeof(cg_entities));
2016-03-27 11:49:47 +02:00
// clear the light styles
CG_ClearLightStyles();
2016-03-27 11:49:47 +02:00
// get the rendering configuration from the client system
CG_GetRendererConfig();
2016-03-27 11:49:47 +02:00
// get the gamestate from the client system
cgi.GetGameState(&cgs.gameState);
2016-03-27 11:49:47 +02:00
// check version
s = CG_ConfigString(CS_GAME_VERSION);
if (strcmp(s, GAME_VERSION)) {
cgi.Error(ERR_DROP, "Client/Server game mismatch: %s/%s", GAME_VERSION, s);
}
2023-07-05 21:24:23 +02:00
s = CG_ConfigString(CS_LEVEL_START_TIME);
cgs.levelStartTime = atoi(s);
2016-03-27 11:49:47 +02:00
CG_ParseServerinfo();
2016-03-27 11:49:47 +02:00
// load the new map
cgi.CM_LoadMap(cgs.mapname, &checksum);
if (cgs.useMapChecksum && checksum != cgs.mapChecksum && cgs.gametype != GT_SINGLE_PLAYER) {
cgi.Error(ERR_DROP, "Client/Server map checksum mismatch: %x/%x", checksum, cgs.mapChecksum);
}
2016-03-27 11:49:47 +02:00
CG_InitMarks();
2016-03-27 11:49:47 +02:00
CG_RegisterSounds();
2023-04-30 00:02:16 +02:00
CG_PrepRefresh();
2016-03-27 11:49:47 +02:00
CG_InitializeSpecialEffectsManager();
CG_InitializeObjectives();
}
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
/*
======================
CG_ServerRestarted
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
The server has beeen restarted, adjust our cgame data accordingly
======================
*/
2023-07-05 21:24:23 +02:00
void CG_ServerRestarted(void)
{
2023-07-05 21:24:23 +02:00
const char *s;
2023-07-05 21:24:23 +02:00
s = CG_ConfigString(CS_LEVEL_START_TIME);
cgs.levelStartTime = atoi(s);
CG_ParseServerinfo();
cg.thisFrameTeleport = qtrue;
// free up any temp models currently spawned
CG_RestartCommandManager();
// get rid of left over decals from the last game
CG_InitMarks();
// clear all the swipes
CG_ClearSwipes();
// Reset tempmodels
CG_ResetTempModels();
// Reset resources
CG_ResetVSSSources();
// Reset objectives
CG_InitializeObjectives();
}
2023-04-30 00:02:16 +02:00
/*
=================
CG_Init
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
Called after every level change or subsystem restart
=================
*/
2023-07-05 21:24:23 +02:00
void CG_Init(clientGameImport_t *imported, int serverMessageNum, int serverCommandSequence, int clientNum)
{
cgi = *imported;
2016-03-27 11:49:47 +02:00
cg_protocol = cgi.Cvar_Get("com_protocol", "", 0)->integer;
2024-06-09 20:10:30 +02:00
cg_target_game = (target_game_e)cgi.Cvar_Get("com_target_game", "0", 0)->integer;
CG_InitCGMessageAPI(&cge);
CG_InitScoresAPI(&cge);
memset(&cg, 0, sizeof(cg));
memset(&cgs, 0, sizeof(cgs));
2023-08-28 20:22:43 +02:00
// clear fog values
cg.farclipOverride = 0;
cg.farplaneColorOverride[0] = -1;
cg.farplaneColorOverride[1] = -1;
cg.farplaneColorOverride[2] = -1;
2023-07-05 21:24:23 +02:00
cg.clientNum = clientNum;
cgs.processedSnapshotNum = serverMessageNum;
cgs.serverCommandSequence = serverCommandSequence;
CG_RegisterCvars();
2016-03-27 11:49:47 +02:00
L_InitEvents();
2016-03-27 11:49:47 +02:00
// init swapping for endian conversion
Swap_Init();
2016-03-27 11:49:47 +02:00
CG_InitializeCommandManager();
2016-03-27 11:49:47 +02:00
CG_GameStateReceived();
2016-03-27 11:49:47 +02:00
CG_InitConsoleCommands();
2016-03-27 11:49:47 +02:00
2023-07-05 21:24:23 +02:00
cg.vEyeOffsetMax[0] = 40.0f;
cg.vEyeOffsetMax[1] = 45.0f;
cg.vEyeOffsetMax[2] = 60.0f;
cg.fEyeOffsetFrac = 0.1f;
cg.fCurrentViewHeight = 0.0f;
cg.fCurrentViewBobPhase = 0.0f;
cg.fCurrentViewBobAmp = 0.0f;
cg.pLastPlayerWorldModel = NULL;
cg.pPlayerFPSModel = NULL;
cg.hPlayerFPSModelHandle = 0;
cg.pAlliedPlayerModel = NULL;
cg.hAlliedPlayerModelHandle = 0;
2023-07-05 21:24:23 +02:00
cg.pAxisPlayerModel = NULL;
cg.hAxisPlayerModelHandle = 0;
cg.bFPSOnGround = qtrue;
2023-04-30 00:02:16 +02:00
// Pop the stats UI screen menu
cgi.UI_HideMenu("StatsScreen", 1);
2023-04-30 00:02:16 +02:00
// Scoreboard setup
CG_PrepScoreBoardInfo();
cgi.UI_HideScoreBoard();
// HUD setup
CG_RefreshHudDrawElements();
cgi.Cmd_Execute(EXEC_NOW, "ui_hud 1\n");
}
2016-03-27 11:49:47 +02:00
/*
=================
CG_Shutdown
Called before every level change or subsystem restart
=================
*/
void CG_Shutdown(void)
{
L_ShutdownEvents();
2023-11-26 19:30:28 +01:00
// Shutdown radar
cgi.CL_InitRadar(NULL, NULL, -1);
// some mods may need to do cleanup work here,
// like closing files or archiving session data
// hide the stats screen
2023-11-26 19:30:28 +01:00
cgi.UI_HideMenu("StatsScreen", qtrue);
// reset the scoreboard
CG_PrepScoreBoardInfo();
cgi.UI_HideScoreBoard();
}
2023-04-30 00:02:16 +02:00
2023-05-01 00:11:26 +02:00
int CG_GetParent(int entnum)
{
2023-07-05 21:24:23 +02:00
return cg_entities[entnum].currentState.parent;
2023-05-01 00:11:26 +02:00
}
float CG_GetObjectiveAlpha()
{
return cg.ObjectivesCurrentAlpha;
}
2016-03-27 11:49:47 +02:00
/*
================
GetCGameAPI
2023-04-30 00:02:16 +02:00
The only exported function from this module
2016-03-27 11:49:47 +02:00
================
*/
2023-04-30 23:29:21 +02:00
clientGameExport_t *GetCGameAPI(void)
{
2023-07-05 21:24:23 +02:00
cge.CG_Init = CG_Init;
cge.CG_DrawActiveFrame = CG_DrawActiveFrame;
cge.CG_Shutdown = CG_Shutdown;
cge.CG_ConsoleCommand = CG_ConsoleCommand;
cge.CG_GetRendererConfig = CG_GetRendererConfig;
cge.CG_Draw2D = CG_Draw2D;
cge.CG_EyePosition = CG_EyePosition;
cge.CG_EyeOffset = CG_EyeOffset;
cge.CG_EyeAngles = CG_EyeAngles;
cge.CG_SensitivityScale = CG_SensitivityScale;
cge.CG_RefreshHudDrawElements = CG_RefreshHudDrawElements;
cge.CG_HudDrawShader = CG_HudDrawShader;
cge.CG_HudDrawFont = CG_HudDrawFont;
cge.CG_PermanentMark = CG_PermanentMark;
cge.CG_PermanentTreadMarkDecal = CG_PermanentTreadMarkDecal;
cge.CG_PermanentUpdateTreadMark = CG_PermanentUpdateTreadMark;
cge.CG_Command_ProcessFile = CG_Command_ProcessFile;
cge.CG_ProcessInitCommands = CG_ProcessInitCommands;
cge.CG_EndTiki = CG_EndTiki;
cge.CG_GetParent = CG_GetParent;
cge.CG_GetObjectiveAlpha = CG_GetObjectiveAlpha;
cge.CG_WeaponCommandButtonBits = CG_WeaponCommandButtonBits;
cge.CG_CheckCaptureKey = CG_CheckCaptureKey;
cge.CG_ReadNonPVSClient = CG_ReadNonPVSClient;
cge.CG_UpdateRadar = CG_UpdateRadar;
cge.CG_SaveStateToBuffer = CG_SaveStateToBuffer;
cge.CG_LoadStateToBuffer = CG_LoadStateToBuffer;
cge.CG_CleanUpTempModels = CG_CleanUpTempModels;
2023-07-05 21:24:23 +02:00
// FIXME
//cge.profStruct = NULL;
return &cge;
2023-04-30 23:29:21 +02:00
}
2016-03-27 11:49:47 +02:00
/*
2023-04-30 00:02:16 +02:00
=====================
CG_DrawActive
2016-03-27 11:49:47 +02:00
2023-04-30 00:02:16 +02:00
Perform all drawing needed to completely fill the screen
=====================
2016-03-27 11:49:47 +02:00
*/
void CG_DrawActive(stereoFrame_t stereoView)
{
2023-07-05 21:24:23 +02:00
float separation;
vec3_t baseOrg;
switch (stereoView) {
case STEREO_CENTER:
separation = 0;
break;
case STEREO_LEFT:
separation = -cg_stereoSeparation->value / 2;
break;
case STEREO_RIGHT:
separation = cg_stereoSeparation->value / 2;
break;
default:
separation = 0;
cgi.Error(ERR_DROP, "CG_DrawActive: Undefined stereoView");
}
// clear around the rendered view if sized down
CG_TileClear();
// offset vieworg appropriately if we're doing stereo separation
VectorCopy(cg.refdef.vieworg, baseOrg);
if (separation != 0) {
2023-07-05 21:24:23 +02:00
VectorMA(cg.refdef.vieworg, -separation, cg.refdef.viewaxis[1], cg.refdef.vieworg);
}
// draw 3D view
cgi.R_RenderScene(&cg.refdef);
// restore original viewpoint if running stereo
if (separation != 0) {
VectorCopy(baseOrg, cg.refdef.vieworg);
}
}
2023-04-30 00:02:16 +02:00
#ifndef CGAME_HARD_LINKED
// this is only here so the functions in q_shared.c and bg_*.c can link (FIXME)
2023-07-05 21:24:23 +02:00
void Com_Error(int level, const char *error, ...)
{
va_list argptr;
char text[1024];
2016-03-27 11:49:47 +02:00
2023-07-05 21:24:23 +02:00
va_start(argptr, error);
2024-09-20 21:53:48 +02:00
Q_vsnprintf(text, sizeof(text), error, argptr);
2023-07-05 21:24:23 +02:00
va_end(argptr);
2016-03-27 11:49:47 +02:00
2023-07-05 21:24:23 +02:00
cgi.Error(level, "%s", text);
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:24:23 +02:00
void Com_Printf(const char *msg, ...)
{
va_list argptr;
char text[1024];
2016-03-27 11:49:47 +02:00
2023-07-05 21:24:23 +02:00
va_start(argptr, msg);
2024-09-20 21:53:48 +02:00
Q_vsnprintf(text, sizeof(text), msg, argptr);
2023-07-05 21:24:23 +02:00
va_end(argptr);
2016-03-27 11:49:47 +02:00
2023-07-05 21:24:23 +02:00
cgi.Printf("%s", text);
2016-03-27 11:49:47 +02:00
}
2023-04-30 00:02:16 +02:00
#endif
2023-07-05 21:24:23 +02:00
void CG_ParseFogInfo_ver_15(const char *str)
{
sscanf(
str,
2023-07-05 21:24:23 +02:00
"%d %f %f %f %f %f %f %f %d %f %f %f %f",
&cg.farplane_cull,
&cg.farplane_distance,
&cg.farplane_bias,
&cg.skyboxFarplane,
&cg.skyboxSpeed,
&cg.farplane_color[0],
&cg.farplane_color[1],
&cg.farplane_color[2],
&cg.renderTerrain,
&cg.farclipOverride,
&cg.farplaneColorOverride[0],
&cg.farplaneColorOverride[1],
&cg.farplaneColorOverride[2]
);
}
2023-07-05 21:24:23 +02:00
void CG_ParseFogInfo_ver_6(const char *str)
{
//
// clear all unsupported fields in protocol below version 15
//
// don't set the farplane_bias 0, otherwise the renderer will set a minimum value
cg.farplane_bias = 0.001f;
cg.skyboxFarplane = 0;
cg.skyboxSpeed = 0;
cg.renderTerrain = qtrue;
cg.farclipOverride = -1.0;
2023-07-05 21:24:23 +02:00
cg.farplaneColorOverride[0] = -1.0;
cg.farplaneColorOverride[1] = -1.0;
cg.farplaneColorOverride[2] = -1.0;
2023-07-05 21:24:23 +02:00
sscanf(
str,
"%d %f %f %f %f",
&cg.farplane_cull,
&cg.farplane_distance,
&cg.farplane_color[0],
&cg.farplane_color[1],
&cg.farplane_color[2]
);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:24:23 +02:00
void CG_ParseFogInfo(const char *str)
{
if (cg_protocol >= PROTOCOL_MOHTA_MIN) {
CG_ParseFogInfo_ver_15(str);
} else {
CG_ParseFogInfo_ver_6(str);
}
}