BASEQ3 Runthrough, Client-side


Illustration of an execution path through a Q3 cgame module (baseq3), from startup through gameplay though shutdown, for client-side mode, in outline form.

Motivation: Enhanced understanding of Q3A to assist in modding and total conversions.

Caveats: This document certainly is not a thorough understanding nor explanation of Q3. Information was gleaned by poring through the cgame source text, with minimal testing. Most of the statements made in this document are based on source comments and gut feelings.

Engine Start


See outline1.html regarding vmMain(). Many descriptions of game's vmMain() also applies to cgame module.

Overview: vmMain command/major cgame function

A quick overview of the vmMain commands (major cgame functions) in baseq3:

When cgame starts (on a new map, level, connect, reconnect, etc.).
Upon cgame shutdown (disconnecting, kicked, crash).
A console command that the engine does not recognize.
To draw stuff on the screen.
XXX: ???
XXX: ???
When key-trapping is enabled, on a key event (press or release).
When mouse-trapping is enabled, on a mouse event (movement, button press/release).
XXX: ???

The Q3 engine marshalls the necessary cgame function values and arguments, then makes the call to vmMain(). Any additions or changes to these major cgame functions must be implemented in the engine and reflected in the cgame module (client-side mod). Only developers with access to the Q3 engine (not just mod) source may make such aditions or changes. Currently (2002 Aug) this consists of Id Software itself and its direct licensees (proprietary license).

vmMain(CGAME_INIT, serverMessageNum, serverCommandSequence, clientNum)

Called when cgame module is initialized, upon connecting to a server (single-player game is a special case of a local server). The cgame is reloaded and reinitialized every time a new map is loaded. Three arguments:

XXX: ???
XXX: ???
The cgame's client number as assigned by the server.

In baseq3, immediate branch to CG_Init().

CG_Init(serverMesssageNum, serverCommandSequence, clientNum)

  1. Zero out (clear) the structures cgs, cg, cg_entities, cg_weapons, cg_items
  2. Record clientNum to cg.clientNum
  3. Record serverMessageNum to cgs.processedSnapshotNum
  4. Record serverCommandSequence to cgs.serverCommandSequence
  5. Load some fonts and the plain-white shader
  6. Register some cvars -- CG_RegisterCvars().
    1. For all elements of cvarTable[], register the location of the vmCvar_t instance, the cvar name, the default value, and the cvar flags -- trap_Cvar_Register()
    2. If also running a local server (non-dedicated host, single-player), record the fact into cgs.localServer
    3. Register cvar modeL, headmodel, team_model, team_headmodel
  7. Initialize cgame console commands -- CG_InitConsoleCommands().
    1. For all elements of commands[], register the command string/name with the Q3 client engine for tab-completion -- trap_AddCommand()
    2. Register a set of commands that can be tab-completed, but are interpreted on the server-side (e.g. kill or say)
  8. Switch/set weapon to machine gun
  9. CTF flags hackage for old-server compatibility (wtf?)
  10. Retrieve rendering configuration into cgs.glconfig -- trap_GetGlconfig()
  11. Set screen scale factors from cgs.glconfig: cgs.screenXScale, cgs.screenYScale
  12. Retrieve gamestate from client system into cgs.gameState -- trap_GetGameState()
  13. Get game (mod) version and compare -- CG_ConfigString(CS_GAME_VERSIOn)
  14. Retrieve level start time into cgs.levelStartTime -- CG_ConfigString(CS_LEVEL_START_TIME)
  15. Parse server info -- CG_ParseServerinfo().
    1. Retrieve ConfigString CS_SERVERINFO into info -- CG_ConfigString(CS_SERVERINFO)
    2. Retrieve values for keys g_gametype, dmflags, teamflags, fraglimit, capturelimit, timelimit, sv_maxclients, mapname, g_redTeam, g_blueTeam
  16. Indicate cgame is loading the map -- CG_LoadingString().
    1. Copy the message string to cg.infoScreenText
    2. update screen -- trap_UpdateScreen()
  17. Load map collision (and models?) -- trap_CM_LoadMap()
  18. Load sounds -- CG_RegisterSounds()
    1. Register a whole gank of sound files with trap_S_RegisterSound() and store the returned handle into various* fields
  19. Load graphics -- CG_RegisterGraphics()
    1. Clear references to any old media
    2. Display the mapname as the loading string
    3. Load map (models and textures?) -- trap_R_LoadWorldMap()
    4. Display game media as loading string
    5. Register numerals font faces
    6. Register a whole gank of media files, saving the returned handles into various* fields: trap_R_RegisterShader(), trap_R_RegisterShaderNoMip(), trap_R_RegisterModel(), trap_R_RegisterSkin()
    7. Register inline models
    8. Register server-specified models
    9. Clear particles -- CG_ClearParticles()
  20. Load clients and player models -- CG_RegisterClients()
    1. Report loading client information (our own) -- CG_LoadingClient().
      1. Get indicated client ConfigString -- CG_ConfigString()
      2. If displayed less than MAX_LOADING_PLAYER_ICONS player icons, figure out the player icon to display, display it, save player icon into loadingPlayerIcons[] increment the icons counter
      3. Get client name from ConfigString
      4. Clean up the name, stripping out invalid, dangerous, or otherwise illicit characters -- Q_CleanStr()
      5. If single-player game, load the announcer voice saying the player's (bot's) name
      6. Display client name as loading string
    2. Update clientinfo (cgs.clientinfo[...]) -- CG_NewClientInfo().
      1. Extract various fields from ConfigString, populate newInfo, then copy into cgs.clientinfo[clientNum]
      2. XXX: CG_ScanForExistingClientInfo()
    3. For all clients (0 through MAX_CLIENTS):
      1. Skip to next client if this is our client number
      2. Get clientinfo from ConfigString -- CG_ConfigString(CS_PLAYERS+...)
      3. If clientinfo is empty, skip to next client
      4. Report on loading client information (others) -- CG_LoadingClient()
      5. Update clientinfo -- CG_NewClientInfo()
    4. Build the list of names of spectators -- CG_BuildSpectatorString().
  21. Set deferred models loading (load up model on death, instead of during play)
  22. Initialize local entities array -- CG_InitLocalEntities()
    1. Zero out (clear) array cg_localEntities[]
    2. Set and cg_activeLocalEntities.prev to cg_activeLocalEntities (itself?)
    3. Set cg_freeLocalEntities to the first element (offset 0) of cg_localEntities[]
    4. Chain up the elements of cg_localEntities[] by setting cg_localEntities[i].next to cg_localEntities[i+1], except the last element, which points next back to the first element (cg_localEntities[] as a circular list)
  23. Initialized marked polygons -- CG_InitMarkPolys()
    1. Zero out (clear) array cg_markPolys[]
    2. Set cg_activeMarkPolys.nextMark and cg_activeMarkPolys.prevMark to cg_activeMarkPolys (itself?)
    3. Set cg_freeMarkPolys to the first element of cg_markPolys[]
    4. Chain up elements of cg_markPolys as a circular list
  24. Clear/erase the loading string/message
  25. Update values -- CG_SetConfigValues()
    1. Retrieve ConfigString CS_SCORES1 into cgs.scores1
    2. Retrieve ConfigString CS_SCORES2 into cgs.scores2
    3. Retrieve ConfigString CS_LEVEL_START_TIME into cgs.levelStartTime
    4. If a CTF gametype, retrieve ConfigString CS_FLAGSTATUS for the status of CTF flags, and store into cgs.redflag and cgs.blueflag
    5. Retrieve ConfigString CS_WARMUP into cg.warmup
  26. Start background music -- CG_StartMusic().
    1. Retrieve ConfigString CS_MUSIC into s
    2. Parse into two words
    3. Start playing background music based on this parsing -- trap_S_StartBackgroundTrack()
  27. Clear "loading..." message
  28. Change shader states as needed -- CG_ShaderStateChanged()
  29. Stop any looping sound -- trap_S_ClearLoopingSounds()


Called when Q3 is shutting down the cgame module, as when ending a map or quitting entirely. No arguments.

Immediate branch to CG_Shutdown().


  1. (nothing. Space reserved for mods that may need some explicit cleaning up, such as closing files, special commands, last desperate attempt to write some file, etc.)


Console commands are any textual commands not recognized by the Q3 engine itself. Such commands may be entered by the drop-down console, or by key bindings (e.g. /bind k kill). No arguments.

Immediate branch to CG_ConsoleCommand().


  1. Go through the array of cgame console commands commands[], trying to match names (case-insensitive).
  2. If a match is found, call the associated function [pointer].

vmMain(CG_DRAW_ACTIVE_FRAME, serverTime, stereoView, demoPlayback)

XXX: blahblah. Three arguments:

The timestamp of the packet (XXX: what packet?) from the server.
Whether to render as stereo view.
True if playing back a demo, false otherwise (online game).

Immediate branch to CG_DrawActiveFrame().

CG_DrawActiveFrame(serverTime, stereoView, demoPlayback)

  1. Record serverTime into cg.time
  2. Record demoPlayback into cg.demoPlayerback
  3. Update cvars -- CG_UpdateCvars()
    1. For all entries in cvarTable[], update the vmCvar_t member with trap_Cvar_Update().
    2. Do any checking of changed cvars, and actions to such changes, as needed.
    3. Checking team overlay (cvar teamoverlay).
    4. Something about cvar cg_forceModel having changed... -- CG_ForceModelChange()
  4. Um... something about loading screen vs. playing? -- CG_DrawInformation()
    1. Retrieve ConfigString CS_SERVERINFO into info.
    2. Retrieve ConfigString CS_SYSTEMINFO into sysInfo.
    3. Retrieve value for mapinfo from info, mangle to get a levelshot name, display on screen -- CG_DrawPic()
      1. Figure out scaled positions and sizes -- CG_AdjustFrom640()
        1. Multiply x and w by cgs.screenXScale
        2. Multiply y and h by cgs.screenYScale
      2. Blt image with recalculated positions and sizes -- trap_R_DrawStretchPic()
    4. Retrieve shader named levelShotDetail, draw it on top of the levelshot image -- trap_R_DrawStretchPic()
    5. draw icons indicating the data loaded -- CG_DrawLoadingIcon()
      1. Draw the player icons representing the player information already loaded so far
      2. Draw the item icons representing the item information already loaded thus far
  5. Clear looping sounds -- trap_S_ClearLoopingSounds()
  6. Clear render list -- trap_R_ClearScene()
  7. Set up cg.snap and cg.nextSnap -- CG_ProcessSnapshot()
    1. Retrieve latest snapshot time from Q3 client engine into n
    2. If the latest snapshot number is less than what we had before, something very very bad happened.
    3. Update cg.latestSnapshotNum accordingly.
    4. If cg.snap is empty:
      1. Retrieve next snapshot into snap -- CG_ReadNextSnapshot()
        1. If latest snapshot and processed snapshot are off by 1000, complain
        2. Loop while cg.processedSnapshotNum is less than cg.latestSnapshotNum:
          1. Figure which slot, cg.activeSnapshots[0] or cg.activeSnapshots[1] to use.
          2. Increment cg_processedSnapshotNum, retrieve snapshot into r -- trap_GetSnapshot()
          3. um... something about snapshot server time?
          4. If retrieval succeeded, record in lagometer info and return -- CG_AddLagometerSnapshotInfo()
            1. If snap is NULL, record -1 into lagometer.snapshotSamples[] and increment lagometer.snapshotCount, and return
            2. Otherwise, record snap's ping into lagometer.snapshotSamples[], record snap's flags into lagometer.snapshotFlags[], and increment lagometer.snapshotCount, and return
        3. Record as dropped packet in lagometer info -- CG_AddLagometerInfo()
        4. If nothing left to read, return NULL (failure)
      2. If no snap found, bad.
      3. ??? -- CG_SetInitialSnapshot()
        1. Record snap into cg.snap (initial snapshot).
        2. Retrieve entityState_t info from snap's playerState_t info.
        3. Build the list of solid and trigger entities -- CG_BuildSolidList()
          1. If nextsnap is available and not currently teleporting, use cg.nextsnap, otherwise use cg.snap
          2. For all entities in snap:
            1. Get entity's state slot
            2. If an item, or a teleport trigger, or a push trigger, record in cg_triggerEntities[] and update cg_numTriggerEntities
            3. If a solid entity, record in cg_solidEntities[] and update cg_numSolidEntities
        4. um.... -- CG_ExecuteNewServerCommands()
          1. Keep looping while cgs.serverCommandSequence < latestSequence (???):
            1. Get server command -- trap_GetServerCommand()
            2. Process server command -- CG_ServerCommand()
              1. (XXX: EEK!)
        5. Change weapon to what the server says -- CG_Respawn()
          1. Disable error decay (??)
          2. Set weapon time?
          3. Set weapon according to snap information (cg.snap->ps.weapon)
        6. For all entities in snap:
          1. Get entity's snap state information.
          2. Get appropriate cent pointer.
          3. Clear current state information.
          4. ??? -- CG_ResetEntity()
            1. Clear event.
            2. Record cg.snap->serverTime into cent->trailTime (XXX: what's this for?)
            3. Copy currentOrigin and currentAngles into lerpOrigin and lerpAngles
            4. If a player entity, call CG_ResetPlayerEntity()
              1. Eliminate error decays (???)
              2. Reset a bunch of animation information.
          5. ??? -- CG_CheckEvents()
            1. If centity's eType is an event:
              1. Do nothing (return) if event already fired (occured)
              2. Mark as already now being fired
              3. Record event type into cent->currentState.event
            2. otherwise:
              1. Do nothing (return) if event is attached to (riding on) another entity
              2. Copy current event into cent->previousEvent
              3. Do nothing if event is not supposed to occur (bitmask flag)?
            3. Calculate new position -- BG_EvaluateTrajectory()
            4. ??? -- CG_SetEntitySoundPosition()
              1. If an inline model, place entity sound position at model's midpoint -- trap_S_UpdateEntityPosition()
              2. Otherwise, place entity sound position at calculated location -- trap_S_UpdateEntityPosition()
            5. ??? -- CG_EntityEvent()
              1. (XXX: EEK!)
    5. Loop ceaselessly:
      1. If cg.nextSnap is empty:
        1. Retrieve next snapshot into snap -- CG_ReadNextSnapshot()
        2. If no snap is available, break loop any (extrapolate)
        3. ??? -- CG_SetNextSnap()
          1. Set cg.nextSnap to the new snap
          2. Extract entityState_t information from playerState_t information in snap
          3. Allow position interpolation
          4. For each entity listed in snap:
            1. Clear cent->nextState
            2. If teleporting, or otherwise doesn't have a prior position to sensibly interpolate from, turn off interpolation; otherwise turn it on.
          5. If client number in current and next snap don't match, act as if teleporting (?)
          6. Something about server restart and not interpolating???
          7. Build the list of solid and trigger entities -- CG_BuildSolidList()
        4. If server time jumped backwards, interpret as level restart.
      2. If cg.time is between cg.snap->serverTime and cg.nextSnap->serverTime, end the loop (interpolate)
      3. Make the transition to the next snapshot -- CG_TransitionSnapshot()
        1. Complain if cg.snap or cg.nextSnap is/are null
        2. ??? -- CG_ExecuteNewServerCommands()
        3. Invalidate all entities in existing snap
        4. Temporarily save cg.snap, move cg.nextSnap into cg.snap
        5. Update clients entity information
        6. For all entities in (new) snap:
          1. ??? -- CG_TransitionEntity()
            1. If centity position is not to be interpolated (popped into view, teleported in, respawned), reset the entity -- CG_ResetEntity()
            2. Turn off interpolation (clear next state?)
            3. Check for events -- CG_CheckEvents()
          2. Update recorded server time
          3. Check if client is teleporting
          4. If not doing client-side prediction, ??? -- CG_TransitionPlayerState()
    6. Assert various conditions (trigger fatal error if any check is false)
  8. Draw loading screen and return if no snapshots received?
  9. Notify Q3 engine of weapon and zoom (mouse) sensitivity -- trap_SetUserCmdValue()
  10. Keep track of how many frames have been rendered so far in cg.clientFrame
  11. Update cg.predictedPlayerState -- CG_PredictPlayerState()
    1. Set cg.hyperspace to qfalse (why???)
    2. If no valid predicted player state data yet, fill it in as needed
    3. If playing back a demo, interpolation movements and return -- CG_InterpolatePlayerState()
      1. if allowing local input (disabled in spectating or demo playback), short-circuit the view angles (change it despite what server says) -- PM_UpdateViewAngles()
      2. If teleporting, return (no point in interpolating)
      3. Check for inconsistencies(?)
      4. Into f goes the fraction between prev->serverTime and next->serverTime that cg.time resides
      5. Set bobbing accordingly.
      6. Move entity by the calculated fractional amount
      7. Change entity fractionally as well if local input allowed
    4. Set up for call to Pmove()
    5. Saved old player state info.
    6. Retrieve current command number into current -- trap_GetCurrentCmdNumber()
    7. If can't get a good command (?), just return (effect is freezing in place) -- trap_GetUserCmd()
    8. Get latest commands -- trap_GetUserCmd()
    9. Use cg.snap or cg.nextSnap depending on next snap being available and teleporting
    10. Retrict cvar pmove_msec within a certain range
    11. Set cg_pmove.pmove_fixed to pmove_fixed
    12. Set cg_pmove.pmove_msec to pmove_msec
  12. Go into third-person view (cg.renderingThirdPerson) if cvar cg_thirdPerson is non-zero, or if dead
  13. Calculate the stuff related to the viewing camera (client's eye(s)) -- CG_CalcViewValues()
    1. Clear cg.refdef
    2. Calculate the portions of the screen to draw camera view within (view rectangle, how much black border around the screen edge) -- CG_CalcVrect()
      1. If intermission, fill up the entire screen (size = 100)
      2. Otherwise, clip cvar cg_viewsize within 30 and 100.
      3. Ensure cg.refdef.width and cg.refdef.height are even numbers.
      4. Center the view rectangle.
    3. If intermission view:
      1. Copy server-reported location into refdef's view origin
      2. Copy server-reported view angles into redef's view angles
      3. Calculate FOV and return -- CG_CalcFov()
    4. Set bobbing.
    5. Something about speed on X-Y plane...
    6. Update camera orbiting data
    7. If errordecay:
      1. Calculate decay fraction.
      2. Shift refdef's view origin accordingly.
    8. Check if drawing in third-person view -- CG_OffsetThirdPersonView()
    9. Otherwise draw in first-person view -- CG_OffsetFirstPersonView()
    10. Position the eyes properly
    11. If not in hyperspace... XXX: ???
    12. Calculate field of view/vision (FOV) -- CG_CalcFov()
      1. If in intermission, fix FOV at 90 degrees
      2. Otherwise, calculate FOV based on cg_fov, clipping within range as needed, and alter based on zoom state; or keep at 90 if server set DF_FIXED_FOV bit in dmflags
      3. Convert FOV degrees to radians.
      4. If inside liquid:
        1. Munge X and Y components of FOV by a sinusoidal function, each component phase-shifted by a half-cycle (widest when shortest, tallest when narrowest)
      5. Set cg.refdef.fov_x and cg.refdef.fov_y
      6. Set cg.zoomSensitivity according to zoomed state.
  14. Show damage blob (the screen-reddening effect when damaged) -- CG_DamageBlendBlob()
    1. If no damage received, return
    2. If detected RagePro vidcard, return
    3. If beyond display time, return
    4. Clear blob entity
    5. Set blob entity parameters (sprite, shown only in first-person view)
    6. Place blob entity right in front of camera
    7. Set full color, set transparency proportional to blob display time (fade away)
    8. Add blob entity to rendering scene -- trap_R_AddRefEntityToScene()
  15. If not in hyperspace, build render list:
    1. Add entities specified in packet from server -- CG_AddPacketEntities()
      1. Calculate interpolation amount
      2. Calculate (all) rotation phase
      3. Calculate (all) fast-rotation phase
      4. Extract entityState_t from playerState_t of cg.predictedPlayerState
      5. Add player entity to rendering -- CG_AddCEntity()
        1. (XXX: EEK!)
      6. Lerp (linearly interpolate) lightning gun position -- CG_CalcEntityLerpPositions()
        1. If cvar cg_smoothClients is 0, client doesn't want extrapolated positions, force TR_INTERPOLATE trajectory type on clients' positions
        2. If TR_INTERPOLATE trajectory type, lerp entity's position -- CG_InterpolateEntityPosition()
          1. Complain if insufficient interpolation data (second position)
          2. Calculate the predicted entity position
          3. Lerp position according to interpolation fraction
          4. Calculate the predicted entity angles
          5. Lerp angles according to interpolation fraction -- Lerp_Angle()
        3. Use current frame and extrapolate position and angle
        4. Adjust for mover's movement if necessary -- CG_AdjustPositionForMover()
          1. Check that entity is valid
          2. Check entity is a mover
          3. Evaluate mover trajectory effect
      7. For each entities listed in the snapshot:
        1. Add the entity to rendering -- CG_AddCEntity()
    2. Add various marks to surfaces (burn marks, blood splatters, etc.) -- CG_AddMarks()
      1. If cvar cg_addMarks is 0, return
      2. For all recorded marks:
        1. Some linked list doohickerymajiggy?
        2. If mark's time expired, free the mark, move on to next mark -- CG_FreeMarkPoly()
          1. Remove from doubly-linked list of active marks
          2. Also update free list (a singly-linked list)
      3. For energy bursts (e.g. plasma-hit-wall, rail-hit-wall), fade out colors based on its age (mp->time)
      4. Fade out the mark based on age.
      5. Add mark entity to render scene -- trap_AddPolyToScene()
    3. Add entities that are implied by game entities, or explicitly generated locally -- CG_AddLocalEntities()
      1. Traverse through the list cg_activeLocalEntities backwards:
        1. grab pointer to next in case of early freeing.
        2. if local entity lifespan exceeded, remove -- CG_FreeLocalEntity()
        3. Based on entity type, call approriate function:
          • LE_MARK -- (handled elsewhere)
          • LE_SPRITE_EXPLOSION -- CG_AddSpriteExplosion()
          • LE_EXPLOSION -- CG_AddExplosion()
          • LE_FRAGMENT (gibs, brass) -- CG_AddFragment()
          • LE_MOVE_SCALE_FADE (water bubble) -- CG_AddMoveScaleFade()
          • LE_FADE_RGB (teleporter, railtrail) -- CG_AddFadeRGB()
          • LE_FALL_SCALE_FADE (gibs blood trail) -- CG_AddFallScaleFade()
          • LE_SCALE_FADE (rocket trail) -- CG_AddScaleFade()
          • LE_SCOREPLUM (floating score) -- CG_AddScorePlum()
      2. (XXX: EEK!)
  16. Draw the gun/weapon -- CG_AddViewWeapon()
    1. If spectator, return
    2. If in intermission, return
    3. If in third person view, don't draw weapon view (it gets rendered in whole, anyway)
    4. If cvar cg_drawGun is 0, don't draw the gun, while doing some special position-shifting hack for lightning gun
    5. If testing a gun model, return
    6. Position the gun lower at higher FOV values
    7. ??? -- CG_RegisterWeapon()
  17. ??? -- CG_PlayBufferedSounds()
    1. (play next sound stored in sound ring buffer)
  18. Play voice chat sounds -- CG_PlayBufferedVoiceChats()
    1. (Q3 Team Arena only, skipping)
  19. Add test model specified by "testmodel" client console command -- CG_AddTestModel()
  20. Record cg.time into cg.refdef.time
  21. Copy cg.snap->areamask into cg.refdef.areamask
  22. Play any sounds associated with powerups wearing out (expiring) -- CG_PowerupTimerSounds()
  23. update audio positions -- trap_S_Respatialize().
  24. Draw lag-o-meter, but make sure it isn't done twice in stereoscopic view -- CG_AddLagometerFrameInfo()
    1. Record lagometer sample offset number info lagometer.frameSamples[], update lagometer.frameCount
  25. um... something about cvar timescale?...
  26. Make rendering calls -- CG_DrawActive()
    1. If no baseline snapshot (cg.snap) still, draw the info/loading screen -- CG_DrawInformation()
    2. If a spectator and looking at scoreboard, display tournament scoreboard -- CG_DrawTourneyScoreboard()
    3. Adjust separation based on stereoscopic view
    4. Add filler graphic around sized-down refresh window (sizeup/sizedown) -- CG_TileClear()
      1. Get video dimensions.
      2. If full-screen rendering, don't bother (return).
      3. Get the edges of the refresh screen.
      4. Clear the top edge of view screen -- CG_TileClearBox()
        1. Calculate how many repeats needed vertically and horizontally
        2. Tile the image -- trap_R_DrawStretchPic()
      5. Clear the bottom edge of view screen -- CG_TileClearBox()
      6. Clear the left edge of view screen -- CG_TileClearBox()
      7. Clear the right edge of view screen -- CG_TileClearBox()
    5. Shift cg.refdef.vieworg based on stereoscopic seperation
    6. Render the scene -- trap_R_RenderScene()
    7. Restore original viewpoint as needed
    8. Draw HUD and other 2D elements -- CG_Draw2D()
      1. If taking levelshot (levelshot is non-zero), don't bother (return)
      2. If draw2D is zero, don't bother (return)
      3. If at intermission, do intermission stuff -- CG_DrawIntermission()
        1. If single player mode, draw something... -- CG_DrawCenterString()
        2. otherwise, draw scoreboard -- CG_DrawScoreboard()
      4. If a spectator, do spectator things:
        1. ??? -- CG_DrawSpectator()
        2. ??? -- CG_DrawCrosshair()
        3. ??? -- CG_DrawCrosshairNames()
      5. Otherwise, non-spectator stuff:
        1. if alive and not looking at scores:
          1. Draw status bar -- CG_DrawStatusBar()
          2. Draw any ammo warning -- CG_DrawAmmoWarning()
          3. Draw crosshairs -- CG_DrawCrosshair()
          4. Draw crosshair names -- CG_DrawCrosshairNames()
          5. Draw weapon selection (the icons along bottom on weapon switch) -- CG_DrawWeaponSelect()
          6. Draw reward icons -- CG_DrawReward()
      6. in either case, then draw vote status -- CG_DrawVote()
      7. and any team votes -- CG_DrawTeamVote()
      8. Draw the lagometer -- CG_DrawLagometer()
      9. Draw uppper right stuff (fps, time) -- CG_DrawUpperRight()
      10. Draw lower right stuff (powerups, scores) -- CG_DrawLowerRight()
      11. Draw lower left stuff -- CG_DrawLowerLeft()
      12. Determine if spectating and following another client -- CG_DrawFollow
      13. Draw any warmup info if not following -- CG_DrawWarmup()
      14. Try to draw scoreboard -- CG_DrawScoreboard)
      15. If not showing scoreboard, try to draw any center strings -- CG_DrawCenterString()
  27. Print cg.clientFrame (frame number) if cvar cg_stats is non-zero.



Immediate branch to CG_CrosshairPlayer().


  1. If still within 1 second of crosshair time (XXX: what's that?), return cg.crosshairClientNum, otherwise return -1.



Immediate branch to CG_LastAttacker().


  1. If client was ever attacked (damaged), return last attacker (cg.snap->ps.persistant[PERS_ATTACKER])


Called when key-trapping is on (trap_Key_SetCatcher(KEYCATCH_CGAME)) and a key is pressed or released. Two arguments:

Keycode of the key event. Every key generates a keycode (not necessarily unique). For the alphanumeric keys, the keycode corresponds to the key's ASCII code. For other keys, the values are something else... (XXX: list keycodes).
Whether the event is generated by a key being pressed (qtrue) or being released (qfalse).

Immediate branch to CG_KeyEvent().


  1. (Empty in baseq3. Mods may use this space for something useful. Q3TA defines a CG_KeyEvent() with real meat; not covered here.)


Called when key-catching is enabled (trap_Set_KeyCatcher(KEYCATCH_CGAME)) and the mouse moves. No mouse event if the mouse is not moving. Button clicks count as key events (keycodes MOUSE1...MOUSE5, MWHEELUP, MWHEELDOWN). Two arguments:

Relative x (horizontal, left/right) motion. Positive values are towards the right, negative values towards the left.
Relative y (vertical, up/down) motion. Positive values are towards the bottom, negative values are towards the top.

Immediate branch to CG_MouseEvent.


  1. (Empty in baseq3. Mods may use this space for something useful. Q3TA defines a CG_MouseEvent() with real meat; not covered here.)


(I'd like to know!) type - 0 = no event handling, 1 = team menu, 2 = hud editor (what hud editor???)

Immediate branch to CG_EventHandling.


  1. (Empty in baseq3. Q3TA defines a CG_EventHandling() with real meat; not covered here.)

Created 2002.09.28
Updated 2011.07.11 - change of contact e-mail

PhaethonH < >