//======================================================================= // Copyright XashXT Group 2007 © // cg_user.c - stuff that will moved into client.dat //======================================================================= #include "common.h" #include "client.h" extern cvar_t *scr_centertime; extern cvar_t *scr_showpause; #define DISPLAY_ITEMS 17 #define STAT_MINUS 10 // num frame for '-' stats digit char *cg_nums[11] = { "hud/num_0", "hud/num_1", "hud/num_2", "hud/num_3", "hud/num_4", "hud/num_5", "hud/num_6", "hud/num_7", "hud/num_8", "hud/num_9", "hud/num_-", }; /* ================ CG_Init initialize cg ================ */ void CG_Init( void ) { cls.cg_numstats = 0; // reset hudprogram statsnames cls.cg_numcvars = 0; // reset hudprogram statsnames cls.cg_init = Com_LoadScript( "scripts/hud.txt", NULL, 0 ); CG_ExecuteProgram( "Hud_Setup" ); // get stats and cvars names } /* ================ V_RenderSplash menu background ================ */ void V_RenderSplash( void ) { // execute hudprogram CG_ExecuteProgram( "Hud_MenuBackground" ); } /* ================ V_RenderLogo loading splash ================ */ void V_RenderLogo( void ) { // execute hudprogram CG_ExecuteProgram( "Hud_DrawPlaque" ); } /* ================ V_RenderHUD user hud rendering ================ */ void V_RenderHUD( void ) { CG_MakeLevelShot(); CG_DrawCenterString(); CG_ExecuteProgram(cl.configstrings[CS_STATUSBAR]); CG_DrawInventory(); CG_DrawLayout(); } /* ============== CG_MakeLevelShot used as splash logo ============== */ void CG_MakeLevelShot( void ) { if(cl.make_levelshot) { Con_ClearNotify(); cl.make_levelshot = false; // make levelshot at nextframe() Cbuf_ExecuteText(EXEC_APPEND, "levelshot\n"); } } /* ============================================================================== CG Draw Tools ============================================================================== */ /* ================ SCR_GetBigStringWidth skips color escape codes ================ */ int CG_GetBigStringWidth( const char *str ) { return com.cstrlen( str ) * BIGCHAR_WIDTH; } /* ================ CG_FadeColor ================ */ float *CG_FadeColor( float starttime, float endtime ) { static vec4_t color; float time, fade_time; if( starttime == 0 ) return NULL; time = cls.realtime - starttime; if( time >= endtime ) return NULL; // fade time is 1/4 of endtime fade_time = endtime / 4; fade_time = bound( 0.3f, fade_time, 10.0f ); // fade out if((endtime - time) < fade_time) color[3] = (endtime - time) * 1.0f / fade_time; else color[3] = 1.0; color[0] = color[1] = color[2] = 1.0f; return color; } void CG_SetColor( float r, float g, float b, float a ) { Vector4Set( cls.cg_color, r, g, b, a ); re->SetColor( cls.cg_color ); } void CG_ResetColor( void ) { Vector4Set( cls.cg_color, 1.0f, 1.0f, 1.0f, 1.0f ); re->SetColor( cls.cg_color ); } void CG_SetScale( float x, float y ) { cls.cg_scale[0] = x; cls.cg_scale[1] = y; } void CG_ResetScale( void ) { cls.cg_scale[0] = cls.cg_scale[1] = 1.0f; } /* ================ CG_DrawLayout ================ */ void CG_DrawLayout( void ) { if(cl.frame.playerstate.stats[STAT_LAYOUTS] & 1) CG_ExecuteProgram( cl.layout ); } /* ================ CG_DrawInventory ================ */ void CG_DrawInventory( void ) { float *select_color = NULL; int num = 0, selected_num = 0; int item, index[MAX_ITEMS]; bool force_color = false; char string[1024]; int i, j, x, y; char binding[1024]; char *bind; int selected; int top; if(!(cl.frame.playerstate.stats[STAT_LAYOUTS] & 2)) return; if(strcmp(cl.layout, "" )) { CG_ExecuteProgram( cl.layout ); return; } selected = cl.frame.playerstate.stats[STAT_SELECTED_ITEM]; num = 0; selected_num = 0; for ( i = 0; i < MAX_ITEMS; i++) { if (i == selected) selected_num = num; if (cl.inventory[i]) { index[num] = i; num++; } } // determine scroll point top = selected_num - DISPLAY_ITEMS/2; if (num - top < DISPLAY_ITEMS) top = num - DISPLAY_ITEMS; if (top < 0) top = 0; x = (SCREEN_WIDTH - 256)>>1; y = (SCREEN_HEIGHT - 240)>>1; CG_DrawCenterPic( 256, 192, "hud/inventory" ); y += 42; x += 24; SCR_DrawSmallStringExt(x, y, "hotkey ### item", NULL, false ); SCR_DrawSmallStringExt(x, y + 8, "------ --- ----", NULL, false ); y += 16; for (i = top; i < num && i < top + DISPLAY_ITEMS; i++) { item = index[i]; // search for a binding com.sprintf(binding, "use %s", cl.configstrings[CS_ITEMS + item]); bind = ""; for (j = 0; j < 256; j++) { if(Key_IsBind(j) && !strcasecmp(Key_IsBind(j), binding)) { bind = Key_KeynumToString(j); break; } } com.sprintf(string, "%6s %3i %s", bind, cl.inventory[item], cl.configstrings[CS_ITEMS+item] ); if (item != selected) { select_color = GetRGBA( 1.0f, 0.5f, 0.0f, 1.0f ); force_color = true; } else // draw a blinky cursor by the selected item { select_color = NULL; force_color = false; if((int)(cls.realtime * 5.0f) & 1) SCR_DrawSmallChar( x - 8, y, 15); } SCR_DrawSmallStringExt(x, y, string, select_color, force_color ); y += 8; } } void CG_DrawField( int value, int x, int y ) { char num[16], *ptr; int l, frame; vec2_t scale; com.sprintf(num, "%i", value ); l = strlen( num ); ptr = num; if( cls.cg_scale[0] == 1.0f ) scale[0] = GIANTCHAR_WIDTH; else scale[0] = cls.cg_scale[0]; if( cls.cg_scale[1] == 1.0f ) scale[1] = GIANTCHAR_HEIGHT; else scale[1] = cls.cg_scale[1]; while (*ptr && l) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; SCR_DrawPic( x, y, scale[0], scale[1], cg_nums[frame]); x += scale[0]; ptr++; l--; } re->SetColor( NULL ); CG_ResetScale(); } void CG_DrawBarImage( float percent, char *picname, int x, int y, int w, int h ) { float progress = bound(0.0f, scr_loading->value * 0.01f, 100.0f ); w = bound(64.0f, w, 512.0f); h = bound(16.0f, h, 64.0f); re->SetColor(GetRGBA(1.0f, 1.0f, 1.0f, 1.0f)); SCR_DrawPic( x, y, w, h, picname ); re->SetColor(GetRGBA(1.0f, 1.0f, 1.0f, 0.3f)); SCR_DrawPic( x, y, w * progress, h, "common/fill_rect"); CG_ResetColor(); } void CG_DrawCenterBarImage( float percent, char *picname, int w, int h ) { CG_DrawBarImage(percent, picname, (SCREEN_WIDTH - w)>>1, (SCREEN_HEIGHT - h)>>1, w, h ); } void CG_DrawBarGeneric( float percent, int x, int y, int w, int h ) { float progress = bound(0.0f, percent * 0.01f, 100.0f ); w = bound(64.0f, w, 512.0f); h = bound(16.0f, h, 64.0f); SCR_FillRect(x, y, w, h, g_color_table[0] ); SCR_DrawPic(x + 1, y + 1, w - 2, h - 2, "common/bar_back"); SCR_DrawPic(x + 1, y + 1, (w - 2) * progress, h - 2, "common/bar_load"); } void CG_DrawCenterBarGeneric( float percent, int w, int h ) { CG_DrawBarGeneric( percent, (SCREEN_WIDTH - w)>>1, (SCREEN_HEIGHT - h)>>1, w, h ); } /* =================== CG_DrawCenterString =================== */ void CG_DrawCenterString( void ) { char *start; int l, x, y, w; float *color; if(!cl.centerPrintTime ) return; color = CG_FadeColor( cl.centerPrintTime, scr_centertime->value ); if( !color ) { cl.centerPrintTime = 0.0f; return; } re->SetColor( color ); start = cl.centerPrint; y = cl.centerPrintY - cl.centerPrintLines * BIGCHAR_HEIGHT/2; while( 1 ) { char linebuffer[1024]; for ( l = 0; l < 50; l++ ) { if ( !start[l] || start[l] == '\n' ) break; linebuffer[l] = start[l]; } linebuffer[l] = 0; w = cl.centerPrintCharWidth * com.cstrlen( linebuffer ); x = ( SCREEN_WIDTH - w )>>1; SCR_DrawStringExt( x, y, cl.centerPrintCharWidth, BIGCHAR_HEIGHT, linebuffer, color, false ); y += cl.centerPrintCharWidth * 1.5; while ( *start && ( *start != '\n' )) start++; if( !*start ) break; start++; } re->SetColor( NULL ); } /* ============== CG_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void CG_CenterPrint( const char *str, int y, int charWidth ) { char *s; strncpy( cl.centerPrint, str, sizeof(cl.centerPrint)); cl.centerPrintTime = cls.realtime; cl.centerPrintY = y; cl.centerPrintCharWidth = charWidth; // count the number of lines for centering cl.centerPrintLines = 1; s = cl.centerPrint; while( *s ) { if (*s == '\n') cl.centerPrintLines++; s++; } } /* ============== CG_DrawCenterPic Called for important messages that should stay in the center of the screen for a few moments ============== */ void CG_DrawCenterPic( int w, int h, char *picname ) { SCR_DrawPic((SCREEN_WIDTH - w)>>1, (SCREEN_HEIGHT - h)>>1, w, h, picname ); } void CG_DrawCenterStringBig( char *text, float alpha ) { int xpos = (SCREEN_WIDTH - com.cstrlen(text) * BIGCHAR_WIDTH)>>1; int ypos = (SCREEN_HEIGHT - BIGCHAR_HEIGHT)>>1; SCR_DrawBigString(xpos, ypos, text, alpha ); re->SetColor( NULL ); } /* ============================================================================== CG Virtual Machine ============================================================================== */ /* ================ CG_GetAliasValue get name from registered list ================ */ bool CG_GetAliasValue( const char *name, int *value, char *string ) { int i, find = 0; if(!name || !name[0]) return false; // lookup stats for(i = 0; i < cls.cg_numstats; i++) { if(!strcmp(cls.cg_stats[i].name, name )) { find = cl.frame.playerstate.stats[cls.cg_stats[i].value]; break; } } if(i != cls.cg_numstats) { *value = find; return true; } // lookup cvars for(i = 0; i < cls.cg_numcvars; i++) { if(!strcmp(cls.cg_cvars[i].name, name )) { cvar_t *alias = Cvar_FindVar( cls.cg_cvars[i].cvar ); if(alias) { find = alias->integer; if(string) strncpy( string, alias->string, MAX_QPATH ); break; } MsgDev(D_WARN, "CG_GetAliasValue: alias %s have invalid cvar name %s\n", name, cls.cg_cvars[i].cvar ); } } if(i != cls.cg_numcvars) { *value = find; return true; } // constant digit ? find = atoi(name); if(find) { *value = find; return true; } *value = 0; return false; } char *CG_StatsString( const char *name, int start, int end ) { int i, value; if(!name || !name[0]) return "common/black"; // clear tempstring memset( cls.cg_tempstring, 0, MAX_QPATH ); // search for alias if(!CG_GetAliasValue( name, &value, cls.cg_tempstring )) { // search for normal name for(i = 0; i < end; i++) { if(!strcmp(cl.configstrings[start + i], name )) { // index can be changed from server return cl.configstrings[start + i]; } } } else if(strlen(cls.cg_tempstring)) { return cls.cg_tempstring; } else if(value < end) { // static image index return cl.configstrings[start + value]; } // direct path ? return (char *)name; } char *CG_ImageIndex( const char *name ) { return CG_StatsString(name, CS_IMAGES, MAX_IMAGES ); } char *CG_ModelIndex( const char *name ) { return CG_StatsString(name, CS_MODELS, MAX_MODELS ); } bool CG_GetInteger( const char *name, int *value ) { return CG_GetAliasValue( name, value, NULL ); } /* ================ CG_ParseInventory ================ */ void CG_ParseInventory( sizebuf_t *msg ) { int i; for (i = 0; i < MAX_ITEMS; i++) { cl.inventory[i] = MSG_ReadShort( msg ); } } /* ================ CG_SetStatsAlias register new alias for stats num (0-32) ================ */ void CG_SetStatsAlias( const char *name, int value ) { int i; if(!name) return; if(strlen(name) > MAX_QPATH) { MsgDev(D_WARN, "SetStatsAlias: %s too long name, limit is %d\n", name, MAX_QPATH ); } if(value < 0 || value >= MAX_STATS) { MsgDev(D_WARN, "SetStatsAlias: value %d out of range\n", value ); value = bound(0, value, MAX_STATS - 1 ); } if(cls.cg_numstats + 1 >= MAX_STATS) { MsgDev(D_WARN, "SetStatsAlias: aliases limit exceeded\n" ); return; } // check for duplicate for(i = 0; i < cls.cg_numstats; i++) { if(!strcmp(cls.cg_stats[i].name, name )) { if(cls.cg_stats[i].value != value) MsgDev(D_WARN, "SetStatsAlias: redefinition stat alias %s\n", name ); else MsgDev(D_WARN, "SetStatsAlias: duplicated stat alias %s\n", name ); break; } } // register new stat alias if(i == cls.cg_numstats) { strncpy(cls.cg_stats[cls.cg_numstats].name, name, MAX_QPATH ); cls.cg_stats[cls.cg_numstats].value = value; cls.cg_numstats++; } } /* ================ CG_SetCvarAlias register new alias for console variable ================ */ void CG_SetCvarAlias( const char *name, const char *cvar ) { int i; if(!name || !cvar) return; if((strlen(name) > MAX_QPATH)) { MsgDev(D_WARN, "SetCvarAlias: %s too long name, limit is %d\n", name, MAX_QPATH ); } if(strlen(cvar) > MAX_QPATH) { MsgDev(D_WARN, "SetCvarAlias: %s too long cvar name, limit is %d\n", cvar, MAX_QPATH ); } if(cls.cg_numstats + 1 >= 128 ) { MsgDev(D_WARN, "SetCvarAlias: aliases limit exceeded\n" ); return; } // check for duplicate for(i = 0; i < cls.cg_numcvars; i++) { if(!strcmp(cls.cg_cvars[i].name, name )) { if(strcmp(cls.cg_cvars[i].cvar, cvar )) MsgDev(D_WARN, "SetCvarAlias: redefinition cvar alias %s\n", name ); else MsgDev(D_WARN, "SetCvarAlias: duplicated cvar alias %s\n", name ); break; } } // register new cvar alias if(i == cls.cg_numcvars) { strncpy(cls.cg_cvars[cls.cg_numcvars].name, name, MAX_QPATH ); strncpy(cls.cg_cvars[cls.cg_numcvars].cvar, cvar, MAX_QPATH ); cls.cg_numcvars++; } } /* ================ CG_ParseArgs soul of virtual machine ================ */ bool CG_ParseArgs( int num_argc ) { cls.cg_argc = 0; memset(cls.cg_argv, 0, MAX_PARMS * MAX_QPATH ); com.strncpy( cls.cg_builtin, com_token, MAX_QPATH ); // bound range silently num_argc = bound(0, num_argc, MAX_PARMS - 1); while(Com_TryToken()) { if(!num_argc) continue; // nothing to handle if(num_argc > 0 && cls.cg_argc > num_argc - 1 ) { MsgDev(D_ERROR, "CG_ParseArgs: %s have too many parameters\n", cls.cg_builtin ); return false; // stack overflow } else if(Com_MatchToken(";")) break; // end of parsing else if(Com_MatchToken(",")) cls.cg_argc++; // new argument else if(Com_MatchToken("(") || Com_MatchToken(")")) continue; // skip punctuation else com.strncpy(cls.cg_argv[cls.cg_argc], com_token, MAX_QPATH ); // fill stack } if(num_argc > 0 && cls.cg_argc < num_argc - 1) MsgDev(D_WARN, "CG_ParseArgs: %s have too few parameters\n", cls.cg_builtin ); return true; } void CG_SkipBlock( void ) { cls.cg_depth2 = cls.cg_depth; do { if(Com_MatchToken("{")) { // for bounds cheking cls.cg_depth++; } else if(Com_MatchToken("}")) { cls.cg_depth--; if(cls.cg_depth == cls.cg_depth2) break; } while(Com_TryToken()); } while(Com_GetToken( true )); if(cls.cg_depth != cls.cg_depth2) { MsgDev(D_ERROR, "CG_SkipBlock: missing } in function %s\n", cls.cg_function ); } } bool CG_ParseExpression( void ) { cg_def_t expression; int j = 0, result = 0; memset(&expression, 0, sizeof(cg_def_t)); cls.cg_depth2 = cls.cg_depth; while(Com_TryToken()) { if(Com_MatchToken("(")) { } else if(Com_MatchToken(")")) break; else if(Com_MatchToken("&&")) expression.op = OP_LOGIC_AND; else if(Com_MatchToken("||")) expression.op = OP_LOGIC_OR; else if(Com_MatchToken("==")) expression.op = OP_EQUAL; else if(Com_MatchToken("!=")) expression.op = OP_NOTEQUAL; else if(Com_MatchToken(">")) expression.op = OP_MORE; else if(Com_MatchToken(">=")) expression.op = OP_MORE_OR_EQUAL; else if(Com_MatchToken("<")) expression.op = OP_SMALLER; else if(Com_MatchToken("<=")) expression.op = OP_SMALLER_OR_EQUAL; else if(Com_MatchToken("&")) expression.op = OP_WITH; else { int val; if(CG_GetInteger( com_token[0] == '!' ? com_token + 1 : com_token, &val )) { if(com_token[0] == '!') expression.val[j] = !val; else expression.val[j] = val; if(expression.op == OP_UNKNOWN) expression.op = OP_NOTEQUAL; // may be overrided j++; // force to done } else MsgDev(D_ERROR, "CG_ParseExpression: unknown variable %s\n", com_token ); } } // exec expression now switch(expression.op) { case OP_LOGIC_OR: if(expression.val[0] || expression.val[1]) result++; else result--; break; case OP_LOGIC_AND: if(expression.val[0] && expression.val[1]) result++; else result--; break; case OP_EQUAL: if(expression.val[0] == expression.val[1]) result++; else result--; break; case OP_NOTEQUAL: if(expression.val[0] != expression.val[1]) result++; else result--; break; case OP_MORE: if(expression.val[0] > expression.val[1]) result++; else result--; break; case OP_MORE_OR_EQUAL: if(expression.val[0] >= expression.val[1]) result++; else result--; break; case OP_SMALLER: if(expression.val[0] < expression.val[1]) result++; else result--; break; case OP_SMALLER_OR_EQUAL: if(expression.val[0] <= expression.val[1]) result++; else result--; break; case OP_WITH: if(expression.val[0] & expression.val[1]) result++; else result--; break; } return result; } bool CG_ExecBuiltins( void ) { int value = 0; // builtins if(Com_MatchToken("{")) { cls.cg_depth++; return false; } else if(Com_MatchToken("}")) { cls.cg_depth--; return false; } else if(Com_MatchToken("if")) { // parse expression if(CG_ParseExpression() == -1) { CG_SkipBlock(); } return false; } else if(Com_MatchToken("SetStatAlias")) { // set alias name for stats numbers if(!CG_ParseArgs( 2 )) return false; CG_SetStatsAlias(cls.cg_argv[0], atoi(cls.cg_argv[1])); } else if(Com_MatchToken("SetCvarAlias")) { // set alias name for stats numbers if(!CG_ParseArgs( 2 )) return false; CG_SetCvarAlias(cls.cg_argv[0], cls.cg_argv[1]); } else if(Com_MatchToken("LoadPic")) { // cache hud pics if(!CG_ParseArgs( 1 )) return false; re->RegisterPic(cls.cg_argv[0]); } else if(Com_MatchToken("DrawField")) { // displayed health, armor, e.t.c. if(!CG_ParseArgs( 3 )) return false; if(!CG_GetInteger(cls.cg_argv[0], &value)) MsgDev(D_ERROR, "%s: can't use undefined alias %s\n", cls.cg_builtin, cls.cg_argv[0]); else CG_DrawField( value, atoi(cls.cg_argv[1]), atoi(cls.cg_argv[2])); } else if(Com_MatchToken("SetColor")) { // set custom color if(!CG_ParseArgs( 4 )) return false; CG_SetColor(atof(cls.cg_argv[0]), atof(cls.cg_argv[1]), atof(cls.cg_argv[2]), atof(cls.cg_argv[3])); } else if(Com_MatchToken("ResetColor")) { // reset custom color if(!CG_ParseArgs( 0 )) return false; CG_ResetColor(); } else if(Com_MatchToken("SetScale")) { // set custom scale if(!CG_ParseArgs( 2 )) return false; CG_SetScale(atof(cls.cg_argv[0]), atof(cls.cg_argv[1])); } else if(Com_MatchToken("ResetScale")) { // reset custom scale if(!CG_ParseArgs( 0 )) return false; CG_ResetScale(); } else if(Com_MatchToken("DrawPic")) { // draw named pic if(!CG_ParseArgs( 5 )) return false; SCR_DrawPic( atoi(cls.cg_argv[1]), atoi(cls.cg_argv[2]), atoi(cls.cg_argv[3]), atoi(cls.cg_argv[4]), CG_ImageIndex(cls.cg_argv[0])); } else if(Com_MatchToken("DrawCenterPic")) { // draw named pic if(!CG_ParseArgs( 3 )) return false; CG_DrawCenterPic( atoi(cls.cg_argv[1]), atoi(cls.cg_argv[2]), CG_ImageIndex(cls.cg_argv[0])); } else if(Com_MatchToken("DrawBarImage")) { // fill image with "common/fill_rect" if(!CG_ParseArgs( 6 )) return false; if(!CG_GetInteger(cls.cg_argv[0], &value)) MsgDev(D_ERROR, "%s: can't use undefined alias %s\n", cls.cg_builtin, cls.cg_argv[0]); else CG_DrawBarImage((float)value, CG_ImageIndex(cls.cg_argv[1]), atoi(cls.cg_argv[2]), atoi(cls.cg_argv[3]), atoi(cls.cg_argv[4]), atoi(cls.cg_argv[5])); } else if(Com_MatchToken("DrawCenterBarImage")) { // fill image with "common/fill_rect" if(!CG_ParseArgs( 4 )) return false; if(!CG_GetInteger(cls.cg_argv[0], &value)) MsgDev(D_ERROR, "%s: can't use undefined alias %s\n", cls.cg_builtin, cls.cg_argv[0]); else CG_DrawCenterBarImage( (float)value, CG_ImageIndex(cls.cg_argv[1]), atoi(cls.cg_argv[2]), atoi(cls.cg_argv[3])); } else if(Com_MatchToken("DrawBarGeneric")) { // draw progress bar if(!CG_ParseArgs( 5 )) return false; if(!CG_GetInteger(cls.cg_argv[0], &value)) MsgDev(D_ERROR, "%s: can't use undefined alias %s\n", cls.cg_builtin, cls.cg_argv[0]); else CG_DrawBarGeneric( (float)value, atoi(cls.cg_argv[1]), atoi(cls.cg_argv[2]), atoi(cls.cg_argv[3]), atoi(cls.cg_argv[4])); } else if(Com_MatchToken("DrawCenterBarGeneric")) { // draw progress bar if(!CG_ParseArgs( 3 )) return false; if(!CG_GetInteger(cls.cg_argv[0], &value)) MsgDev(D_ERROR, "%s: can't use undefined alias %s\n", cls.cg_builtin, cls.cg_argv[0]); else CG_DrawCenterBarGeneric( (float)value, atoi(cls.cg_argv[1]), atoi(cls.cg_argv[2])); } else if(Com_MatchToken("DrawString")) { // draw named pic if(!CG_ParseArgs( 3 )) return false; SCR_DrawBigString( atoi(cls.cg_argv[1]), atoi(cls.cg_argv[2]), CG_ImageIndex(cls.cg_argv[0]), 1.0f ); } else if(Com_MatchToken("DrawCenterString")) { // draw named pic if(!CG_ParseArgs( 1 )) return false; CG_DrawCenterStringBig(CG_ImageIndex(cls.cg_argv[0]), 1.0f ); } else if(Com_MatchToken("(") || Com_MatchToken(")")); else { MsgDev(D_WARN, "%s: can't exec function %s\n", cls.cg_builtin, com_token ); while(Com_TryToken()); } return true; } /* ================ CG_ExecuteProgram Hudprogram executor ================ */ void CG_ExecuteProgram( char *section ) { bool skip = true; Com_ResetScript(); com.strncpy( cls.cg_function, section, MAX_QPATH ); cls.cg_depth = 0; // not loaded if(!cls.cg_init) return; // section not specified if(!section || !*section ) return; while(Com_GetToken( true )) { // first, we must find start of section if(Com_MatchToken("void")) { Com_GetToken( false ); // get section name if(Com_MatchToken( section )) skip = false; CG_ParseArgs( 1 ); // go to end of line } if(skip) continue; if(Com_MatchToken("return")) { break; } else if(!CG_ExecBuiltins()) { // end of section if(!cls.cg_depth) break; continue; } } CG_ResetColor(); // don't forget reset color }