// cl_cgame.c -- client system interaction with client game //Anything above this #include will be ignored by the compiler #include "../qcommon/exe_headers.h" #include "../RMG/RM_Headers.h" #include "client.h" #include "../game/botlib.h" #include "../RMG/RM_Headers.h" #if !defined(FX_EXPORT_H_INC) #include "FXExport.h" #endif #include "FXutil.h" #if !defined(CROFFSYSTEM_H_INC) #include "../qcommon/ROFFSystem.h" #endif #ifdef _DONETPROFILE_ #include "../qcommon/INetProfile.h" #endif #include "../renderer/tr_worldeffects.h" /* Ghoul2 Insert Start */ #if !defined(G2_H_INC) #include "../ghoul2/G2_local.h" #endif #include "../qcommon/stringed_ingame.h" #include "../ghoul2/G2_gore.h" extern CMiniHeap *G2VertSpaceClient; #include "snd_ambient.h" #include "../qcommon/timing.h" #include "../renderer/tr_local.h" //extern int contentOverride; /* Ghoul2 Insert End */ #include "../cgame/cg_local.h" #ifdef _XBOX #include "cl_data.h" #endif extern botlib_export_t *botlib_export; extern qboolean loadCamera(const char *name); extern void startCamera(int time); extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles); void FX_FeedTrail(effectTrailArgStruct_t *a); int CM_LoadSubBSP(const char *name, qboolean clientload); void RE_InitRendererTerrain( const char *info ); /* ==================== CL_GetGameState ==================== */ void CL_GetGameState( gameState_t *gs ) { *gs = cl->gameState; } /* ==================== CL_GetGlconfig ==================== */ void CL_GetGlconfig( glconfig_t *glconfig ) { *glconfig = cls.glconfig; } /* ==================== CL_GetUserCmd ==================== */ qboolean CL_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { // cmds[cmdNumber] is the last properly generated command // can't return anything that we haven't created yet if ( cmdNumber > cl->cmdNumber ) { Com_Error( ERR_DROP, "CL_GetUserCmd: %i >= %i", cmdNumber, cl->cmdNumber ); } // the usercmd has been overwritten in the wrapping // buffer because it is too far out of date if ( cmdNumber <= cl->cmdNumber - CMD_BACKUP ) { return qfalse; } *ucmd = cl->cmds[ cmdNumber & CMD_MASK ]; return qtrue; } int CL_GetCurrentCmdNumber( void ) { return cl->cmdNumber; } /* ==================== CL_GetParseEntityState ==================== */ qboolean CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) { // can't return anything that hasn't been parsed yet if ( parseEntityNumber >= cl->parseEntitiesNum ) { Com_Error( ERR_DROP, "CL_GetParseEntityState: %i >= %i", parseEntityNumber, cl->parseEntitiesNum ); } // can't return anything that has been overwritten in the circular buffer if ( parseEntityNumber <= cl->parseEntitiesNum - MAX_PARSE_ENTITIES ) { return qfalse; } *state = cl->parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ]; return qtrue; } /* ==================== CL_GetCurrentSnapshotNumber ==================== */ void CL_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { *snapshotNumber = cl->snap.messageNum; *serverTime = cl->snap.serverTime; } /* ==================== CL_GetSnapshot ==================== */ qboolean CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { clSnapshot_t *clSnap; int i, count; if ( snapshotNumber > cl->snap.messageNum ) { Com_Error( ERR_DROP, "CL_GetSnapshot: snapshotNumber > cl->snapshot.messageNum" ); } // if the frame has fallen out of the circular buffer, we can't return it if ( cl->snap.messageNum - snapshotNumber >= PACKET_BACKUP ) { return qfalse; } // if the frame is not valid, we can't return it clSnap = &cl->snapshots[snapshotNumber & PACKET_MASK]; if ( !clSnap->valid ) { return qfalse; } // if the entities in the frame have fallen out of their // circular buffer, we can't return it if ( cl->parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES ) { return qfalse; } // write the snapshot snapshot->snapFlags = clSnap->snapFlags; snapshot->serverCommandSequence = clSnap->serverCommandNum; snapshot->ping = clSnap->ping; snapshot->serverTime = clSnap->serverTime; Com_Memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) ); snapshot->ps = clSnap->ps; snapshot->vps = clSnap->vps; //get the vehicle ps count = clSnap->numEntities; if ( count > MAX_ENTITIES_IN_SNAPSHOT ) { Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT ); count = MAX_ENTITIES_IN_SNAPSHOT; } snapshot->numEntities = count; for ( i = 0 ; i < count ; i++ ) { int entNum = ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ; // copy everything but the ghoul2 pointer memcpy(&snapshot->entities[i], &cl->parseEntities[ entNum ], sizeof(entityState_t)); } // FIXME: configstring changes and server commands!!! return qtrue; } qboolean CL_GetDefaultState(int index, entityState_t *state) { if (index < 0 || index >= MAX_GENTITIES) { return qfalse; } if (!(cl->entityBaselines[index].eFlags & EF_PERMANENT)) { return qfalse; } *state = cl->entityBaselines[index]; return qtrue; } /* ===================== CL_SetUserCmdValue ===================== */ extern float cl_mPitchOverride; extern float cl_mYawOverride; extern float cl_mSensitivityOverride; void CL_SetUserCmdValue( int userCmdValue, float sensitivityScale, float mPitchOverride, float mYawOverride, float mSensitivityOverride, int fpSel, int invenSel ) { cl->cgameUserCmdValue = userCmdValue; cl->cgameSensitivity = sensitivityScale; cl_mPitchOverride = mPitchOverride; cl_mYawOverride = mYawOverride; cl_mSensitivityOverride = mSensitivityOverride; cl->cgameForceSelection = fpSel; cl->cgameInvenSelection = invenSel; } /* ===================== CL_SetClientForceAngle ===================== */ void CL_SetClientForceAngle(int time, vec3_t angle) { cl->cgameViewAngleForceTime = time; VectorCopy(angle, cl->cgameViewAngleForce); } /* ===================== CL_AddCgameCommand ===================== */ void CL_AddCgameCommand( const char *cmdName ) { Cmd_AddCommand( cmdName, NULL ); } /* ===================== CL_CgameError ===================== */ void CL_CgameError( const char *string ) { Com_Error( ERR_DROP, "%s", string ); } int gCLTotalClientNum = 0; //keep track of the total number of clients extern cvar_t *cl_autolodscale; //if we want to do autolodscaling void CL_DoAutoLODScale(void) { float finalLODScaleFactor = 0; if ( gCLTotalClientNum >= 8 ) { finalLODScaleFactor = (gCLTotalClientNum/-8.0f); } Cvar_Set( "r_autolodscalevalue", va("%f", finalLODScaleFactor) ); } /* ===================== CL_ConfigstringModified ===================== */ void CL_ConfigstringModified( void ) { char *old, *s; int i, index; char *dup; gameState_t oldGs; int len; index = atoi( Cmd_Argv(1) ); if ( index < 0 || index >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } // get everything after "cs " s = Cmd_ArgsFrom(2); old = cl->gameState.stringData + cl->gameState.stringOffsets[ index ]; if ( !strcmp( old, s ) ) { return; // unchanged } // build the new gameState_t oldGs = cl->gameState; Com_Memset( &cl->gameState, 0, sizeof( cl->gameState ) ); // leave the first 0 for uninitialized strings cl->gameState.dataCount = 1; for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { if ( i == index ) { dup = s; } else { dup = oldGs.stringData + oldGs.stringOffsets[ i ]; } if ( !dup[0] ) { continue; // leave with the default empty string } len = strlen( dup ); if ( len + 1 + cl->gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl->gameState.stringOffsets[ i ] = cl->gameState.dataCount; Com_Memcpy( cl->gameState.stringData + cl->gameState.dataCount, dup, len + 1 ); cl->gameState.dataCount += len + 1; } if (cl_autolodscale && cl_autolodscale->integer) { if (index >= CS_PLAYERS && index < CS_G2BONES) { //this means that a client was updated in some way. Go through and count the clients. int clientCount = 0; i = CS_PLAYERS; while (i < CS_G2BONES) { s = cl->gameState.stringData + cl->gameState.stringOffsets[ i ]; if (s && s[0]) { clientCount++; } i++; } gCLTotalClientNum = clientCount; #ifdef _DEBUG Com_DPrintf("%i clients\n", gCLTotalClientNum); #endif CL_DoAutoLODScale(); } } if ( index == CS_SYSTEMINFO ) { // parse serverId and other cvars CL_SystemInfoChanged(); } } #ifndef MAX_STRINGED_SV_STRING #define MAX_STRINGED_SV_STRING 1024 #endif // just copied it from CG_CheckSVStringEdRef( void CL_CheckSVStringEdRef(char *buf, const char *str) { //I don't really like doing this. But it utilizes the system that was already in place. int i = 0; int b = 0; int strLen = 0; qboolean gotStrip = qfalse; if (!str || !str[0]) { if (str) { strcpy(buf, str); } return; } strcpy(buf, str); strLen = strlen(str); if (strLen >= MAX_STRINGED_SV_STRING) { return; } while (i < strLen && str[i]) { gotStrip = qfalse; if (str[i] == '@' && (i+1) < strLen) { if (str[i+1] == '@' && (i+2) < strLen) { if (str[i+2] == '@' && (i+3) < strLen) { //@@@ should mean to insert a StringEd reference here, so insert it into buf at the current place char stringRef[MAX_STRINGED_SV_STRING]; int r = 0; while (i < strLen && str[i] == '@') { i++; } while (i < strLen && str[i] && str[i] != ' ' && str[i] != ':' && str[i] != '.' && str[i] != '\n') { stringRef[r] = str[i]; r++; i++; } stringRef[r] = 0; buf[b] = 0; Q_strcat(buf, MAX_STRINGED_SV_STRING, SE_GetString("MP_SVGAME", stringRef)); b = strlen(buf); } } } if (!gotStrip) { buf[b] = str[i]; b++; } i++; } buf[b] = 0; } /* =================== CL_GetServerCommand Set up argc/argv for the given command =================== */ qboolean CL_GetServerCommand( int serverCommandNumber ) { char *s; char *cmd; static char bigConfigString[BIG_INFO_STRING]; // if we have irretrievably lost a reliable command, drop the connection if ( serverCommandNumber <= clc->serverCommandSequence - MAX_RELIABLE_COMMANDS ) { int i = 0; // when a demo record was started after the client got a whole bunch of // reliable commands then the client never got those first reliable commands #ifndef _XBOX // No demos on Xbox if ( clc->demoplaying ) return qfalse; #endif while (i < MAX_RELIABLE_COMMANDS) { //spew out the reliable command buffer if (clc->reliableCommands[i][0]) { Com_Printf("%i: %s\n", i, clc->reliableCommands[i]); } i++; } Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" ); return qfalse; } if ( serverCommandNumber > clc->serverCommandSequence ) { Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" ); return qfalse; } s = clc->serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ]; clc->lastExecutedServerCommand = serverCommandNumber; Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s ); rescan: Cmd_TokenizeString( s ); cmd = Cmd_Argv(0); if ( !strcmp( cmd, "disconnect" ) ) { // char strEd[MAX_STRINGED_SV_STRING]; // CL_CheckSVStringEdRef(strEd, Cmd_Argv(1)); // These now always JUST send a string_ref: Com_Error ( ERR_SERVERDISCONNECT, Cmd_Argv(1) ); } if ( !strcmp( cmd, "bcs0" ) ) { Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) ); return qfalse; } if ( !strcmp( cmd, "bcs1" ) ) { s = Cmd_Argv(2); if( strlen(bigConfigString) + strlen(s) >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); } strcat( bigConfigString, s ); return qfalse; } if ( !strcmp( cmd, "bcs2" ) ) { s = Cmd_Argv(2); if( strlen(bigConfigString) + strlen(s) + 1 >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); } strcat( bigConfigString, s ); strcat( bigConfigString, "\"" ); s = bigConfigString; goto rescan; } if ( !strcmp( cmd, "cs" ) ) { CL_ConfigstringModified(); // reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString() Cmd_TokenizeString( s ); return qtrue; } if ( !strcmp( cmd, "map_restart" ) ) { // clear notify lines and outgoing commands before passing // the restart to the cgame Con_ClearNotify(); Com_Memset( cl->cmds, 0, sizeof( cl->cmds ) ); return qtrue; } // the clientLevelShot command is used during development // to generate 128*128 screenshots from the intermission // point of levels for the menu system to use // we pass it along to the cgame to make apropriate adjustments, // but we also clear the console and notify lines here if ( !strcmp( cmd, "clientLevelShot" ) ) { // don't do it if we aren't running the server locally, // otherwise malicious remote servers could overwrite // the existing thumbnails if ( !com_sv_running->integer ) { return qfalse; } // close the console Con_Close(); // take a special screenshot next frame Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" ); return qtrue; } // we may want to put a "connect to other server" command here // cgame can now act on the command return qtrue; } /* ==================== CL_CM_LoadMap Just adds default parameters that cgame doesn't need to know about ==================== */ void CL_CM_LoadMap( const char *mapname ) { int checksum; CM_LoadMap( mapname, qtrue, &checksum ); } /* ==================== CL_ShutdonwCGame ==================== */ void CL_ShutdownCGame( void ) { cls.keyCatchers &= ~KEYCATCH_CGAME; cls.cgameStarted = qfalse; if ( !cgvm ) { return; } VM_Call( cgvm, CG_SHUTDOWN ); VM_Free( cgvm ); cgvm = NULL; #ifdef _DONETPROFILE_ ClReadProf().ShowTotals(); #endif } static int FloatAsInt( float f ) { int temp; *(float *)&temp = f; return temp; } /* ==================== CL_CgameSystemCalls The cgame module is making a system call ==================== */ #define VMA(x) VM_ArgPtr(args[x]) #define VMF(x) ((float *)args)[x] extern int s_entityWavVol[MAX_GENTITIES]; void R_WorldEffectCommand(const char *command); extern int CL_GetValueForHidden(const char *s); //cl_parse.cpp extern void R_AutomapElevationAdjustment(float newHeight); //tr_world.cpp extern qboolean R_InitializeWireframeAutomap(void); //tr_world.cpp /* extern float tr_distortionAlpha; //tr_shadows.cpp extern float tr_distortionStretch; //tr_shadows.cpp extern qboolean tr_distortionPrePost; //tr_shadows.cpp extern qboolean tr_distortionNegate; //tr_shadows.cpp */ extern qboolean cl_bUseFighterPitch; //cl_input.cpp int CL_CgameSystemCalls( int *args ) { switch( args[0] ) { //rww - alright, DO NOT EVER add a GAME/CGAME/UI generic call without adding a trap to match, and //all of these traps must be shared and have cases in sv_game, cl_cgame, and cl_ui. They must also //all be in the same order, and start at 100. case TRAP_MEMSET: Com_Memset( VMA(1), args[2], args[3] ); return 0; case TRAP_MEMCPY: Com_Memcpy( VMA(1), VMA(2), args[3] ); return 0; case TRAP_STRNCPY: return (int)strncpy( (char *)VMA(1), (const char *)VMA(2), args[3] ); case TRAP_SIN: return FloatAsInt( sin( VMF(1) ) ); case TRAP_COS: return FloatAsInt( cos( VMF(1) ) ); case TRAP_ATAN2: return FloatAsInt( atan2( VMF(1), VMF(2) ) ); case TRAP_SQRT: return FloatAsInt( sqrt( VMF(1) ) ); case TRAP_MATRIXMULTIPLY: MatrixMultiply( (vec3_t *)VMA(1), (vec3_t *)VMA(2), (vec3_t *)VMA(3) ); return 0; case TRAP_ANGLEVECTORS: AngleVectors( (const float *)VMA(1), (float *)VMA(2), (float *)VMA(3), (float *)VMA(4) ); return 0; case TRAP_PERPENDICULARVECTOR: PerpendicularVector( (float *)VMA(1), (const float *)VMA(2) ); return 0; case TRAP_FLOOR: return FloatAsInt( floor( VMF(1) ) ); case TRAP_CEIL: return FloatAsInt( ceil( VMF(1) ) ); case TRAP_TESTPRINTINT: return 0; case TRAP_TESTPRINTFLOAT: return 0; case TRAP_ACOS: return FloatAsInt( Q_acos( VMF(1) ) ); case TRAP_ASIN: return FloatAsInt( Q_asin( VMF(1) ) ); case CG_PRINTALWAYS: Com_PrintfAlways( "%s", VMA(1) ); return 0; case CG_PRINT: Com_Printf( "%s", VMA(1) ); return 0; case CG_ERROR: Com_Error( ERR_DROP, "%s", VMA(1) ); return 0; case CG_MILLISECONDS: return Sys_Milliseconds(); //rww - precision timer funcs... -ALWAYS- call end after start with supplied ptr, or you'll get a nasty memory leak. //not that you should be using these outside of debug anyway.. because you shouldn't be. So don't. case CG_PRECISIONTIMER_START: { void **suppliedPtr =(void **)VMA(1); //we passed in a pointer to a point timing_c *newTimer = new timing_c; //create the new timer *suppliedPtr = newTimer; //assign the pointer within the pointer to point at the mem addr of our new timer newTimer->Start(); //start the timer } return 0; case CG_PRECISIONTIMER_END: { int r; timing_c *timer = (timing_c *)args[1]; //this is the pointer we assigned in start, so we can directly cast it back r = timer->End(); //get the result delete timer; //delete the timer since we're done with it return r; //return the result } case CG_CVAR_REGISTER: Cvar_Register( (vmCvar_t *)VMA(1), (const char *)VMA(2), (const char *)VMA(3), args[4] ); return 0; case CG_CVAR_UPDATE: Cvar_Update( (vmCvar_t *)VMA(1) ); return 0; case CG_CVAR_SET: Cvar_Set( (const char *)VMA(1), (const char *)VMA(2) ); return 0; case CG_CVAR_VARIABLESTRINGBUFFER: Cvar_VariableStringBuffer( (const char *)VMA(1), (char *)VMA(2), args[3] ); return 0; /* case CG_CVAR_GETHIDDENVALUE: return CL_GetValueForHidden((const char *)VMA(1)); */ case CG_ARGC: return Cmd_Argc(); case CG_ARGV: Cmd_ArgvBuffer( args[1], (char *)VMA(2), args[3] ); return 0; case CG_ARGS: Cmd_ArgsBuffer( (char *)VMA(1), args[2] ); return 0; case CG_FS_FOPENFILE: return FS_FOpenFileByMode( (const char *)VMA(1), (int *)VMA(2), (fsMode_t)args[3] ); case CG_FS_READ: FS_Read2( VMA(1), args[2], args[3] ); return 0; case CG_FS_WRITE: FS_Write( VMA(1), args[2], args[3] ); return 0; case CG_FS_FCLOSEFILE: FS_FCloseFile( args[1] ); return 0; case CG_FS_GETFILELIST: return FS_GetFileList( (const char *)VMA(1), (const char *)VMA(2), (char *)VMA(3), args[4] ); case CG_SENDCONSOLECOMMAND: Cbuf_AddText( (const char *)VMA(1) ); return 0; case CG_ADDCOMMAND: CL_AddCgameCommand( (const char *)VMA(1) ); return 0; case CG_REMOVECOMMAND: Cmd_RemoveCommand( (const char *)VMA(1) ); return 0; case CG_SENDCLIENTCOMMAND: CL_AddReliableCommand( (const char *)VMA(1) ); return 0; case CG_UPDATESCREEN: // this is used during lengthy level loading, so pump message loop // Com_EventLoop(); // FIXME: if a server restarts here, BAD THINGS HAPPEN! // We can't call Com_EventLoop here, a restart will crash and this _does_ happen // if there is a map change while we are downloading at pk3. // ZOID SCR_UpdateScreen(); return 0; case CG_CM_LOADMAP: /* if (args[2]) { CM_LoadSubBSP(va("maps/%s.bsp", ((const char *)VMA(1)) + 1), qfalse); } else */ { CL_CM_LoadMap( (const char *)VMA(1) ); } return 0; case CG_CM_NUMINLINEMODELS: return CM_NumInlineModels(); case CG_CM_INLINEMODEL: return CM_InlineModel( args[1] ); case CG_CM_TEMPBOXMODEL: return CM_TempBoxModel( (const float *)VMA(1), (const float *)VMA(2), /*int capsule*/ qfalse ); case CG_CM_TEMPCAPSULEMODEL: return CM_TempBoxModel( (const float *)VMA(1), (const float *)VMA(2), /*int capsule*/ qtrue ); case CG_CM_POINTCONTENTS: return CM_PointContents( (const float *)VMA(1), args[2] ); case CG_CM_TRANSFORMEDPOINTCONTENTS: return CM_TransformedPointContents( (const float *)VMA(1), args[2], (const float *)VMA(3), (const float *)VMA(4) ); case CG_CM_BOXTRACE: CM_BoxTrace( (trace_t *)VMA(1), (const float *)VMA(2), (const float *)VMA(3), (const float *)VMA(4), (const float *)VMA(5), args[6], args[7], /*int capsule*/ qfalse ); return 0; case CG_CM_CAPSULETRACE: CM_BoxTrace( (trace_t *)VMA(1), (const float *)VMA(2), (const float *)VMA(3), (const float *)VMA(4), (const float *)VMA(5), args[6], args[7], /*int capsule*/ qtrue ); return 0; case CG_CM_TRANSFORMEDBOXTRACE: CM_TransformedBoxTrace( (trace_t *)VMA(1), (const float *)VMA(2), (const float *)VMA(3), (const float *)VMA(4), (const float *)VMA(5), args[6], args[7], (const float *)VMA(8), (const float *)VMA(9), /*int capsule*/ qfalse ); return 0; case CG_CM_TRANSFORMEDCAPSULETRACE: CM_TransformedBoxTrace( (trace_t *)VMA(1), (const float *)VMA(2), (const float *)VMA(3), (const float *)VMA(4), (const float *)VMA(5), args[6], args[7], (const float *)VMA(8), (const float *)VMA(9), /*int capsule*/ qtrue ); return 0; case CG_CM_MARKFRAGMENTS: return re.MarkFragments( args[1], (const vec3_t *)VMA(2), (const float *)VMA(3), args[4], (float *)VMA(5), args[6], (markFragment_t *)VMA(7) ); case CG_S_GETVOICEVOLUME: return s_entityWavVol[args[1]]; case CG_S_MUTESOUND: S_MuteSound( args[1], args[2] ); return 0; case CG_S_STARTSOUND: S_StartSound( (float *)VMA(1), args[2], args[3], args[4] ); return 0; case CG_S_STARTLOCALSOUND: S_StartLocalSound( args[1], args[2] ); return 0; case CG_S_CLEARLOOPINGSOUNDS: S_ClearLoopingSounds(); return 0; case CG_S_ADDLOOPINGSOUND: S_AddLoopingSound( args[1], (const float *)VMA(2), (const float *)VMA(3), args[4] ); return 0; case CG_S_ADDREALLOOPINGSOUND: //S_AddRealLoopingSound( args[1], (const float *)VMA(2), (const float *)VMA(3), args[4] ); S_AddLoopingSound( args[1], (const float *)VMA(2), (const float *)VMA(3), args[4] ); return 0; case CG_S_STOPLOOPINGSOUND: S_StopLoopingSound( args[1] ); return 0; case CG_S_UPDATEENTITYPOSITION: S_UpdateEntityPosition( args[1], (const float *)VMA(2) ); return 0; case CG_S_RESPATIALIZE: S_Respatialize( args[1], (const float *)VMA(2), (vec3_t *)VMA(3), args[4] ); return 0; case CG_S_SHUTUP: s_shutUp = (qboolean)args[1]; return 0; case CG_S_REGISTERSOUND: return S_RegisterSound( (const char *)VMA(1) ); case CG_S_STARTBACKGROUNDTRACK: S_StartBackgroundTrack( (const char *)VMA(1), (const char *)VMA(2), args[3]?qtrue:qfalse ); return 0; case CG_S_UPDATEAMBIENTSET: S_UpdateAmbientSet((const char *)VMA(1), (float *)VMA(2)); return 0; case CG_AS_PARSESETS: AS_ParseSets(); return 0; case CG_AS_ADDPRECACHEENTRY: AS_AddPrecacheEntry((const char *)VMA(1)); return 0; case CG_S_ADDLOCALSET: return S_AddLocalSet((const char *)VMA(1), (float *)VMA(2), (float *)VMA(3), args[4], args[5]); case CG_AS_GETBMODELSOUND: return AS_GetBModelSound((const char *)VMA(1), args[2]); case CG_R_LOADWORLDMAP: re.LoadWorld( (const char *)VMA(1) ); return 0; case CG_R_REGISTERMODEL: return re.RegisterModel( (const char *)VMA(1) ); case CG_R_REGISTERSKIN: return re.RegisterSkin( (const char *)VMA(1) ); case CG_R_REGISTERSHADER: return re.RegisterShader( (const char *)VMA(1) ); case CG_R_REGISTERSHADERNOMIP: return re.RegisterShaderNoMip( (const char *)VMA(1) ); case CG_R_REGISTERFONT: return re.RegisterFont( (const char *)VMA(1) ); case CG_R_FONT_STRLENPIXELS: return re.Font_StrLenPixels( (const char *)VMA(1), args[2], VMF(3) ); case CG_R_FONT_STRLENCHARS: return re.Font_StrLenChars( (const char *)VMA(1) ); case CG_R_FONT_STRHEIGHTPIXELS: return re.Font_HeightPixels( args[1], VMF(2) ); case CG_R_FONT_DRAWSTRING: re.Font_DrawString( args[1], args[2], (const char *)VMA(3), (const float *) VMA(4), args[5], args[6], VMF(7) ); return 0; case CG_LANGUAGE_ISASIAN: return re.Language_IsAsian(); case CG_LANGUAGE_USESSPACES: return re.Language_UsesSpaces(); case CG_ANYLANGUAGE_READCHARFROMSTRING: return re.AnyLanguage_ReadCharFromString( (const char *) VMA(1), (int *) VMA(2), (qboolean *) VMA(3) ); case CG_R_CLEARSCENE: re.ClearScene(); return 0; case CG_R_CLEARDECALS: re.ClearDecals(); return 0; case CG_R_ADDREFENTITYTOSCENE: re.AddRefEntityToScene( (const refEntity_t *)VMA(1) ); return 0; case CG_R_ADDPOLYTOSCENE: re.AddPolyToScene( args[1], args[2], (const polyVert_t *)VMA(3), 1 ); return 0; case CG_R_ADDPOLYSTOSCENE: re.AddPolyToScene( args[1], args[2], (const polyVert_t *)VMA(3), args[4] ); return 0; case CG_R_ADDDECALTOSCENE: re.AddDecalToScene( (qhandle_t)args[1], (const float*)VMA(2), (const float*)VMA(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), (qboolean)args[9], VMF(10), (qboolean)args[11] ); return 0; case CG_R_LIGHTFORPOINT: return re.LightForPoint( (float *)VMA(1), (float *)VMA(2), (float *)VMA(3), (float *)VMA(4) ); case CG_R_ADDLIGHTTOSCENE: re.AddLightToScene( (const float *)VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) ); return 0; case CG_R_ADDADDITIVELIGHTTOSCENE: re.AddAdditiveLightToScene( (const float *)VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) ); return 0; case CG_R_RENDERSCENE: re.RenderScene( (const refdef_t *)VMA(1) ); return 0; case CG_R_SETCOLOR: re.SetColor( (const float *)VMA(1) ); return 0; case CG_R_DRAWSTRETCHPIC: re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] ); return 0; case CG_R_MODELBOUNDS: re.ModelBounds( args[1], (float *)VMA(2), (float *)VMA(3) ); return 0; case CG_R_LERPTAG: return re.LerpTag( (orientation_t *)VMA(1), args[2], args[3], args[4], VMF(5), (const char *)VMA(6) ); case CG_R_DRAWROTATEPIC: re.DrawRotatePic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), VMF(9), args[10] ); return 0; case CG_R_DRAWROTATEPIC2: re.DrawRotatePic2( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), VMF(9), args[10] ); return 0; case CG_R_SETRANGEFOG: tr.rangedFog = VMF(1); return 0; case CG_R_SETREFRACTIONPROP: /* tr_distortionAlpha = VMF(1); tr_distortionStretch = VMF(2); tr_distortionPrePost = (qboolean)args[3]; tr_distortionNegate = (qboolean)args[4]; */ return 0; case CG_GETGLCONFIG: CL_GetGlconfig( (glconfig_t *)VMA(1) ); return 0; case CG_GETGAMESTATE: CL_GetGameState( (gameState_t *)VMA(1) ); return 0; case CG_GETCURRENTSNAPSHOTNUMBER: CL_GetCurrentSnapshotNumber( (int *)VMA(1), (int *)VMA(2) ); return 0; case CG_GETSNAPSHOT: return CL_GetSnapshot( args[1], (snapshot_t *)VMA(2) ); case CG_GETDEFAULTSTATE: return CL_GetDefaultState(args[1], (entityState_t *)VMA(2)); case CG_GETSERVERCOMMAND: return CL_GetServerCommand( args[1] ); case CG_GETCURRENTCMDNUMBER: return CL_GetCurrentCmdNumber(); case CG_GETUSERCMD: return CL_GetUserCmd( args[1], (struct usercmd_s *)VMA(2) ); case CG_SETUSERCMDVALUE: cl_bUseFighterPitch = (qboolean)args[8]; CL_SetUserCmdValue( args[1], VMF(2), VMF(3), VMF(4), VMF(5), args[6], args[7] ); return 0; case CG_SETCLIENTFORCEANGLE: CL_SetClientForceAngle(args[1], (float *)VMA(2)); return 0; case CG_SETCLIENTTURNEXTENT: return 0; case CG_OPENUIMENU: VM_Call( uivm, UI_SET_ACTIVE_MENU, args[1] ); return 0; case CG_MEMORY_REMAINING: return Hunk_MemoryRemaining(); case CG_KEY_ISDOWN: return Key_IsDown( args[1] ); case CG_KEY_GETCATCHER: return Key_GetCatcher(); case CG_KEY_SETCATCHER: Key_SetCatcher( args[1] ); return 0; case CG_KEY_GETKEY: return Key_GetKey( (const char *)VMA(1) ); case CG_PC_ADD_GLOBAL_DEFINE: return botlib_export->PC_AddGlobalDefine( (char *)VMA(1) ); case CG_PC_LOAD_SOURCE: return botlib_export->PC_LoadSourceHandle( (const char *)VMA(1) ); case CG_PC_FREE_SOURCE: return botlib_export->PC_FreeSourceHandle( args[1] ); case CG_PC_READ_TOKEN: return botlib_export->PC_ReadTokenHandle( args[1], (struct pc_token_s *)VMA(2) ); case CG_PC_SOURCE_FILE_AND_LINE: return botlib_export->PC_SourceFileAndLine( args[1], (char *)VMA(2), (int *)VMA(3) ); case CG_PC_LOAD_GLOBAL_DEFINES: return botlib_export->PC_LoadGlobalDefines ( (char *)VMA(1) ); case CG_PC_REMOVE_ALL_GLOBAL_DEFINES: botlib_export->PC_RemoveAllGlobalDefines ( ); return 0; case CG_S_STOPBACKGROUNDTRACK: S_StopBackgroundTrack(); return 0; case CG_REAL_TIME: return Com_RealTime( (struct qtime_s *)VMA(1) ); case CG_SNAPVECTOR: Sys_SnapVector( (float *)VMA(1) ); return 0; case CG_CIN_PLAYCINEMATIC: return CIN_PlayCinematic((const char *)VMA(1), args[2], args[3], args[4], args[5], args[6]); case CG_CIN_STOPCINEMATIC: return CIN_StopCinematic(args[1]); case CG_CIN_RUNCINEMATIC: return CIN_RunCinematic(args[1]); case CG_CIN_DRAWCINEMATIC: CIN_DrawCinematic(args[1]); return 0; case CG_CIN_SETEXTENTS: CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]); return 0; case CG_R_REMAP_SHADER: re.RemapShader( (const char *)VMA(1), (const char *)VMA(2), (const char *)VMA(3) ); return 0; case CG_R_GET_LIGHT_STYLE: re.GetLightStyle(args[1], (unsigned char *)VMA(2)); return 0; case CG_R_SET_LIGHT_STYLE: re.SetLightStyle(args[1], args[2]); return 0; case CG_R_GET_BMODEL_VERTS: re.GetBModelVerts( args[1], (float (*)[3])VMA(2), (float *)VMA(3) ); return 0; case CG_R_GETDISTANCECULL: { float *f; f = (float *)VMA(1); *f = tr.distanceCull; } return 0; case CG_R_GETREALRES: { int *w = (int *)VMA(1); int *h = (int *)VMA(2); *w = glConfig.vidWidth; *h = glConfig.vidHeight; } return 0; case CG_R_AUTOMAPELEVADJ: R_AutomapElevationAdjustment(VMF(1)); return 0; case CG_R_INITWIREFRAMEAUTO: return R_InitializeWireframeAutomap(); /* case CG_LOADCAMERA: return loadCamera(VMA(1)); case CG_STARTCAMERA: startCamera(args[1]); return 0; case CG_GETCAMERAINFO: return getCameraInfo(args[1], VMA(2), VMA(3)); */ case CG_GET_ENTITY_TOKEN: return re.GetEntityToken( (char *)VMA(1), args[2] ); case CG_R_INPVS: return re.inPVS( (const float *)VMA(1), (const float *)VMA(2), (byte *)VMA(3) ); #ifndef DEBUG_DISABLEFXCALLS case CG_FX_ADDLINE: FX_AddLine( (float *)VMA(1), (float *)VMA(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), (float *)VMA(9), (float *)VMA(10), VMF(11), args[12], args[13], args[14]); return 0; case CG_FX_REGISTER_EFFECT: return FX_RegisterEffect((const char *)VMA(1)); case CG_FX_PLAY_EFFECT: FX_PlayEffect((const char *)VMA(1), (float *)VMA(2), (float *)VMA(3), args[4], args[5] ); return 0; case CG_FX_PLAY_ENTITY_EFFECT: assert(0);//gone! //FX_PlayEntityEffect((const char *)VMA(1), (float *)VMA(2), (vec3_t *)VMA(3), args[4], args[5], args[6], args[7] ); return 0; case CG_FX_PLAY_EFFECT_ID: FX_PlayEffectID(args[1], (float *)VMA(2), (float *)VMA(3), args[4], args[5] ); return 0; case CG_FX_PLAY_PORTAL_EFFECT_ID: FX_PlayEffectID(args[1], (float *)VMA(2), (float *)VMA(3), args[4], args[5], qtrue ); return 0; case CG_FX_PLAY_ENTITY_EFFECT_ID: FX_PlayEntityEffectID(args[1], (float *)VMA(2), (vec3_t *)VMA(3), args[4], args[5], args[6], args[7] ); return 0; case CG_FX_PLAY_BOLTED_EFFECT_ID: { //( int id, vec3_t org, void *pGhoul2, const int boltNum, const int entNum, const int modelNum, int iLooptime, qboolean isRelative ); CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[3]); int boltInfo=0; if ( G2API_AttachEnt( &boltInfo, &g2[args[6]], args[4], args[5], args[6] ) ) { FX_PlayBoltedEffectID(args[1], (float *)VMA(2), boltInfo, g2.mItem, args[7], (qboolean)args[8] ); return 1; } return 0; } case CG_FX_ADD_SCHEDULED_EFFECTS: FX_AddScheduledEffects((qboolean)args[1]); return 0; case CG_FX_DRAW_2D_EFFECTS: FX_Draw2DEffects ( VMF(1), VMF(2) ); return 0; case CG_FX_INIT_SYSTEM: return FX_InitSystem( (refdef_t*)VMA(1) ); case CG_FX_SET_REFDEF: FX_SetRefDefFromCGame( (refdef_t*)VMA(1) ); return 0; case CG_FX_FREE_SYSTEM: return FX_FreeSystem(); case CG_FX_ADJUST_TIME: FX_AdjustTime(args[1]); return 0; case CG_FX_RESET: FX_Free ( false ); return 0; case CG_FX_ADDPOLY: { addpolyArgStruct_t *p; p = (addpolyArgStruct_t *)VMA(1);//args[1]; if (p) { FX_AddPoly(p->p, p->ev, p->numVerts, p->vel, p->accel, p->alpha1, p->alpha2, p->alphaParm, p->rgb1, p->rgb2, p->rgbParm, p->rotationDelta, p->bounce, p->motionDelay, p->killTime, p->shader, p->flags); } } return 0; case CG_FX_ADDBEZIER: { addbezierArgStruct_t *b; b = (addbezierArgStruct_t *)VMA(1);//args[1]; if (b) { FX_AddBezier(b->start, b->end, b->control1, b->control1Vel, b->control2, b->control2Vel, b->size1, b->size2, b->sizeParm, b->alpha1, b->alpha2, b->alphaParm, b->sRGB, b->eRGB, b->rgbParm, b->killTime, b->shader, b->flags); } } return 0; case CG_FX_ADDPRIMITIVE: { effectTrailArgStruct_t *a; a = (effectTrailArgStruct_t *)VMA(1);//args[1]; if (a) { FX_FeedTrail(a); } } return 0; case CG_FX_ADDSPRITE: { addspriteArgStruct_t *s; s = (addspriteArgStruct_t *)VMA(1);//args[1]; if (s) { vec3_t rgb; rgb[0] = 1; rgb[1] = 1; rgb[2] = 1; //FX_AddSprite(NULL, s->origin, s->vel, s->accel, s->scale, s->dscale, s->sAlpha, s->eAlpha, // s->rotation, s->bounce, s->life, s->shader, s->flags); FX_AddParticle(s->origin, s->vel, s->accel, s->scale, s->dscale, 0, s->sAlpha, s->eAlpha, 0, rgb, rgb, 0, s->rotation, 0, vec3_origin, vec3_origin, s->bounce, 0, 0, s->life, s->shader, s->flags); } } return 0; case CG_FX_ADDELECTRICITY: { addElectricityArgStruct_t *p; p = (addElectricityArgStruct_t *)VMA(1); if (p) { FX_AddElectricity(p->start, p->end, p->size1, p->size2, p->sizeParm, p->alpha1, p->alpha2, p->alphaParm, p->sRGB, p->eRGB, p->rgbParm, p->chaos, p->killTime, p->shader, p->flags); } } return 0; #else case CG_FX_REGISTER_EFFECT: case CG_FX_PLAY_EFFECT: case CG_FX_PLAY_ENTITY_EFFECT: case CG_FX_PLAY_EFFECT_ID: case CG_FX_PLAY_PORTAL_EFFECT_ID: case CG_FX_PLAY_ENTITY_EFFECT_ID: case CG_FX_PLAY_BOLTED_EFFECT_ID: case CG_FX_ADD_SCHEDULED_EFFECTS: case CG_FX_INIT_SYSTEM: case CG_FX_FREE_SYSTEM: case CG_FX_ADJUST_TIME: case CG_FX_ADDPOLY: case CG_FX_ADDBEZIER: case CG_FX_ADDPRIMITIVE: case CG_FX_ADDSPRITE: case CG_FX_ADDELECTRICITY: return 0; #endif // case CG_SP_PRINT: // CL_SP_Print(args[1], (byte *)VMA(2)); // return 0; case CG_ROFF_CLEAN: return theROFFSystem.Clean(qtrue); case CG_ROFF_UPDATE_ENTITIES: theROFFSystem.UpdateEntities(qtrue); return 0; case CG_ROFF_CACHE: return theROFFSystem.Cache( (char *)VMA(1), qtrue ); case CG_ROFF_PLAY: return theROFFSystem.Play(args[1], args[2], (qboolean)args[3], qtrue ); case CG_ROFF_PURGE_ENT: return theROFFSystem.PurgeEnt( args[1], qtrue ); //rww - dynamic vm memory allocation! case CG_TRUEMALLOC: VM_Shifted_Alloc((void **)VMA(1), args[2]); return 0; case CG_TRUEFREE: VM_Shifted_Free((void **)VMA(1)); return 0; /* Ghoul2 Insert Start */ case CG_G2_GETMODELNAME: G2API_GetModelName(*(CGhoul2Info_v*)args[1], args[2], (const char**)VMA(3)); break; case CG_G2_LISTSURFACES: G2API_ListSurfaces( (CGhoul2Info *) args[1] ); return 0; case CG_G2_LISTBONES: G2API_ListBones( (CGhoul2Info *) args[1], args[2]); return 0; case CG_G2_HAVEWEGHOULMODELS: return G2API_HaveWeGhoul2Models( *((CGhoul2Info_v *)args[1]) ); case CG_G2_SETMODELS: G2API_SetGhoul2ModelIndexes( *((CGhoul2Info_v *)args[1]),(qhandle_t *)VMA(2),(qhandle_t *)VMA(3)); return 0; case CG_G2_GETBOLT: return G2API_GetBoltMatrix(*((CGhoul2Info_v *)args[1]), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9)); case CG_G2_GETBOLT_NOREC: gG2_GBMNoReconstruct = qtrue; return G2API_GetBoltMatrix(*((CGhoul2Info_v *)args[1]), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9)); case CG_G2_GETBOLT_NOREC_NOROT: //gG2_GBMNoReconstruct = qtrue; //Yeah, this was probably BAD. gG2_GBMUseSPMethod = qtrue; return G2API_GetBoltMatrix(*((CGhoul2Info_v *)args[1]), args[2], args[3], (mdxaBone_t *)VMA(4), (const float *)VMA(5),(const float *)VMA(6), args[7], (qhandle_t *)VMA(8), (float *)VMA(9)); case CG_G2_INITGHOUL2MODEL: #ifdef _FULL_G2_LEAK_CHECKING g_G2AllocServer = 0; #endif return G2API_InitGhoul2Model((CGhoul2Info_v **)VMA(1), (const char *)VMA(2), args[3], (qhandle_t) args[4], (qhandle_t) args[5], args[6], args[7]); case CG_G2_SETSKIN: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); int modelIndex = args[2]; return G2API_SetSkin(&g2[modelIndex], args[3], args[4]); } case CG_G2_COLLISIONDETECT: G2API_CollisionDetect ( (CollisionRecord_t*)VMA(1), *((CGhoul2Info_v *)args[2]), (const float*)VMA(3), (const float*)VMA(4), args[5], args[6], (float*)VMA(7), (float*)VMA(8), (float*)VMA(9), G2VertSpaceClient, args[10], args[11], VMF(12) ); return 0; case CG_G2_ANGLEOVERRIDE: return G2API_SetBoneAngles(*((CGhoul2Info_v *)args[1]), args[2], (const char *)VMA(3), (float *)VMA(4), args[5], (const Eorientations) args[6], (const Eorientations) args[7], (const Eorientations) args[8], (qhandle_t *)VMA(9), args[10], args[11] ); case CG_G2_CLEANMODELS: #ifdef _FULL_G2_LEAK_CHECKING g_G2AllocServer = 0; #endif G2API_CleanGhoul2Models((CGhoul2Info_v **)VMA(1)); // G2API_CleanGhoul2Models((CGhoul2Info_v **)args[1]); return 0; case CG_G2_PLAYANIM: return G2API_SetBoneAnim(*((CGhoul2Info_v *)args[1]), args[2], (const char *)VMA(3), args[4], args[5], args[6], VMF(7), args[8], VMF(9), args[10]); case CG_G2_GETBONEANIM: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); int modelIndex = args[10]; return G2API_GetBoneAnim(&g2[modelIndex], (const char*)VMA(2), args[3], (float *)VMA(4), (int *)VMA(5), (int *)VMA(6), (int *)VMA(7), (float *)VMA(8), (int *)VMA(9)); } case CG_G2_GETBONEFRAME: { //rwwFIXMEFIXME: Just make a G2API_GetBoneFrame func too. This is dirty. CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); int modelIndex = args[6]; int iDontCare1 = 0, iDontCare2 = 0, iDontCare3 = 0; float fDontCare1 = 0; return G2API_GetBoneAnim(&g2[modelIndex], (const char*)VMA(2), args[3], (float *)VMA(4), &iDontCare1, &iDontCare2, &iDontCare3, &fDontCare1, (int *)VMA(5)); } case CG_G2_GETGLANAME: // return (int)G2API_GetGLAName(*((CGhoul2Info_v *)VMA(1)), args[2]); { char *point = ((char *)VMA(3)); char *local; local = G2API_GetGLAName(*((CGhoul2Info_v *)args[1]), args[2]); if (local) { strcpy(point, local); } } return 0; case CG_G2_COPYGHOUL2INSTANCE: return (int)G2API_CopyGhoul2Instance(*((CGhoul2Info_v *)args[1]), *((CGhoul2Info_v *)args[2]), args[3]); case CG_G2_COPYSPECIFICGHOUL2MODEL: G2API_CopySpecificG2Model(*((CGhoul2Info_v *)args[1]), args[2], *((CGhoul2Info_v *)args[3]), args[4]); return 0; case CG_G2_DUPLICATEGHOUL2INSTANCE: #ifdef _FULL_G2_LEAK_CHECKING g_G2AllocServer = 0; #endif G2API_DuplicateGhoul2Instance(*((CGhoul2Info_v *)args[1]), (CGhoul2Info_v **)VMA(2)); return 0; case CG_G2_HASGHOUL2MODELONINDEX: return (int)G2API_HasGhoul2ModelOnIndex((CGhoul2Info_v **)VMA(1), args[2]); //return (int)G2API_HasGhoul2ModelOnIndex((CGhoul2Info_v **)args[1], args[2]); case CG_G2_REMOVEGHOUL2MODEL: #ifdef _FULL_G2_LEAK_CHECKING g_G2AllocServer = 0; #endif return (int)G2API_RemoveGhoul2Model((CGhoul2Info_v **)VMA(1), args[2]); //return (int)G2API_RemoveGhoul2Model((CGhoul2Info_v **)args[1], args[2]); case CG_G2_SKINLESSMODEL: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); return G2API_SkinlessModel(&g2[args[2]]); } case CG_G2_GETNUMGOREMARKS: #ifdef _G2_GORE { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); return G2API_GetNumGoreMarks(&g2[args[2]]); } #endif return 0; case CG_G2_ADDSKINGORE: #ifdef _G2_GORE G2API_AddSkinGore(*((CGhoul2Info_v *)args[1]),*(SSkinGoreData *)VMA(2)); #endif return 0; case CG_G2_CLEARSKINGORE: #ifdef _G2_GORE G2API_ClearSkinGore ( *((CGhoul2Info_v *)args[1]) ); #endif return 0; case CG_G2_SIZE: return G2API_Ghoul2Size ( *((CGhoul2Info_v *)args[1]) ); break; case CG_G2_ADDBOLT: return G2API_AddBolt(*((CGhoul2Info_v *)args[1]), args[2], (const char *)VMA(3)); case CG_G2_ATTACHENT: // G2API_AttachEnt(int *boltInfo, CGhoul2Info *ghlInfoTo, int toBoltIndex, int entNum, int toModelNum) { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[2]); return G2API_AttachEnt( (int*)VMA(1), &g2[0], args[3], args[4], args[5] ); } case CG_G2_SETBOLTON: G2API_SetBoltInfo(*((CGhoul2Info_v *)args[1]), args[2], args[3]); return 0; #ifdef _SOF2 case CG_G2_ADDSKINGORE: G2API_AddSkinGore(*((CGhoul2Info_v *)args[1]),*(SSkinGoreData *)VMA(2)); return 0; #endif // _SOF2 /* Ghoul2 Insert End */ case CG_G2_SETROOTSURFACE: return G2API_SetRootSurface(*((CGhoul2Info_v *)args[1]), args[2], (const char *)VMA(3)); case CG_G2_SETSURFACEONOFF: return G2API_SetSurfaceOnOff(*((CGhoul2Info_v *)args[1]), (const char *)VMA(2), /*(const int)VMA(3)*/args[3]); case CG_G2_SETNEWORIGIN: return G2API_SetNewOrigin(*((CGhoul2Info_v *)args[1]), /*(const int)VMA(2)*/args[2]); case CG_G2_DOESBONEEXIST: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); return G2API_DoesBoneExist(&g2[args[2]], (const char *)VMA(3)); } case CG_G2_GETSURFACERENDERSTATUS: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); return G2API_GetSurfaceRenderStatus(&g2[args[2]], (const char *)VMA(3)); } case CG_G2_GETTIME: return G2API_GetTime(0); case CG_G2_SETTIME: G2API_SetTime(args[1], args[2]); return 0; case CG_G2_ABSURDSMOOTHING: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); G2API_AbsurdSmoothing(g2, (qboolean)args[2]); } return 0; case CG_G2_SETRAGDOLL: { //Convert the info in the shared structure over to the class-based version. sharedRagDollParams_t *rdParamst = (sharedRagDollParams_t *)VMA(2); CRagDollParams rdParams; if (!rdParamst) { G2API_ResetRagDoll(*((CGhoul2Info_v *)args[1])); return 0; } VectorCopy(rdParamst->angles, rdParams.angles); VectorCopy(rdParamst->position, rdParams.position); VectorCopy(rdParamst->scale, rdParams.scale); VectorCopy(rdParamst->pelvisAnglesOffset, rdParams.pelvisAnglesOffset); VectorCopy(rdParamst->pelvisPositionOffset, rdParams.pelvisPositionOffset); rdParams.fImpactStrength = rdParamst->fImpactStrength; rdParams.fShotStrength = rdParamst->fShotStrength; rdParams.me = rdParamst->me; rdParams.startFrame = rdParamst->startFrame; rdParams.endFrame = rdParamst->endFrame; rdParams.collisionType = rdParamst->collisionType; rdParams.CallRagDollBegin = rdParamst->CallRagDollBegin; rdParams.RagPhase = (CRagDollParams::ERagPhase)rdParamst->RagPhase; rdParams.effectorsToTurnOff = (CRagDollParams::ERagEffector)rdParamst->effectorsToTurnOff; G2API_SetRagDoll(*((CGhoul2Info_v *)args[1]), &rdParams); } return 0; break; case CG_G2_ANIMATEG2MODELS: { sharedRagDollUpdateParams_t *rduParamst = (sharedRagDollUpdateParams_t *)VMA(3); CRagDollUpdateParams rduParams; if (!rduParamst) { return 0; } VectorCopy(rduParamst->angles, rduParams.angles); VectorCopy(rduParamst->position, rduParams.position); VectorCopy(rduParamst->scale, rduParams.scale); VectorCopy(rduParamst->velocity, rduParams.velocity); rduParams.me = rduParamst->me; rduParams.settleFrame = rduParamst->settleFrame; G2API_AnimateG2Models(*((CGhoul2Info_v *)args[1]), args[2], &rduParams); } return 0; break; //additional ragdoll options -rww case CG_G2_RAGPCJCONSTRAINT: return G2API_RagPCJConstraint(*((CGhoul2Info_v *)args[1]), (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4)); case CG_G2_RAGPCJGRADIENTSPEED: return G2API_RagPCJGradientSpeed(*((CGhoul2Info_v *)args[1]), (const char *)VMA(2), VMF(3)); case CG_G2_RAGEFFECTORGOAL: return G2API_RagEffectorGoal(*((CGhoul2Info_v *)args[1]), (const char *)VMA(2), (float *)VMA(3)); case CG_G2_GETRAGBONEPOS: return G2API_GetRagBonePos(*((CGhoul2Info_v *)args[1]), (const char *)VMA(2), (float *)VMA(3), (float *)VMA(4), (float *)VMA(5), (float *)VMA(6)); case CG_G2_RAGEFFECTORKICK: return G2API_RagEffectorKick(*((CGhoul2Info_v *)args[1]), (const char *)VMA(2), (float *)VMA(3)); case CG_G2_RAGFORCESOLVE: return G2API_RagForceSolve(*((CGhoul2Info_v *)args[1]), (qboolean)args[2]); case CG_G2_SETBONEIKSTATE: return G2API_SetBoneIKState(*((CGhoul2Info_v *)args[1]), args[2], (const char *)VMA(3), args[4], (sharedSetBoneIKStateParams_t *)VMA(5)); case CG_G2_IKMOVE: return G2API_IKMove(*((CGhoul2Info_v *)args[1]), args[2], (sharedIKMoveParams_t *)VMA(3)); case CG_G2_REMOVEBONE: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); return G2API_RemoveBone(&g2[args[3]], (const char *)VMA(2)); } case CG_G2_ATTACHINSTANCETOENTNUM: { G2API_AttachInstanceToEntNum(*((CGhoul2Info_v *)args[1]), args[2], (qboolean)args[3]); } return 0; case CG_G2_CLEARATTACHEDINSTANCE: G2API_ClearAttachedInstance(args[1]); return 0; case CG_G2_CLEANENTATTACHMENTS: G2API_CleanEntAttachments(); return 0; case CG_G2_OVERRIDESERVER: { CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); return G2API_OverrideServerWithClientData(&g2[0]); } case CG_G2_GETSURFACENAME: { //Since returning a pointer in such a way to a VM seems to cause MASSIVE FAILURE, we will shove data into the pointer the vm passes instead char *point = ((char *)VMA(4)); char *local; int modelindex = args[3]; CGhoul2Info_v &g2 = *((CGhoul2Info_v *)args[1]); local = G2API_GetSurfaceName(&g2[modelindex], args[2]); if (local) { strcpy(point, local); } } return 0; case CG_SP_GETSTRINGTEXTSTRING: // case CG_SP_GETSTRINGTEXT: const char* text; assert(VMA(1)); assert(VMA(2)); // if (args[0] == CG_SP_GETSTRINGTEXT) // { // text = SP_GetStringText( args[1] ); // } // else { text = SE_GetString( (const char *) VMA(1) ); } if ( text[0] ) { Q_strncpyz( (char *) VMA(2), text, args[3] ); return qtrue; } else { Com_sprintf( (char *) VMA(2), args[3], "??%s", VMA(1) ); return qfalse; } break; case CG_SET_SHARED_BUFFER: cl->mSharedMemory = ((char *)VMA(1)); return 0; /* case CG_CM_REGISTER_TERRAIN: return CM_RegisterTerrain((const char *)VMA(1), false)->GetTerrainId(); */ /* case CG_RMG_INIT: #ifndef PRE_RELEASE_DEMO if (!com_sv_running->integer) { // don't do this if we are connected locally if (!TheRandomMissionManager) { TheRandomMissionManager = new CRMManager; } TheRandomMissionManager->SetLandScape( cmg.landScape ); if (TheRandomMissionManager->LoadMission(qfalse)) { if (!TheRandomMissionManager->SpawnMission(qfalse)) { Com_Error(ERR_DROP, "Error spawning mission for terrain"); } } cmg.landScape->UpdatePatches(); } RM_CreateRandomModels(args[1], (const char *)VMA(2)); // TheRandomMissionManager->CreateMap(); #endif // PRE_RELEASE_DEMO return 0; */ /* case CG_RE_INIT_RENDERER_TERRAIN: RE_InitRendererTerrain((const char *)VMA(1)); return 0; */ case CG_R_WEATHER_CONTENTS_OVERRIDE: //contentOverride = args[1]; return 0; case CG_R_WORLDEFFECTCOMMAND: R_WorldEffectCommand((const char *)VMA(1)); return 0; case CG_WE_ADDWEATHERZONE: R_AddWeatherZone( (vec_t *)VMA(1), (vec_t *)VMA(2) ); return 0; default: assert(0); // bk010102 Com_Error( ERR_DROP, "Bad cgame system trap: %i", args[0] ); } return 0; } /* ==================== CL_InitCGame Should only be called by CL_StartHunkUsers ==================== */ void CL_InitCGame( void ) { const char *info; const char *mapname; // int t1, t2; vmInterpret_t interpret; // t1 = Sys_Milliseconds(); // put away the console Con_Close(); // find the current mapname info = cl->gameState.stringData + cl->gameState.stringOffsets[ CS_SERVERINFO ]; mapname = Info_ValueForKey( info, "mapname" ); Com_sprintf( cl->mapname, sizeof( cl->mapname ), "maps/%s.bsp", mapname ); // load the dll or bytecode if ( cl_connectedToPureServer != 0 ) { #if 0 // if sv_pure is set we only allow qvms to be loaded interpret = VMI_COMPILED; #else //load the module type based on what the server is doing -rww interpret = (vmInterpret_t)cl_connectedCGAME; #endif } else { interpret = (vmInterpret_t)(int)Cvar_VariableValue( "vm_cgame" ); } cgvm = VM_Create( "cgame", CL_CgameSystemCalls, interpret ); if ( !cgvm ) { Com_Error( ERR_DROP, "VM_Create on cgame failed" ); } #ifdef _XBOX // if(ClientManager::splitScreenMode == qtrue) // ClientManager::ActiveClient().state = CA_LOADING; // else #endif cls.state = CA_LOADING; // init for this gamestate // use the lastExecutedServerCommand instead of the serverCommandSequence // otherwise server commands sent just before a gamestate are dropped VM_Call( cgvm, CG_INIT, clc->serverMessageSequence, clc->lastExecutedServerCommand, clc->clientNum ); // we will send a usercmd this frame, which // will cause the server to send us the first snapshot #ifdef _XBOX // if(ClientManager::splitScreenMode == qtrue) // ClientManager::ActiveClient().state = CA_PRIMED; // else #endif cls.state = CA_PRIMED; // t2 = Sys_Milliseconds(); // Com_Printf( "CL_InitCGame: %5.2f seconds\n", (t2-t1)/1000.0 ); // have the renderer touch all its images, so they are present // on the card even if the driver does deferred loading re.EndRegistration(); // make sure everything is paged in // if (!Sys_LowPhysicalMemory()) { Com_TouchMemory(); } // clear anything that got printed Con_ClearNotify (); #ifdef _DONETPROFILE_ ClReadProf().Reset(); #endif } /* ==================== CL_GameCommand See if the current console command is claimed by the cgame ==================== */ qboolean CL_GameCommand( void ) { if ( !cgvm ) { return qfalse; } return (qboolean)VM_Call( cgvm, CG_CONSOLE_COMMAND ); } /* ===================== CL_CGameRendering ===================== */ void CL_CGameRendering( stereoFrame_t stereo ) { //rww - RAGDOLL_BEGIN #ifdef _XBOX if(ClientManager::ActiveClientNum() == 0) { #endif if (!com_sv_running->integer) { //set the server time to match the client time, if we don't have a server going. G2API_SetTime(cl->serverTime, 0); } G2API_SetTime(cl->serverTime, 1); //rww - RAGDOLL_END #ifdef _XBOX } #endif #ifdef _XBOX // No demos on Xbox VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl->serverTime, stereo, 0 ); #else VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl->serverTime, stereo, clc->demoplaying ); #endif VM_Debug( 0 ); } /* ================= CL_AdjustTimeDelta Adjust the clients view of server time. We attempt to have cl->serverTime exactly equal the server's view of time plus the timeNudge, but with variable latencies over the internet it will often need to drift a bit to match conditions. Our ideal time would be to have the adjusted time approach, but not pass, the very latest snapshot. Adjustments are only made when a new snapshot arrives with a rational latency, which keeps the adjustment process framerate independent and prevents massive overadjustment during times of significant packet loss or bursted delayed packets. ================= */ #define RESET_TIME 500 void CL_AdjustTimeDelta( void ) { int resetTime; int newDelta; int deltaDelta; cl->newSnapshots = qfalse; // the delta never drifts when replaying a demo #ifndef _XBOX // No demos on Xbox if ( clc->demoplaying ) { return; } #endif // if the current time is WAY off, just correct to the current value if ( com_sv_running->integer ) { resetTime = 100; } else { resetTime = RESET_TIME; } newDelta = cl->snap.serverTime - cls.realtime; deltaDelta = abs( newDelta - cl->serverTimeDelta ); if ( deltaDelta > RESET_TIME ) { cl->serverTimeDelta = newDelta; cl->oldServerTime = cl->snap.serverTime; // FIXME: is this a problem for cgame? cl->serverTime = cl->snap.serverTime; if ( cl_showTimeDelta->integer ) { Com_Printf( " " ); } } else if ( deltaDelta > 100 ) { // fast adjust, cut the difference in half if ( cl_showTimeDelta->integer ) { Com_Printf( " " ); } cl->serverTimeDelta = ( cl->serverTimeDelta + newDelta ) >> 1; } else { // slow drift adjust, only move 1 or 2 msec // if any of the frames between this and the previous snapshot // had to be extrapolated, nudge our sense of time back a little // the granularity of +1 / -2 is too high for timescale modified frametimes if ( com_timescale->value == 0 || com_timescale->value == 1 ) { if ( cl->extrapolatedSnapshot ) { cl->extrapolatedSnapshot = qfalse; cl->serverTimeDelta -= 2; } else { // otherwise, move our sense of time forward to minimize total latency cl->serverTimeDelta++; } } } if ( cl_showTimeDelta->integer ) { Com_Printf( "%i ", cl->serverTimeDelta ); } } /* ================== CL_FirstSnapshot ================== */ extern void RE_RegisterMedia_LevelLoadEnd(void); void CL_FirstSnapshot( void ) { // ignore snapshots that don't have entities if ( cl->snap.snapFlags & SNAPFLAG_NOT_ACTIVE ) { return; } RE_RegisterMedia_LevelLoadEnd(); #ifdef _XBOX // if(ClientManager::splitScreenMode == qtrue) // ClientManager::ActiveClient().state = CA_ACTIVE; // else #endif cls.state = CA_ACTIVE; // set the timedelta so we are exactly on this first frame cl->serverTimeDelta = cl->snap.serverTime - cls.realtime; cl->oldServerTime = cl->snap.serverTime; #ifndef _XBOX // No demos on Xbox clc->timeDemoBaseTime = cl->snap.serverTime; #endif // if this is the first frame of active play, // execute the contents of activeAction now // this is to allow scripting a timedemo to start right // after loading if ( cl_activeAction->string[0] ) { Cbuf_AddText( cl_activeAction->string ); Cvar_Set( "activeAction", "" ); } Sys_BeginProfiling(); #ifdef _XBOX // turn vsync back on - tearing is ugly qglEnable(GL_VSYNC); // Start (or update) advertising on MM if ( com_sv_running->integer ) XBL_MM_Advertise(); #endif } /* ================== CL_SetCGameTime ================== */ void CL_SetCGameTime( void ) { #ifdef _XBOX if(ClientManager::splitScreenMode == qtrue) CM_START_LOOP(); #endif #ifdef _XBOX /* if(ClientManager::splitScreenMode == qtrue) { // getting a valid frame message ends the connection process if ( ClientManager::ActiveClient().state != CA_ACTIVE ) { if ( ClientManager::ActiveClient().state != CA_PRIMED ) { return; } if ( cl->newSnapshots ) { cl->newSnapshots = qfalse; CL_FirstSnapshot(); } if ( ClientManager::ActiveClient().state != CA_ACTIVE ) { return; } } } else { */ #endif // getting a valid frame message ends the connection process if ( cls.state != CA_ACTIVE ) { if ( cls.state != CA_PRIMED ) { return; } if ( cl->newSnapshots ) { cl->newSnapshots = qfalse; CL_FirstSnapshot(); } if ( cls.state != CA_ACTIVE ) { return; } } #ifdef _XBOX // } #endif // if we have gotten to this point, cl->snap is guaranteed to be valid if ( !cl->snap.valid ) { Com_Error( ERR_DROP, "CL_SetCGameTime: !cl->snap.valid" ); } // allow pause in single player if ( sv_paused->integer && cl_paused->integer && com_sv_running->integer ) { // paused return; } if ( cl->snap.serverTime < cl->oldFrameServerTime ) { Com_Error( ERR_DROP, "cl->snap.serverTime < cl->oldFrameServerTime" ); } cl->oldFrameServerTime = cl->snap.serverTime; // get our current view of time #ifndef _XBOX // No demos on Xbox if ( clc->demoplaying && cl_freezeDemo->integer ) { // cl_freezeDemo is used to lock a demo in place for single frame advances } else #endif { // cl_timeNudge is a user adjustable cvar that allows more // or less latency to be added in the interest of better // smoothness or better responsiveness. int tn; tn = cl_timeNudge->integer; #ifdef _DEBUG if (tn<-900) { tn = -900; } else if (tn>900) { tn = 900; } #else if (tn<-30) { tn = -30; } else if (tn>30) { tn = 30; } #endif cl->serverTime = cls.realtime + cl->serverTimeDelta - tn; // guarantee that time will never flow backwards, even if // serverTimeDelta made an adjustment or cl_timeNudge was changed if ( cl->serverTime < cl->oldServerTime ) { cl->serverTime = cl->oldServerTime; } cl->oldServerTime = cl->serverTime; // note if we are almost past the latest frame (without timeNudge), // so we will try and adjust back a bit when the next snapshot arrives if ( cls.realtime + cl->serverTimeDelta >= cl->snap.serverTime - 5 ) { cl->extrapolatedSnapshot = qtrue; } } // if we have gotten new snapshots, drift serverTimeDelta // don't do this every frame, or a period of packet loss would // make a huge adjustment if ( cl->newSnapshots ) { CL_AdjustTimeDelta(); } #ifdef _XBOX CM_END_LOOP(); #endif #ifndef _XBOX // No demos on Xbox if ( !clc->demoplaying ) { return; } // if we are playing a demo back, we can just keep reading // messages from the demo file until the cgame definately // has valid snapshots to interpolate between // a timedemo will always use a deterministic set of time samples // no matter what speed machine it is run on, // while a normal demo may have different time samples // each time it is played back if ( cl_timedemo->integer ) { if (!clc->timeDemoStart) { clc->timeDemoStart = Sys_Milliseconds(); } clc->timeDemoFrames++; cl->serverTime = clc->timeDemoBaseTime + clc->timeDemoFrames * 50; } while ( cl->serverTime >= cl->snap.serverTime ) { // feed another messag, which should change // the contents of cl->snap CL_ReadDemoMessage(); if ( cls.state != CA_ACTIVE ) { return; // end of demo } } #endif // _XBOX }