Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .cursor/rules/build.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: How to build and deploy Devotion on Windows
alwaysApply: true
---

# Build (Windows)

After changing game/cgame/ui code or when verifying a compile:

From repo root in PowerShell:

.\build_windows.ps1 -Quiet -Deploy

Do not use `ratoa_gamecode/windows_scripts/windows_compile_game.bat` unless explicitly asked — use the root script above.

See BUILD_WINDOWS.md for flags and troubleshooting.
1 change: 1 addition & 0 deletions docs/BOT-CVARS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Console variables that control bot AI, navigation, chat, and fill rules. Set the
| `bot_enhanced_tactics` | Devotion | `0` | 0 or 1 | Extra combat decisions when `1` and `bot_enhanced` is `1`: gauntlet rush/flee, react to third-party damage, finish wounded targets, prefer nearer threats. Saved to config. |
| `bot_enhanced_items` | Devotion | `1` | 0 or 1 | Visible mega/red/yellow armor pickup with committed goals when `1` and `bot_enhanced` is `1`. Saved to config. |
| `bot_enhanced_items_debug` | Devotion | `0` | 0 or 1 | Server console lines when bots commit to or abandon major item pickups. |
| `bot_enhanced_movement` | Devotion | `0` | 0 or 1 | Human-like movement behaviors when `1` and `bot_enhanced` is `1`: active rocket-jump maneuver prep/fire and walk-off ledge fall-damage avoidance. Saved to config. |
| `bot_enhanced_weapons` | Devotion | `0` | 0 or 1 | Smarter weapon picks by range and ammo when `1` and `bot_enhanced` is `1` (e.g. rail/MG at distance, rocket mid-range, shotgun up close). Saved to config. |
| `bot_fastchat` | Vanilla | `0` | 0 or 1 | When `1`, bots are more likely to use chat lines (skips some random “stay quiet” rolls). |
| `bot_forceclustering` | Vanilla | `0` | 0 or 1 | Forces the navigation system to rebuild area clusters for the current map (slow; map load / AAS build). |
Expand Down
73 changes: 58 additions & 15 deletions docs/BOT-ENHANCED-ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ flowchart TB
| Aim harness | `bot_enhanced` + `bot_enhanced_aim` | Facade: `BotEnhanced_AimActive()` (`bot_challenge` ignored) |
| Smart weapons | `bot_enhanced` + `bot_enhanced_weapons` | Facade: `BotEnhanced_WeaponsActive()` |
| Tactical AI | `bot_enhanced` + `bot_enhanced_tactics` | Facade: `BotEnhanced_TacticsActive()` |
| Movement | `bot_enhanced` + `bot_enhanced_movement` | Facade: `BotEnhanced_MovementActive()`. Gates: enhanced RJ maneuver prep/fire; walk-off ledge fall-damage avoidance. Enhanced RJ also requires `bot_enhanced_aim` (view bypass). |
| Debug (cheat) | `bot_debugAim` | Independent of master; client `cg_debugBotAim` |

Legacy `bot_humanizeaim` / `bot_smartWeaponChoice` / `bot_tacticalAI` are migrated once at init if new cvars are still default (see BOT-CVARS.md).
Expand All @@ -62,16 +63,18 @@ Legacy `bot_humanizeaim` / `bot_smartWeaponChoice` / `bot_tacticalAI` are migrat

| File | Role |
|------|------|
| `ai_bot_enhanced.c/h` | Master cvar, facade gates, `OnThinkStart`, register/reset orchestration |
| `ai_bot_enhanced.c/h` | Master cvar, facade gates, `OnThinkStart`, register/reset orchestration, goal-stack overflow guards (`BotEnhanced_PushGoalSafe`, `BotEnhanced_ReserveGoalStackRoom`, `BotEnhanced_SanitizeGoalStack`) |
| `ai_bot_items.c/h` | Visible pickup commits, botlib item chooser wrappers (uses enhanced goal-stack API) |
| `ai_bot_combat.c/h` | `bot_combat_intent_t` on `bot_state_t`; stance/move/fire policy (defaults = legacy) |
| `ai_bot_events.c/h` | Ingress queue (`evt_*`); `BotEvents_Drain` → tactics scan/process |
| `ai_bot_move_harness.c/h` | Stub only (`BotMoveHarness_IsActive` always false) |
| `ai_aim_harness.c/h` | Humanized view motor + suppressive fire |
| `ai_weapon_select.c/h` | Range/ammo weapon picker + roam selection |
| `ai_bot_tactics.c/h` | Gauntlet flee, hurt-by-other, closer threat, finish wounded |
| `ai_main.h` | `combat`, `evt_*`, `aimh_*`, `wps_*`, `tact_*` blocks |
| `ai_dmq3.c` | `BotDeathmatchAI`, aim-at-enemy, `BotChooseWeapon` (facade at boundaries) |
| `ai_dmnet.c` | Battle/retreat node hooks into tactics |
| `ai_bot_move_harness.c/h` | Botlib movement-view bypass (`bot_enhanced_aim`); `bot_enhanced_movement` cvar + `BotEnhanced_MovementActive()` gate; enhanced RJ maneuver prep/fire (requires both aim + movement); walk-off ledge fall-damage avoidance; `BotMove_EffectiveTfl`; hooks think/input |
| `ai_bot_move_util.c/h` | Shared horiz walk, approach speed, view actuation, goal anchors for maneuvers |
| `ai_aim_harness.c/h` | Humanized view motor; monotonic menu skill 0–5 accuracy ladder; suppressive fire (wide cone, all skills); rail/RL/SG shot urgency (reload+grace without firing widens track/trace tolerances); rail lead-and-wait + trace/urgency fire |
| `ai_weapon_select.c/h` | Range/ammo weapon picker + roam selection; voluntary close combat (25%: SG > plasma > gauntlet) |
| `ai_bot_tactics.c/h` | Gauntlet flee, hurt-by-other, closer threat (skill + distance-scaled swap chance), finish wounded |
| `ai_main.h` | `combat`, `evt_*`, `aimh_*`, `movej_*`, `wps_*`, `tact_*` blocks |
| `ai_dmq3.c` | `BotDeathmatchAI`, aim-at-enemy, `BotChooseWeapon`, `BotJumppad_Update` (thin `BotJumppad_EffectiveTfl` → harness) |
| `ai_dmnet.c` | Battle/retreat node hooks; calls `BotEnhanced_*` goal-stack helpers before nearby/LTG item choose |
| `ai_main.c` | `BotUpdateInput` aim path |

---
Expand All @@ -89,7 +92,24 @@ Legacy `bot_humanizeaim` / `bot_smartWeaponChoice` / `bot_tacticalAI` are migrat

1. Extend `bot_move_policy_t` in `ai_bot_combat.h`.
2. Set policy in `BotCombat_UpdateIntent()`.
3. Wire `ai_bot_move_harness.c` when ready; call from `BotAttackMove` / `BotUpdateInput` only after `BotMoveHarness_IsActive()` is implemented.
3. Implement actuation in `ai_bot_move_harness.c` (reuse `ai_bot_move_util.c` for walk/view/helpers); call `BotMove_OnPostMoveToGoal` after `trap_BotMoveToGoal` and `BotMove_OnInputFrame` from `BotUpdateInput` when `BotMove_SuppressesAimMotor()`.

### Botlib movement + enhanced aim (rocket jump)

When `bot_enhanced_aim` is on, botlib `MOVERESULT_MOVEMENT*` (rocket/BFG jump, swim, activate shoot) must not be overridden by the aim harness motor.

| Hook | Where | Role |
|------|--------|------|
| `BotMove_OnPostMoveToGoal` | `ai_dmnet.c` after each `trap_BotMoveToGoal` | Cache moveresult flags; short latch after movement view |
| `BotMove_SuppressRoamView` | `ai_dmnet.c` roam-view branches | Skip roam ideal angles while botlib owns view |
| `BotMove_SuppressesAimMotor` | `ai_aim_harness.c`, `BotUpdateInput` | Skip harness motor / use legacy input path |
| `BotMove_OnInputFrame` | `BotUpdateInput` | Vanilla view smoothing + `trap_EA_GetInput` while suppressed |

No rocket-jump logic in `ai_dmq3.c`; botlib handles jump/attack timing.

**Enhanced RJ maneuver** (active prep/fire coordination) requires **both** `bot_enhanced_aim` (view-motor bypass) and `bot_enhanced_movement`. With only `bot_enhanced_aim` on, the aim motor still yields to botlib view during native RJ travel but the harness does not actively prepare or fire the jump. The standard `bot_rocketjump` botlib path is unaffected by either cvar.

**Walk-off ledge:** after `trap_BotMoveToGoal`, if `traveltype == TRAVEL_WALKOFFLEDGE` and `bot_enhanced_movement` is on, `trap_AAS_PredictClientMovement` with `SE_HITGROUNDDAMAGE` estimates fall damage; if damage ≥ half current health, the reach is avoided (`TFL_WALKOFFLEDGE` stripped, `BotResetAvoidReach`) and `BotItems_RequestUrgentHealth` commits a visible health pickup when possible.

### New world event (next think)

Expand Down Expand Up @@ -135,6 +155,7 @@ set bot_enhanced 0
set bot_enhanced_aim 0
set bot_enhanced_weapons 0
set bot_enhanced_tactics 0
set bot_enhanced_movement 0
set bot_challenge 0
set bot_debugAim 0
```
Expand All @@ -145,14 +166,17 @@ set bot_debugAim 0
|---|--------|----------|------|
| 1 | All enhanced **off** (defaults) | Vanilla bot aim, weapon pick, combat decisions | Pass |
| 2 | `bot_enhanced 1`; all sub-cvars **0** | Same as row 1 (master on, features off) | Pass |
| 3 | `bot_enhanced 1`; `bot_enhanced_aim 1` only | Smooth humanized aim; MG/LG hold fire; no snap-aim jitter | Pass |
| 3 | `bot_enhanced 1`; `bot_enhanced_aim 1` only | Humanized aim by menu skill 0–5 ladder; skill 5 ~0.94 harness accuracy; MG/LG suppressive fire wide cone; rail trace at skill 3+; view yields to botlib during native RJ travel | Pass |
| 4 | `bot_enhanced 1`; `bot_enhanced_weapons 1` only | Range/ammo-aware switches; roaming silent weapon bias | Pass |
| 5 | `bot_enhanced 1`; `bot_enhanced_tactics 1` only | Gauntlet flee/rush, third-party hurt switch, nearer threat, finish wounded | Pass |
| 6 | All enhanced **on** (`bot_enhanced 1` + all three sub-cvars `1`) | Same as pre-refactor with all three legacy features enabled together | Pass |
| 6 | `bot_enhanced 1` + aim + weapons + tactics (`1`) | Same as pre-refactor with all three legacy features enabled together | Pass |
| 7 | Row 3 + `bot_challenge 1` | Same as row 3 (harness stays on; legacy challenge snap path skipped) | Pass |
| 8 | Row 3 or 6 + `bot_debugAim 1` + client `cg_debugBotAim 1` | Debug lines unchanged (green wish, yellow crosshair) | Pass |
| 9 | Row 3 + maps with rocket jumps (`bot_rocketjump 1`) | Bots complete RJ via vanilla botlib; no stare-down / timeout retry loop; enhanced maneuver **off** | — |
| 10 | `bot_enhanced 1`; `bot_enhanced_aim 1`; `bot_enhanced_movement 1`; maps with RJ (`bot_rocketjump 1`) | Enhanced RJ maneuver active: bots approach spot, aim down, fire+jump; no retry stare-down | — |
| 11 | `bot_enhanced 1`; `bot_enhanced_movement 1` only (aim **off**); maps with ledge drops | Walk-off avoidance active (no enhanced RJ — requires aim too); bots reroute away from lethal drops; standard RJ via botlib unaffected | — |

**Row 1 trap (master gate):** `bot_enhanced 0` with all sub-cvars `1` must still behave as vanilla.
**Row 1 trap (master gate):** `bot_enhanced 0` with all sub-cvars `1` (including `bot_enhanced_movement`) must still behave as vanilla.

**Legacy migration (optional):** `bot_enhanced 0`, `set bot_humanizeaim 1` only in `server.cfg`, restart map → server prints deprecation line; aim matches row 3 after migration.

Expand All @@ -164,37 +188,56 @@ set bot_enhanced 0
set bot_enhanced_aim 0
set bot_enhanced_weapons 0
set bot_enhanced_tactics 0
set bot_enhanced_movement 0

// Row 3 — aim only
set bot_enhanced 1
set bot_enhanced_aim 1
set bot_enhanced_weapons 0
set bot_enhanced_tactics 0
set bot_enhanced_movement 0

// Row 4 — weapons only
set bot_enhanced 1
set bot_enhanced_aim 0
set bot_enhanced_weapons 1
set bot_enhanced_tactics 0
set bot_enhanced_movement 0

// Row 5 — tactics only
set bot_enhanced 1
set bot_enhanced_aim 0
set bot_enhanced_weapons 0
set bot_enhanced_tactics 1
set bot_enhanced_movement 0

// Row 6 — all on
// Row 6 — aim + weapons + tactics (no movement)
set bot_enhanced 1
set bot_enhanced_aim 1
set bot_enhanced_weapons 1
set bot_enhanced_tactics 1
set bot_enhanced_movement 0

// Row 10 — aim + movement (enhanced RJ maneuver + ledge avoidance)
set bot_enhanced 1
set bot_enhanced_aim 1
set bot_enhanced_weapons 0
set bot_enhanced_tactics 0
set bot_enhanced_movement 1

// Row 11 — movement only (ledge avoidance; no enhanced RJ without aim)
set bot_enhanced 1
set bot_enhanced_aim 0
set bot_enhanced_weapons 0
set bot_enhanced_tactics 0
set bot_enhanced_movement 1
```

### Regression focus

While running rows 3–6, watch for regressions vs. known pre-refactor behavior:

- **Weapon switching cadence** — no rapid flip-flop; MG not primary at long range when rail/RL available.
- **Weapon switching cadence** — no rapid flip-flop; MG not primary at long range when rail/RL available; low combat-skill bots penalized more for audible rail/LG while roaming.
- **Gauntlet-only survival** — bot flees when far with only gauntlet; rushes when close (tactics).
- **MG + humanize aim** — sustained fire on target with smooth view tracking, not single-tap snap shots.
- **Third-party damage** — bot fighting A switches toward B when B chips them (tactics, row 5/6).
Expand All @@ -211,7 +254,7 @@ Do not add new stance/move gameplay until:
- [x] `BotCombat_OnWeaponCommitted` called from weapon notify (stub)
- [x] `BotEvents_*` ingress; drain only from `OnThinkStart`
- [x] Legacy hooks use `BotEnhanced_*Active()` at boundaries
- [x] `ai_bot_move_harness` linked (no-op)
- [x] `ai_bot_move_harness` — botlib movement bypass for enhanced aim
- [x] This document (architecture + parity table)
- [x] **Parity table rows 1–8 passed** (manual sign-off)

Expand Down
Binary file added ratoa_assets/assets/music/sad_synthwave.ogg
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions ratoa_gamecode/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,7 @@ Q3GOBJ_ = \
$(B)/baseq3/game/ai_bot_enhanced.o \
$(B)/baseq3/game/ai_bot_combat.o \
$(B)/baseq3/game/ai_bot_events.o \
$(B)/baseq3/game/ai_bot_move_util.o \
$(B)/baseq3/game/ai_bot_move_harness.o \
$(B)/baseq3/game/ai_aim_harness.o \
$(B)/baseq3/game/ai_weapon_select.o \
Expand Down Expand Up @@ -1863,6 +1864,7 @@ MPGOBJ_ = \
$(B)/missionpack/game/ai_bot_enhanced.o \
$(B)/missionpack/game/ai_bot_combat.o \
$(B)/missionpack/game/ai_bot_events.o \
$(B)/missionpack/game/ai_bot_move_util.o \
$(B)/missionpack/game/ai_bot_move_harness.o \
$(B)/missionpack/game/ai_aim_harness.o \
$(B)/missionpack/game/ai_weapon_select.o \
Expand Down
4 changes: 4 additions & 0 deletions ratoa_gamecode/code/cgame/cg_consolecmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ static void CG_ScoresDown_f( void ) {
if (cg.predictedPlayerState.pm_type == PM_INTERMISSION) {
cg.showScoreboardNum++;
}

/* Load deferred player models when the scoreboard is opened (cg_deferPlayers 1). */
cg.deferredPlayerLoading = 0;
CG_LoadDeferredPlayers();
}

static void CG_ScoresUp_f( void ) {
Expand Down
24 changes: 0 additions & 24 deletions ratoa_gamecode/code/cgame/cg_draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -6391,30 +6391,6 @@ void CG_DrawEngineSupport(void) {
0);
return;
}
/*
if (!CG_SupportsOggVorbis()) {
static int drawEngineMessageTime = 0;
const char *s = "WARNING: Engine lacks Ogg Vorbis support, some sounds might not play!";
float char_h = 12;
float char_w = CG_HeightToWidth(6);
float color[4] = { 1.0, 1.0, 0.0, 1.0 };

if (drawEngineMessageTime == 0) {
drawEngineMessageTime = cg.time;
} else if (drawEngineMessageTime + 60 * 1000 < cg.time) {
return;
}
CG_DrawStringExt( SCREEN_WIDTH * 0.5 - (CG_DrawStrlen(s) * char_w)/2.0,
1,
s,
color,
qtrue,
qfalse,
char_w,
char_h,
0);
}
*/
}

#define HELPMOTDOVERLAY_LINES CONSOLE_MAXHEIGHT
Expand Down
19 changes: 13 additions & 6 deletions ratoa_gamecode/code/cgame/cg_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,16 +682,23 @@ static void CG_RegisterItemSounds( int itemNum ) {
}
}

/*
===============
CG_SupportsOggVorbis

Probe whether the client engine is capable of loading OGG-format audio files.
Note: This function is duplicated in ui_menu.c as ui_SupportsOggVorbis
because they're built as separate QVMs without a suitable cross-linked header
to put the function in
===============
*/
qboolean CG_SupportsOggVorbis(void) {
static int supports_ogg = -1;

//We only ever test this once per game load, second attempt will just fall through this check and return
if (supports_ogg == -1) {
qhandle_t ogg = trap_S_RegisterSound("sound/testoggvorbis.ogg", qtrue);
if (ogg) {
supports_ogg = 1;
} else {
supports_ogg = 0;
}
qhandle_t ogg = trap_S_RegisterSound("music/sad_synthwave.ogg", qtrue);
supports_ogg = (ogg != 0) ? 1 : 0;
}

return (qboolean)supports_ogg;
Expand Down
40 changes: 22 additions & 18 deletions ratoa_gamecode/code/cgame/cg_players.c
Original file line number Diff line number Diff line change
Expand Up @@ -1001,10 +1001,10 @@ static void CG_SetDeferredClientInfo( int clientNum, clientInfo_t *ci ) {
}
}

// find the first valid clientinfo and grab its stuff
// find the first valid, fully-loaded clientinfo and grab its stuff
for ( i = 0 ; i < cgs.maxclients ; i++ ) {
match = &cgs.clientinfo[ i ];
if ( !match->infoValid ) {
if ( !match->infoValid || match->deferred ) {
continue;
}

Expand Down Expand Up @@ -1321,6 +1321,8 @@ clientInfo_t *ci;
clientInfo_t newInfo;
const char *configstring;
const char *v;
const char *modelConfig;
const char *headModelConfig;
char *slash;
const char *local_config;
int local_team;
Expand Down Expand Up @@ -1436,18 +1438,20 @@ clientInfo_t *ci;
Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME);

// model
v = Info_ValueForKey( configstring, "model" );
CG_SetSkinAndModel( &newInfo, v, allowNativeModel, viewerTeam, clientNum, qtrue,
newInfo.modelName, sizeof( newInfo.modelName ), newInfo.skinName, sizeof( newInfo.skinName ) );
modelConfig = Info_ValueForKey( configstring, "model" );

CG_SetSkinAndModel( &newInfo, modelConfig, allowNativeModel, viewerTeam, clientNum, qtrue,
newInfo.modelName, sizeof( newInfo.modelName ), newInfo.skinName, sizeof( newInfo.skinName ) );

if ( cg_teamColor.string[0] && team != TEAM_SPECTATOR ) {
v = CG_GetTeamColorsOSP( cg_teamColor.string );
len = strlen( v );
const char *ospColors;

ospColors = CG_GetTeamColorsOSP( cg_teamColor.string );
len = strlen( ospColors );
if ( len >= 4 )
CG_ColorFromChar( v[3], newInfo.color1 );
CG_ColorFromChar( ospColors[3], newInfo.color1 );
if ( len >= 5 )
CG_ColorFromChar( v[4], newInfo.color2 );
CG_ColorFromChar( ospColors[4], newInfo.color2 );
}

if (CG_IsTeamGametype()) {
Expand Down Expand Up @@ -1522,14 +1526,14 @@ clientInfo_t *ci;
}

if (CG_IsTeamGametype()) {
// keep skin name
slash = strchr( v, '/' );
// keep skin name from this player's configstring model
slash = strchr( modelConfig, '/' );
if ( slash ) {
Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
}
}
} else {
Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) );
Q_strncpyz( newInfo.modelName, modelConfig, sizeof( newInfo.modelName ) );

slash = strchr( newInfo.modelName, '/' );
if ( !slash ) {
Expand All @@ -1543,9 +1547,9 @@ clientInfo_t *ci;
}

// head model
v = Info_ValueForKey( configstring, "hmodel" );
headModelConfig = Info_ValueForKey( configstring, "hmodel" );

CG_SetSkinAndModel( &newInfo, v, allowNativeModel, viewerTeam, clientNum, qtrue,
CG_SetSkinAndModel( &newInfo, headModelConfig, allowNativeModel, viewerTeam, clientNum, qtrue,
newInfo.headModelName, sizeof( newInfo.headModelName ), newInfo.headSkinName, sizeof( newInfo.headSkinName ) );

if (cgs.ratFlags & RAT_ALLOWFORCEDMODELS &&
Expand Down Expand Up @@ -1606,14 +1610,14 @@ clientInfo_t *ci;
}

if (CG_IsTeamGametype()) {
// keep skin name
slash = strchr( v, '/' );
// keep skin name from this player's configstring head model
slash = strchr( headModelConfig, '/' );
if ( slash ) {
Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
}
}
} else {
Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) );
Q_strncpyz( newInfo.headModelName, headModelConfig, sizeof( newInfo.headModelName ) );

slash = strchr( newInfo.headModelName, '/' );
if ( !slash ) {
Expand Down
12 changes: 7 additions & 5 deletions ratoa_gamecode/code/cgame/cg_servercmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -1540,15 +1540,17 @@ static void CG_ConfigStringModified( void ) {
cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str, qfalse );
}
} else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) {
CG_NewClientInfo( num - CS_PLAYERS );
if (num - CS_PLAYERS == cg.clientNum) {
// make sure to update all other player models in case we switched teams
int clientNum;

clientNum = num - CS_PLAYERS;
CG_NewClientInfo( clientNum );
if ( clientNum == cg.clientNum ) {
/* Local team/userinfo change: refresh all clients (team/enemy model rules). */
CG_ForceModelChange();
CG_ParseForcedColors();
}
/* Other slots: CG_NewClientInfo only (defer 0 loads there; defer 1 waits for scoreboard). */
CG_BuildSpectatorString();
CG_ForceModelChange(); //duffman91 - for bugs w/ quake 3 pm models.
CG_ParseForcedColors();
} else if ( num == CS_FLAGSTATUS ) {
// if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION || cgs.gametype == GT_DOUBLE_D) {
if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTF_ELIMINATION ) {
Expand Down
Loading