/* * Warsow hud functions */ #include "cg_local.h" extern cvar_t *cg_weaponlist; extern cvar_t *cg_showWeaponSelect; extern cvar_t *cg_debug_HUD; extern cvar_t *cg_clientHUD; //============================================================================= typedef struct { char *name; int value; } constant_integer_t; static const constant_integer_t cg_integer_constants[] = { { "NOTSET", STAT_NOTSET }, // teams { "TEAM_SPECTATOR", TEAM_SPECTATOR }, { "TEAM_PLAYERS", TEAM_PLAYERS }, { "TEAM_RED", TEAM_RED }, { "TEAM_BLUE", TEAM_BLUE }, { "TEAM_GREEN", TEAM_GREEN }, { "TEAM_YELLOW", TEAM_YELLOW }, // align { "LEFT", 1 }, { "CENTER", 2 }, { "RIGHT", 3 }, { "TOP", 1 }, { "MIDDLE", 2 }, { "BOTTOM", 3 }, { "WIDTH", 800 }, { "HEIGHT", 600 }, // gametypes { "GAMETYPE_DM", GAMETYPE_DM}, { "GAMETYPE_DUEL", GAMETYPE_DUEL}, { "GAMETYPE_TDM", GAMETYPE_TDM}, { "GAMETYPE_CTF", GAMETYPE_CTF}, { "GAMETYPE_RACE", GAMETYPE_RACE}, { "GAMETYPE_MIDAIR", GAMETYPE_MIDAIR}, // ctf { "FLAG_SAFE", FLAG_STATUS_SAFE }, { "FLAG_STOLEN", FLAG_STATUS_STOLEN }, { "FLAG_DROPPED", FLAG_STATUS_DROPPED }, { NULL, 0 } }; #if 0 // not used yet atleast typedef struct { char *name; char *value; } constant_string_t; static const constant_string_t cg_string_constants[] = { { NULL, 0 } }; #endif //============================================================================= int cgWeaponListData[WEAP_TOTAL][3]; void SCR_UpdateWeaponListData( void ) { int i; memset( &cgWeaponListData, 0, sizeof(cgWeaponListData) ); for( i = 0; (i < WEAP_TOTAL-1) && (i < MAX_WEAPLIST_STATS); i++ ) { cgWeaponListData[i+1][0] = cg.frame.playerState.weaponlist[i][0]; cgWeaponListData[i+1][1] = cg.frame.playerState.weaponlist[i][1]; cgWeaponListData[i+1][2] = cg.frame.playerState.weaponlist[i][2]; } } //================ //SCR_DrawWeaponList //================ void SCR_DrawWeaponList( int x, int y, int ix, int iy, int wx, int wy, int sx, int sy ) { int width; int xi[WEAP_TOTAL], yi[WEAP_TOTAL], xw[WEAP_TOTAL], yw[WEAP_TOTAL], xs[WEAP_TOTAL], ys[WEAP_TOTAL]; int xmin, xmax, ymin, ymax; int i, j; vec4_t color; qboolean selected_weapon; if ( !cg_weaponlist || !cg_weaponlist->integer ) return; SCR_UpdateWeaponListData(); //jalfixme: remove me xi[0] = yi[0] = xmin = ymin = xmax = ymax = 0; xw[0] = xi[0] + wx; yw[0] = yi[0] + wy; xs[0] = xi[0] + sx; ys[0] = yi[0] + sy; if ( xmin > xw[0] ) xmin = xw[0]; if ( xmin > xs[0] ) xmin = xs[0]; if ( ymin > yw[0] ) ymin = yw[0]; if ( ymin > ys[0] ) ymin = ys[0]; if ( xmax < xi[0] + 32 ) xmax = xi[0] + 32; if ( xmax < xw[0] + 32 ) xmax = xw[0] + 32; if ( xmax < xs[0] + 32 ) xmax = xs[0] + 32; if ( ymax < yi[0] + 32 ) ymax = yi[0] + 32; if ( ymax < yw[0] + 16 ) ymax = yw[0] + 16; if ( ymax < ys[0] + 16 ) ymax = ys[0] + 16; j = 1; for( i = WEAP_GUNBLADE ; i < WEAP_TOTAL ; i++ ) { // if player doesnt have this weapon, skip it if ( !(cgWeaponListData[i][0] || cgWeaponListData[i][1] || cgWeaponListData[i][2]) ) { j++; continue; } xi[i] = xi[i-j] + ix; yi[i] = yi[i-j] + iy; xw[i] = xi[i] + wx; yw[i] = yi[i] + wy; xs[i] = xi[i] + sx; ys[i] = yi[i] + sy; if ( xmin > xi[i] ) xmin = xi[i]; if ( xmin > xw[i] ) xmin = xw[i]; if ( xmin > xs[i] ) xmin = xs[i]; if ( ymin > yi[i] ) ymin = yi[i]; if ( ymin > yw[i] ) ymin = yw[i]; if ( ymin > ys[i] ) ymin = ys[i]; if ( xmax < xi[i] + 32 ) xmax = xi[i] + 32; if ( xmax < xw[i] + 32 ) xmax = xw[i] + 32; if ( xmax < xs[i] + 32 ) xmax = xs[i] + 32; if ( ymax < yi[i] + 32 ) ymax = yi[i] + 32; if ( ymax < yw[i] + 16 ) ymax = yw[i] + 16; if ( ymax < ys[i] + 16 ) ymax = ys[i] + 16; j = 1; } VectorCopy( colorWhite, color ); for( i = WEAP_GUNBLADE ; i < WEAP_TOTAL ; i++ ) { // if player doesnt have this weapon, skip it if( !(cgWeaponListData[i][0] || cgWeaponListData[i][1] || cgWeaponListData[i][2]) ) continue; if( cg.demoPlaying && cg.frame.playerState.pmove.pm_type == PM_CHASECAM ) { if( i == cg.frame.playerState.stats[STAT_SELECTED_ITEM] ) selected_weapon = qtrue; else selected_weapon = qfalse; } else { if( (cg.latched_weapon != WEAP_NONE && i == cg.latched_weapon) || (cg.latched_weapon == WEAP_NONE && i == cg.frame.playerState.stats[STAT_SELECTED_ITEM]) ) selected_weapon = qtrue; else selected_weapon = qfalse; } if( selected_weapon ) color[3] = 1.0; else color[3] = 0.5; if( cgWeaponListData[i][0] ) { // wsw : pb : display a little box around selected weapon in weaponlist (cg_showWeaponSelect) if( selected_weapon && cg_showWeaponSelect->integer ) { // selected weapon trap_R_DrawStretchPic( (int)x+xi[i]+(xmin-xmax)/2-xmin, (int)y+yi[i]+(ymin-ymax)/2-ymin, (int)(32 * cgs.vidWidth/800 ), (int)(32 * cgs.vidHeight/600 ), 0, 0, 1, 1, color, CG_MediaShader( cgs.media.shaderSelect ) ); } trap_R_DrawStretchPic ( (int)x+xi[i]+(xmin-xmax)/2-xmin, (int)y+yi[i]+(ymin-ymax)/2-ymin, (int)(32 * cgs.vidWidth/800 ), (int)(32 * cgs.vidHeight/600 ), 0, 0, 1, 1, color, CG_MediaShader( cgs.media.shaderWeaponIcon[i-1] ) ); } else { trap_R_DrawStretchPic( (int)x+xi[i]+(xmin-xmax)/2-xmin, (int)y+yi[i]+(ymin-ymax)/2-ymin, (int)(32 * cgs.vidWidth/800 ), (int)(32 * cgs.vidHeight/600 ), 0, 0, 1, 1, color, CG_MediaShader( cgs.media.shaderNoGunWeaponIcon[i-1] ) ); } width = cgWeaponListData[i][2] >= 100 ? 3 : 2; CG_DrawHUDField( x+xw[i]+(xmin-xmax)/2-xmin, y+yw[i]+(ymin-ymax)/2-ymin, ALIGN_LEFT_TOP, color, 12, width, cgWeaponListData[i][2] ); if( cgWeaponListData[i][1] > 0 ) { width = cgWeaponListData[i][1] >= 100 ? 3 : 2; CG_DrawHUDField( x+xs[i]+(xmin-xmax)/2-xmin, y+ys[i]+(ymin-ymax)/2-ymin, ALIGN_LEFT_TOP, color, 12, width, cgWeaponListData[i][1] ); } } } //============================================================================= // STATUS BAR PROGRAMS //============================================================================= // we will always operate with floats so we don't have to code 2 different numeric paths // it's not like using float or ints would make a difference in this simple-scripting case. static float CG_OpFuncAdd( const float a, const float b ) { return a + b; } static float CG_OpFuncSubtract( const float a, const float b ) { return a - b; } static float CG_OpFuncMultiply( const float a, const float b ) { return a * b; } static float CG_OpFuncDivide( const float a, const float b ) { return a / b; } static float CG_OpFuncAND( const float a, const float b ) { return (int)a & (int)b; } static float CG_OpFuncOR( const float a, const float b ) { return (int)a | (int)b; } static float CG_OpFuncXOR( const float a, const float b ) { return (int)a ^ (int)b; } static float CG_OpFuncCompareEqual( const float a, const float b ) { return (a == b); } static float CG_OpFuncCompareNotEqual( const float a, const float b ) { return (a != b); } static float CG_OpFuncCompareGreater( const float a, const float b ) { return (a > b); } static float CG_OpFuncCompareGreaterOrEqual( const float a, const float b ) { return (a >= b); } float CG_OpFuncCompareSmaller( const float a, const float b ) { return (a < b); } static float CG_OpFuncCompareSmallerOrEqual( const float a, const float b ) { return (a <= b); } typedef struct cg_layoutoperators_s { char *name; float (*opFunc)( const float a, const float b ); }cg_layoutoperators_t; static cg_layoutoperators_t cg_LayoutOperators[] = { { "+", CG_OpFuncAdd }, { "-", CG_OpFuncSubtract }, { "*", CG_OpFuncMultiply }, { "/", CG_OpFuncDivide }, { "&", CG_OpFuncAND }, { "|", CG_OpFuncOR }, { "^", CG_OpFuncXOR }, { "==", CG_OpFuncCompareEqual }, { "!=", CG_OpFuncCompareNotEqual }, { ">", CG_OpFuncCompareGreater }, { ">=", CG_OpFuncCompareGreaterOrEqual }, { "<", CG_OpFuncCompareSmaller }, { "<=", CG_OpFuncCompareSmallerOrEqual }, { NULL, NULL }, }; //================ //CG_OperatorFuncForArgument //================ void *CG_OperatorFuncForArgument( char *token ) { cg_layoutoperators_t *op; while( *token == ' ' ) token++; for( op = cg_LayoutOperators; op->name; op++ ) { if( !Q_stricmp( token, op->name ) ) return op->opFunc; } return NULL; } //============================================================================= static char *CG_GetStringArg( struct cg_layoutnode_s **argumentsnode ); static float CG_GetNumericArg( struct cg_layoutnode_s **argumentsnode ); //============================================================================= static int layout_cursor_x = 400; static int layout_cursor_y = 300; static int layout_cursor_width = 100; static int layout_cursor_height = 100; static int layout_cursor_align = ALIGN_LEFT_TOP; static vec4_t layout_cursor_color = { 1, 1, 1, 1 }; static vec4_t layout_cursor_flashcolor = { 1, 1, 1, 1 }; static vec3_t layout_cursor_rotation = { 0, 0, 0 }; static struct mufont_s *layout_cursor_font; qboolean layout_cursor_flash = qfalse; enum { LNODE_NUMERIC, LNODE_STAT, LNODE_STRING, LNODE_COMMAND }; //============================================================================= // Commands' Functions //============================================================================= static int CG_LFuncDrawRaceTimer( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { char time[64]; int min, sec, milli; milli = (int)CG_GetNumericArg( &argumentnode ); if( milli == STAT_NOTSET ) return qfalse; // stat is in milliseconds/100.0f min=milli/600; milli-=min*600; sec=milli/10; milli-=sec*10; // we want MM:SS:m Q_snprintfz( time, sizeof(time), "%02d:%02d.%1d", min, sec, milli ); trap_SCR_DrawString( layout_cursor_x, layout_cursor_y, layout_cursor_align, time, layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } static int CG_LFuncDrawPicByIndex( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int value = (int)CG_GetNumericArg( &argumentnode ); int x, y; if( value >= 0 && value < MAX_IMAGES ) { if( cgs.configStrings[CS_IMAGES + value][0] ) { x = CG_HorizontalAlignForWidth( layout_cursor_x, layout_cursor_align, layout_cursor_width ); y = CG_VerticalAlignForHeight( layout_cursor_y, layout_cursor_align, layout_cursor_height ); trap_R_DrawStretchPic( x, y, layout_cursor_width, layout_cursor_height, 0, 0, 1, 1, layout_cursor_color, trap_R_RegisterPic(cgs.configStrings[CS_IMAGES+value]) ); return qtrue; } } return qfalse; } static int CG_LFuncDrawPicByItemIndex( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int itemindex = (int)CG_GetNumericArg( &argumentnode ); int x, y; gitem_t *item; item = GS_FindItemByTag( itemindex ); if( !item ) return qfalse; x = CG_HorizontalAlignForWidth( layout_cursor_x, layout_cursor_align, layout_cursor_width ); y = CG_VerticalAlignForHeight( layout_cursor_y, layout_cursor_align, layout_cursor_height ); trap_R_DrawStretchPic( x, y, layout_cursor_width, layout_cursor_height, 0, 0, 1, 1, layout_cursor_color, trap_R_RegisterPic(item->icon) ); return qtrue; } static int CG_LFuncDrawPicByItemName( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int x, y; gitem_t *item = GS_FindItemByName( CG_GetStringArg(&argumentnode) ); if( !item ) return qfalse; x = CG_HorizontalAlignForWidth( layout_cursor_x, layout_cursor_align, layout_cursor_width ); y = CG_VerticalAlignForHeight( layout_cursor_y, layout_cursor_align, layout_cursor_height ); trap_R_DrawStretchPic( x, y, layout_cursor_width, layout_cursor_height, 0, 0, 1, 1, layout_cursor_color, trap_R_RegisterPic(item->icon) ); return qtrue; } static int CG_LFuncDrawPicByName( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int x, y; x = CG_HorizontalAlignForWidth( layout_cursor_x, layout_cursor_align, layout_cursor_width ); y = CG_VerticalAlignForHeight( layout_cursor_y, layout_cursor_align, layout_cursor_height ); trap_R_DrawStretchPic( x, y, layout_cursor_width, layout_cursor_height, 0, 0, 1, 1, layout_cursor_color, trap_R_RegisterPic( CG_GetStringArg(&argumentnode) ) ); return qtrue; } static int CG_LFuncDrawModelByIndex( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { struct model_s *model; int value = (int)CG_GetNumericArg( &argumentnode ); if( value >= 0 && value < MAX_MODELS ) { model = value > 1 ? CG_RegisterModel( cgs.configStrings[CS_MODELS+value] ) : NULL; CG_DrawHUDModel( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_width, layout_cursor_height, model, NULL, layout_cursor_rotation[YAW] ); return qtrue; } return qfalse; } static int CG_LFuncDrawModelByName( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { struct model_s *model; struct shader_s *shader; char *shadername; model = CG_RegisterModel( CG_GetStringArg(&argumentnode) ); shadername = CG_GetStringArg( &argumentnode ); shader = Q_stricmp( shadername, "NULL" ) ? trap_R_RegisterPic( shadername ) : NULL; CG_DrawHUDModel( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_width, layout_cursor_height, model, shader, layout_cursor_rotation[YAW] ); return qtrue; } static int CG_LFuncDrawModelByItemIndex( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int i; gitem_t *item; struct model_s *model; int itemindex = (int)CG_GetNumericArg( &argumentnode ); item = GS_FindItemByTag( itemindex ); if( !item ) return qfalse; for( i = 0; i < MAX_ITEM_MODELS; i++ ) { if( item->world_model[i] != NULL ) { model = itemindex >= 1 ? CG_RegisterModel( item->world_model[i] ) : NULL; CG_DrawHUDModel( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_width, layout_cursor_height, model, NULL, layout_cursor_rotation[YAW] ); } } return qtrue; } static int CG_LFuncDrawModelByItemName( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int x, y, i; struct model_s *model; gitem_t *item = GS_FindItemByName( CG_GetStringArg(&argumentnode) ); if( !item ) return qfalse; x = CG_HorizontalAlignForWidth( layout_cursor_x, layout_cursor_align, layout_cursor_width ); y = CG_VerticalAlignForHeight( layout_cursor_y, layout_cursor_align, layout_cursor_height ); for( i = 0; i < MAX_ITEM_MODELS; i++ ) { model = item->world_model[i] ? CG_RegisterModel( item->world_model[i] ) : NULL; CG_DrawHUDModel( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_width, layout_cursor_height, model, NULL, layout_cursor_rotation[YAW] ); } return qtrue; } static int CG_LFuncCursor( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { layout_cursor_x = (int)(CG_GetNumericArg( &argumentnode )*cgs.vidWidth/800); layout_cursor_y = (int)(CG_GetNumericArg( &argumentnode )*cgs.vidHeight/600); return qtrue; } static int CG_LFuncSize( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { layout_cursor_width = (int)(CG_GetNumericArg( &argumentnode )*cgs.vidWidth/800); layout_cursor_height = (int)(CG_GetNumericArg( &argumentnode )*cgs.vidHeight/600); return qtrue; } static int CG_LFuncColor( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int i; for( i = 0; i < 4; i++ ) { layout_cursor_color[i] = CG_GetNumericArg( &argumentnode ); clamp( layout_cursor_color[i], 0, 1 ); } return qtrue; } static int CG_LFuncFlashColor( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int i; for( i = 0; i < 4; i++ ) { layout_cursor_flashcolor[i] = CG_GetNumericArg( &argumentnode ); clamp( layout_cursor_flashcolor[i], 0, 1 ); } return qtrue; } static int CG_LFuncColorToTeamColor( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { GS_TeamColor( CG_GetNumericArg( &argumentnode ), layout_cursor_color ); return qtrue; } static int CG_LFuncFlashColorToTeamColor( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { GS_TeamColor( CG_GetNumericArg( &argumentnode ), layout_cursor_flashcolor ); return qtrue; } static int CG_LFuncColorAlpha( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { layout_cursor_color[3] = CG_GetNumericArg( &argumentnode ); return qtrue; } static int CG_LFuncFlashColorAlpha( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { layout_cursor_flashcolor[3] = CG_GetNumericArg( &argumentnode ); return qtrue; } static int CG_LFuncRotationSpeed( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int i; for( i = 0; i < 3; i++ ) { layout_cursor_rotation[i] = CG_GetNumericArg( &argumentnode ); clamp( layout_cursor_rotation[i], 0, 999 ); } return qtrue; } static int CG_LFuncAlign( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int v, h; h = (int)CG_GetNumericArg( &argumentnode ); v = (int)CG_GetNumericArg( &argumentnode ); if( h < 1 ) h = 1; if( v < 1 ) v = 1; layout_cursor_align = (h-1)+(3*(v-1)); return qtrue; } static int CG_LFuncFont( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { struct mufont_s *font; char *fontname = CG_GetStringArg(&argumentnode); if( !Q_stricmp(fontname, "con_fontsystemsmall") ) { font = cgs.fontSystemSmall; } else if( !Q_stricmp(fontname, "con_fontsystemmedium") ) { font = cgs.fontSystemMedium; } else if( !Q_stricmp(fontname, "con_fontsystembig") ) { font = cgs.fontSystemBig; } else { font = trap_SCR_RegisterFont( fontname ); } if( font ) { layout_cursor_font = font; return qtrue; } return qfalse; } void CG_DrawFPS( int x, int y, int align, struct mufont_s *font, vec4_t color ); static int CG_LFuncDrawFPS( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { CG_DrawFPS( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_font, layout_cursor_color ); return qtrue; } void CG_DrawSpeed( int x, int y, int align, struct mufont_s *font, vec4_t color ); static int CG_LFuncDrawSpeed( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { CG_DrawSpeed( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_font, layout_cursor_color ); return qtrue; } void CG_DrawClock( int x, int y, int align, struct mufont_s *font, vec4_t color ); static int CG_LFuncDrawClock( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { CG_DrawClock( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } static int CG_LFuncDrawClockText( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { char *message = NULL; qboolean abbr = (CG_GetNumericArg(&argumentnode) != 0); if( cg.frame.match.state == MATCH_STATE_WARMUP ) { message = (abbr ? "WA" : "Warmup"); } else if( cg.frame.match.state == MATCH_STATE_COUNTDOWN ) { message = (abbr ? "CD" : "Countdown"); } else if( cg.frame.match.state == MATCH_STATE_PLAYTIME ) { if( cg.frame.match.extendedtime && cg.frame.match.timelimit ) message = (abbr ? "OT" : "Overtime"); else if( cg.frame.match.extendedtime && !cg.frame.match.timelimit ) message = (abbr ? "SD" : "Suddendeath"); } if( message ) { trap_SCR_DrawString( layout_cursor_x, layout_cursor_y, layout_cursor_align, message, layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); } return qtrue; } static int CG_LFuncDrawHelpMessage( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { // hide this one when scoreboard is up if( !(cg.frame.playerState.stats[STAT_LAYOUTS] & STAT_LAYOUT_SCOREBOARD) ) { if( cg_showhelp->integer && !cg.demoPlaying ) { char *helpmessage = cg.matchmessage; int y = layout_cursor_y; size_t len; while( (len = trap_SCR_StrlenForWidth( helpmessage, layout_cursor_font, layout_cursor_width )) ) { trap_SCR_DrawStringLen( layout_cursor_x, y, layout_cursor_align, helpmessage, len, layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); helpmessage += len; if( helpmessage[0] == '\n' ) { y += trap_SCR_strHeight( layout_cursor_font ); helpmessage++; } } } } return qtrue; } void CG_DrawPointed( int x, int y, int align, struct mufont_s *font, vec4_t color ); static int CG_LFuncDrawPointed( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { CG_DrawPointed( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_font, layout_cursor_color ); return qtrue; } static int CG_LFuncDrawString( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { char *string = CG_GetStringArg(&argumentnode); if( !string || !string[0] ) return qfalse; trap_SCR_DrawString( layout_cursor_x, layout_cursor_y, layout_cursor_align, string, layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } static int CG_LFuncDrawItemNameFromIndex( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { gitem_t *item; int itemindex = CG_GetNumericArg(&argumentnode); item = GS_FindItemByTag( itemindex ); if( !item || !item->pickup_name ) return qfalse; trap_SCR_DrawString( layout_cursor_x, layout_cursor_y, layout_cursor_align, item->pickup_name, layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } static int CG_LFuncDrawConfigstring( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int index = (int)CG_GetNumericArg( &argumentnode ); if( index < 0 || index >= MAX_CONFIGSTRINGS ) { CG_Printf( "WARNING 'CG_LFuncDrawConfigstring' Bad stat_string index" ); return qfalse; } trap_SCR_DrawString( layout_cursor_x, layout_cursor_y, layout_cursor_align, cgs.configStrings[index], layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } static int CG_LFuncDrawPlayername( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int index = (int)CG_GetNumericArg( &argumentnode ) - 1; if( index >= 0 && index < MAX_CLIENTS && cgs.clientInfo[index].name ) { trap_SCR_DrawString( layout_cursor_x, layout_cursor_y, layout_cursor_align, cgs.clientInfo[index].name, layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } return qfalse; } void CG_DrawHUDNumeric( int x, int y, int align, float *color, int charwidth, int charheight, int value ); static int CG_LFuncDrawNumeric( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int value = (int)CG_GetNumericArg( &argumentnode ); CG_DrawHUDNumeric( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color, layout_cursor_width, layout_cursor_height, value ); return qtrue; } static int CG_LFuncDrawStretchNum( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { static char num[16]; int len; int value = (int)CG_GetNumericArg( &argumentnode ); Q_snprintfz( num, sizeof(num), "%i", value ); len = strlen( num ); if( len * layout_cursor_height <= layout_cursor_width ) { CG_DrawHUDNumeric( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color, layout_cursor_height, layout_cursor_height, value ); } else { //stretch numbers CG_DrawHUDNumeric( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color, layout_cursor_width / len, layout_cursor_height, value ); } return qtrue; } static int CG_LFuncDrawNumeric2( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int value = (int)CG_GetNumericArg( &argumentnode ); trap_SCR_DrawString( layout_cursor_x, layout_cursor_y, layout_cursor_align, va("%i", value), layout_cursor_font, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } static int CG_LFuncDrawBar( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int value = (int)CG_GetNumericArg( &argumentnode ); int maxvalue = (int)CG_GetNumericArg( &argumentnode ); CG_DrawHUDRect( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_width, layout_cursor_height, value, maxvalue, layout_cursor_flash ? layout_cursor_flashcolor : layout_cursor_color ); return qtrue; } static int CG_LFuncDrawWeaponList( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { int ix, iy, wx, wy, sx, sy; // TODO: align support ix = (int)( CG_GetNumericArg(&argumentnode) * cgs.vidWidth/800 ); iy = (int)( CG_GetNumericArg(&argumentnode) * cgs.vidHeight/600 ); wx = (int)( CG_GetNumericArg(&argumentnode) * cgs.vidWidth/800 ); wy = (int)( CG_GetNumericArg(&argumentnode) * cgs.vidHeight/600 ); sx = (int)( CG_GetNumericArg(&argumentnode) * cgs.vidWidth/800 ); sy = (int)( CG_GetNumericArg(&argumentnode) * cgs.vidHeight/600 ); SCR_DrawWeaponList( layout_cursor_x, layout_cursor_y, ix, iy, wx, wy, sx, sy ); return qtrue; } void SCR_DrawTeamInfo( int x, int y, int align, struct mufont_s *font, vec4_t color ); static int CG_LFuncDrawTeamInfo( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { SCR_DrawTeamInfo( layout_cursor_x, layout_cursor_y, layout_cursor_align, layout_cursor_font, layout_cursor_color ); return qtrue; } static int CG_LFuncDrawCrossHair( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { SCR_DrawCrosshair( layout_cursor_x, layout_cursor_y, layout_cursor_align ); return qtrue; } void SCR_DrawNet( int x, int y, int w, int h, int align, vec4_t color ); static int CG_LFuncDrawNet( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { SCR_DrawNet( layout_cursor_x, layout_cursor_y, layout_cursor_width, layout_cursor_height, layout_cursor_align, layout_cursor_color ); return qtrue; } static int CG_LFuncIf( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ) { return (int)CG_GetNumericArg(&argumentnode); } typedef struct cg_layoutcommand_s { char *name; int (*func)( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ); int numparms; char *help; }cg_layoutcommand_t; static cg_layoutcommand_t cg_LayoutCommands[] = { { "setCursor", CG_LFuncCursor, 2, "Moves the cursor to x and y coordinates." }, { "setAlign", CG_LFuncAlign, 2, "Changes align setting. Parameters: horizontal alignement, vertical alignement" }, { "setSize", CG_LFuncSize, 2, "Sets width and height. Used for pictures and models." }, { "setFont", CG_LFuncFont, 1, "Sets font by font name. Accepts 'system_small' and 'system_medium' as shortcut to default game fonts." }, { "setColor", CG_LFuncColor, 4, "Sets color setting in RGBA mode. Used for text and pictures" }, { "setFlashColor", CG_LFuncFlashColor, 4, "Sets flash color setting in RGBA mode. Used for text and pictures when they flash" }, { "setColorToTeamColor", CG_LFuncColorToTeamColor, 1, "Sets cursor color to the color of the team provided in the argument" }, { "setFlashColorToTeamColor", CG_LFuncFlashColorToTeamColor, 1, "Sets cursor flash-color to the color of the team provided in the argument" }, { "setColorAlpha", CG_LFuncColorAlpha, 1, "Changes the alpha value of the current color" }, { "setFlashColorAlpha", CG_LFuncFlashColorAlpha, 1, "Changes the alpha value of the current flash-color" }, { "setRotationSpeed", CG_LFuncRotationSpeed, 3, "Sets rotation speeds as vector. Used for models" }, { "drawFPS", CG_LFuncDrawFPS, 0, "Draws renderer frames per second" }, { "drawSpeed", CG_LFuncDrawSpeed, 0, "Draws current player speed" }, { "drawClock", CG_LFuncDrawClock, 0, "Draws clock" }, { "drawClockText", CG_LFuncDrawClockText, 1, "Draws text associated with current clock time" }, { "drawHelpString", CG_LFuncDrawHelpMessage, 0, "Draws the help message" }, { "drawPlayername", CG_LFuncDrawPlayername, 1, "Draws the name of the player with id provided by the argument" }, { "drawPointing", CG_LFuncDrawPointed, 0, "Draws the name of the player in the crosshair" }, { "drawStatString", CG_LFuncDrawConfigstring, 1, "Draws configstring of argument id" }, { "drawItemName", CG_LFuncDrawItemNameFromIndex, 1, "Draws the name of the item with given item index" }, { "drawString", CG_LFuncDrawString, 1, "Draws the string in the argument" }, { "drawStringNum", CG_LFuncDrawNumeric2, 1, "Draws numbers as text" }, { "drawNum", CG_LFuncDrawNumeric, 1, "Draws numbers of given character size" }, { "drawStretchNum", CG_LFuncDrawStretchNum, 1, "Draws numbers stretch inside a given size" }, { "drawBar", CG_LFuncDrawBar, 2, "Draws a bar of size setting, the bar is filled in proportion to the arguments" }, { "drawCrosshair", CG_LFuncDrawCrossHair, 0, "Draws the game crosshair" }, { "drawNetIcon", CG_LFuncDrawNet, 0, "Draws the disconnection icon" }, { "drawPicByIndex", CG_LFuncDrawPicByIndex, 1, "Draws a pic with argument as imageIndex" }, { "drawPicByItemindex", CG_LFuncDrawPicByItemIndex, 1, "Draws a item icon pic with argument as itemIndex" }, { "drawPicByItemname", CG_LFuncDrawPicByItemName, 1, "Draws a item icon pic with argument being the item name" }, { "drawPicByName", CG_LFuncDrawPicByName, 1, "Draws a pic with argument being the file path" }, { "drawModelByIndex", CG_LFuncDrawModelByIndex, 1, "Draws a model with argument being the modelIndex" }, { "drawModelByName", CG_LFuncDrawModelByName, 2, "Draws a model with argument being the path to the model file" }, { "drawModelByItemindex", CG_LFuncDrawModelByItemIndex, 1, "Draws a item model with argument being the item index" }, { "drawModelByItemname", CG_LFuncDrawModelByItemName, 1, "Draws a model with argument being the item name" }, { "drawWeaponList", CG_LFuncDrawWeaponList, 6, "Draws the weapon list" }, { "drawTeamInfo", CG_LFuncDrawTeamInfo, 0, "Draws the Team Info (locations) box" }, { "if", CG_LFuncIf, 1, "Conditional expression. Argument accepts operations >, <, ==, >=, <=, etc" }, { "endif", NULL, 0, "End of conditional expression block" }, { "drawRaceTimer", CG_LFuncDrawRaceTimer, 1, "Draws a timer clock for the race gametype" }, { NULL, NULL, 0, NULL } }; void Cmd_CG_PrintHudHelp_f( void ) { cg_layoutcommand_t *cmd; cg_layoutoperators_t *op; int i; CG_Printf( "- %sHUD scripts commands\n---------------------------------%s\n", S_COLOR_YELLOW, S_COLOR_WHITE ); for( cmd = cg_LayoutCommands; cmd->name; cmd++ ) { CG_Printf( "- cmd: %s%s%s expected arguments: %s%i%s\n- desc: %s%s%s\n", S_COLOR_YELLOW, cmd->name, S_COLOR_WHITE, S_COLOR_YELLOW, cmd->numparms, S_COLOR_WHITE, S_COLOR_BLUE, cmd->help, S_COLOR_WHITE ); } CG_Printf( "\n" ); CG_Printf( "- %sHUD scripts operators\n---------------------------------%s\n", S_COLOR_YELLOW, S_COLOR_WHITE ); CG_Printf( "- " ); for( op = cg_LayoutOperators; op->name; op++ ) { CG_Printf( "%s%s%s, ", S_COLOR_YELLOW, op->name, S_COLOR_WHITE ); } CG_Printf( "\n\n" ); CG_Printf( "- %sHUD scripts STATS names\n---------------------------------%s\n", S_COLOR_YELLOW, S_COLOR_WHITE ); for( i = 0; gs_stat_names[i] != NULL; i++ ) { CG_Printf( "- %s%s%s\n", S_COLOR_YELLOW, gs_stat_names[i], S_COLOR_WHITE ); } CG_Printf( "\n" ); CG_Printf( "- %sHUD scripts CONSTANT names\n---------------------------------%s\n", S_COLOR_YELLOW, S_COLOR_WHITE ); for( i = 0; cg_integer_constants[i].name != NULL; i++ ) { CG_Printf( "%s%s%s, ", S_COLOR_YELLOW, cg_integer_constants[i].name, S_COLOR_WHITE ); } CG_Printf( "\n", S_COLOR_WHITE ); } //============================================================================= typedef struct cg_layoutnode_s { int (*func)( struct cg_layoutnode_s *commandnode, struct cg_layoutnode_s *argumentnode, int numArguments ); int type; char *string; int integer; float value; float (*opFunc)( const float a, float b ); struct cg_layoutnode_s *parent; struct cg_layoutnode_s *next; struct cg_layoutnode_s *ifthread; }cg_layoutnode_t; //================ //CG_GetStringArg //================ static char *CG_GetStringArg( struct cg_layoutnode_s **argumentsnode ) { struct cg_layoutnode_s *anode = *argumentsnode; if( !anode || anode->type == LNODE_COMMAND ) CG_Error( "'CG_LayoutGetIntegerArg': bad arg count" ); // we can return anything as string *argumentsnode = anode->next; return anode->string; } //================ //CG_GetNumericArg //can use recursion for mathematical operations //================ static float CG_GetNumericArg( struct cg_layoutnode_s **argumentsnode ) { struct cg_layoutnode_s *anode = *argumentsnode; float value; if( !anode || anode->type == LNODE_COMMAND ) CG_Error( "'CG_LayoutGetIntegerArg': bad arg count" ); if( anode->type != LNODE_NUMERIC && anode->type != LNODE_STAT ) CG_Printf( "WARNING: 'CG_LayoutGetIntegerArg': arg %s is not numeric", anode->string ); *argumentsnode = anode->next; if( anode->type == LNODE_STAT ) value = cg.frame.playerState.stats[anode->integer]; else value = anode->value; // recurse if there are operators if( anode->opFunc != NULL ) { value = anode->opFunc( value, CG_GetNumericArg(argumentsnode) ); } return value; } //================ //CG_LayoutParseCommandNode //alloc a new node for a command //================ static cg_layoutnode_t *CG_LayoutParseCommandNode( char *token ) { int i = 0; cg_layoutcommand_t *command = NULL; cg_layoutnode_t *node; for( i = 0; cg_LayoutCommands[i].name; i++ ) { if( !Q_stricmp(token, cg_LayoutCommands[i].name) ) { command = &cg_LayoutCommands[i]; break; } } if( command == NULL ) return NULL; node = CG_Malloc( sizeof(cg_layoutnode_t) ); node->type = LNODE_COMMAND; node->integer = command->numparms; node->value = 0.0f; node->string = CG_CopyString( command->name ); node->func = command->func; node->ifthread = NULL; return node; } //================ //CG_LayoutParseArgumentNode //alloc a new node for an argument //================ static cg_layoutnode_t *CG_LayoutParseArgumentNode( char *token ) { cg_layoutnode_t *node; int type = LNODE_NUMERIC; char *valuetok; static char tmpstring[8]; // find what's it if( !token ) return NULL; valuetok = token; if( token[0] == '%' ) { // it's a stat parm int i; type = LNODE_STAT; valuetok++; // skip % // replace stat names by values for( i = 0; gs_stat_names[i] != NULL; i++ ) { if( !Q_stricmp( valuetok, gs_stat_names[i] ) ) { Q_snprintfz( tmpstring, sizeof(tmpstring), "%i", i ); valuetok = tmpstring; break; } } } else if( token[0] == '#' ) { // it's a integer constant int i; type = LNODE_NUMERIC; valuetok++; // skip # // replace stat names by values for( i = 0; cg_integer_constants[i].name != NULL; i++ ) { if( !Q_stricmp( valuetok, cg_integer_constants[i].name ) ) { Q_snprintfz( tmpstring, sizeof(tmpstring), "%i", cg_integer_constants[i].value ); valuetok = tmpstring; break; } } if( cg_integer_constants[i].name == NULL ) valuetok = "0"; #if 0 // not used yet atleast } else if( token[0] == '$' ) { // it's a string constant int i; type = LNODE_STRING; valuetok++; // skip $ // replace stat names by values for( i = 0; cg_string_constants[i].name != NULL; i++ ) { if( !Q_stricmp( valuetok, cg_string_constants[i].name ) ) { Q_snprintfz( tmpstring, sizeof(tmpstring), "%s", cg_string_constants[i].value ); valuetok = tmpstring; break; } } #endif } else if( token[0] < '0' && token[0] > '9' && token[0] != '.' ) { type = LNODE_STRING; } // alloc node = CG_Malloc( sizeof(cg_layoutnode_t) ); node->type = type; node->integer = atoi( valuetok ); node->value = atof( valuetok ); node->string = CG_CopyString( token ); node->func = NULL; node->ifthread = NULL; // return it return node; } //================ //CG_LayoutCathegorizeToken //================ static int CG_LayoutCathegorizeToken( char *token ) { int i = 0; for( i = 0; cg_LayoutCommands[i].name; i++ ) { if( !Q_stricmp(token, cg_LayoutCommands[i].name) ) return LNODE_COMMAND; } if( token[0] == '%' ) { // it's a stat parm return LNODE_STAT; } else if( token[0] == '#' ) { // it's a numerical constant return LNODE_NUMERIC; #if 0 } else if( token[0] == '$' ) { // it's a string constant return LNODE_STRING; #endif } else if( token[0] < '0' && token[0] > '9' && token[0] != '.' ) { return LNODE_STRING; } return LNODE_NUMERIC; } //================ //CG_RecurseFreeLayoutThread //recursive for freeing "if" subtrees //================ static void CG_RecurseFreeLayoutThread( cg_layoutnode_t *rootnode ) { cg_layoutnode_t *node; if( !rootnode ) return; while( rootnode ) { node = rootnode; rootnode = rootnode->parent; if( node->ifthread ) CG_RecurseFreeLayoutThread( node->ifthread ); if( node->string ) CG_Free( node->string ); CG_Free( node ); } } //================ //CG_LayoutFixCommasInToken //commas are accepted in the scripts. They actually do nothing, but are good for readability //================ static qboolean CG_LayoutFixCommasInToken( char **ptr, char **backptr ) { char *token; char *back; int offset, count; qboolean stepback = qfalse; token = *ptr; back = *backptr; if( !token || !strlen(token) )return qfalse; // check that sizes match (quotes are removed from tokens) offset = count = strlen(token); back = *backptr; while( count-- ) { if( *back == '"' ) { count++; offset++; } back--; } back = *backptr - offset; while( offset ) { if( *back == '"' ) { offset--; back++; continue; } if( *token != *back ) CG_Printf( "Token and Back mismatch %c - %c\n", *token, *back ); if( *back == ',' ) { *back = ' '; stepback = qtrue; } offset--; token++; back++; } return stepback; } //================ //CG_RecurseParseLayoutScript //recursive for generating "if" subtrees //================ /*static */cg_layoutnode_t *CG_RecurseParseLayoutScript( char **ptr, int level ) { cg_layoutnode_t *command = NULL; cg_layoutnode_t *node = NULL; cg_layoutnode_t *rootnode = NULL; int expecArgs = 0, numArgs = 0; int token_type; qboolean add; char *token, *s_tokenback; if( !ptr ) return NULL; if( !*ptr || !*ptr[0] ) return NULL; while( *ptr ) { s_tokenback = *ptr; token = COM_Parse( ptr ); while( *token == ' ' ) token++; // eat up whitespaces if( !Q_stricmp( ",", token) ) continue; // was just a comma if( CG_LayoutFixCommasInToken( &token, ptr ) ) { *ptr = s_tokenback; // step back continue; } if( !*token ) continue; if( !strlen(token) ) continue; add = qfalse; token_type = CG_LayoutCathegorizeToken( token ); // if it's an operator, we don't create a node, but add the operation to the last one if( CG_OperatorFuncForArgument(token) != NULL ) { if( !node ) { CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i): \"%s\" Operator hasn't any prior argument\n", level, token ); continue; } if( node->type == LNODE_COMMAND || node->type == LNODE_STRING ) CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i): \"%s\" Operator was assigned to a command node\n", level, token ); else expecArgs++; // we now expect one extra argument (not counting the operator one) node->opFunc = CG_OperatorFuncForArgument( token ); continue; // skip and continue } if( expecArgs > numArgs ) { // we are expecting an argument switch( token_type ) { case LNODE_NUMERIC: case LNODE_STRING: case LNODE_STAT: break; case LNODE_COMMAND: { CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i): \"%s\" is not a valid argument for \"%s\"\n", level, token, command ? command->string : "" ); continue; } break; default: { CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i) skip and continue: Unrecognized token \"%s\"\n", level, token ); continue; } break; } } else { if( token_type != LNODE_COMMAND ) { // we are expecting a command CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i): unrecognized command \"%s\"\n", level, token ); continue; } //special case: endif commands interrupt the thread and are not saved if( !Q_stricmp( token, "endif" ) ) { //finish the last command properly if( command ) command->integer = expecArgs; return rootnode; } //special case: last command was "if", we create a new sub-thread and ignore the new command if( command && !Q_stricmp( command->string, "if" ) ) { *ptr = s_tokenback; // step back one token command->ifthread = CG_RecurseParseLayoutScript( ptr, level + 1 ); } } // things look fine, proceed creating the node switch( token_type ) { case LNODE_NUMERIC: case LNODE_STRING: case LNODE_STAT: { node = CG_LayoutParseArgumentNode( token ); if( !node ) { CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i): \"%s\" is not a valid argument for \"%s\"\n", level, token, command ? command->string : "" ); break; } numArgs++; add = qtrue; } break; case LNODE_COMMAND: { node = CG_LayoutParseCommandNode( token ); if( !node ) { CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i): \"%s\" is not a valid command\n", level, token ); break; // skip and continue } // expected arguments could have been extended by the operators if( command ) command->integer = expecArgs; // move on into the new command command = node; numArgs = 0; expecArgs = command->integer; add = qtrue; } break; default: break; } if( add == qtrue ) { if( rootnode ) rootnode->next = node; node->parent = rootnode; rootnode = node; } } if( level > 0 ) CG_Printf( "WARNING 'CG_RecurseParseLayoutScript'(level %i): If without endif\n", level ); return rootnode; } #if 0 static void CG_RecursePrintLayoutThread( cg_layoutnode_t *rootnode, int level ) { int i; cg_layoutnode_t *node; node = rootnode; while(node->parent) node = node->parent; while( node ) { for( i = 0; i < level; i++ ) CG_Printf( " " ); CG_Printf( "%s\n", node->string ); if( node->ifthread ) CG_RecursePrintLayoutThread( node->ifthread, level+1 ); node = node->next; } } #endif //================ //CG_ParseLayoutScript //================ void CG_ParseLayoutScript( char *string, cg_layoutnode_t *rootnode ) { CG_RecurseFreeLayoutThread( cg.statusBar ); cg.statusBar = CG_RecurseParseLayoutScript( &string, 0 ); #if 0 CG_RecursePrintLayoutThread( cg_layoutRootNode, 0 ); #endif } //============================================================================= //============================================================================= //================ //CG_RecurseExecuteLayoutThread // Execution works like this: First node (on backwards) is expected to be the command, followed by arguments nodes. // we keep a pointer to the command and run the tree counting arguments until we reach the next command, // then we call the command function sending the pointer to first argument and the pointer to the command. // At return we advance one node (we stopped at last argument node) so it starts again from the next command (if any). // // When finding an "if" command with a subtree, we execute the "if" command. In the case it // returns any value, we recurse execute the subtree //================ static void CG_RecurseExecuteLayoutThread( cg_layoutnode_t *rootnode ) { cg_layoutnode_t *argumentnode = NULL; cg_layoutnode_t *commandnode = NULL; int numArguments; if( !rootnode ) return; // run until the real root commandnode = rootnode; while( commandnode->parent ) { commandnode = commandnode->parent; } // now run backwards up to the next command node while( commandnode ) { argumentnode = commandnode->next; // we could trust the parser, but I prefer counting the arguments here numArguments = 0; while( argumentnode ) { if( argumentnode->type == LNODE_COMMAND ) break; argumentnode = argumentnode->next; numArguments++; } // reset argumentnode = commandnode->next; // Execute the command node if( commandnode->integer != numArguments ) { CG_Printf( "ERROR: Layout command %s: invalid argument count (expecting %i, found %i)\n", commandnode->string, commandnode->integer, numArguments ); return; } if( commandnode->func ) { //special case for if commands if( commandnode->func( commandnode, argumentnode, numArguments ) ) { // execute the "if" thread when command returns a value if( commandnode->ifthread ) CG_RecurseExecuteLayoutThread( commandnode->ifthread ); } } //move up to next command node commandnode = argumentnode; if( commandnode == rootnode ) return; while( commandnode && commandnode->type != LNODE_COMMAND ) { commandnode = commandnode->next; } } } //================ //CG_ExecuteLayoutProgram //================ void CG_ExecuteLayoutProgram( struct cg_layoutnode_s *rootnode ) { CG_RecurseExecuteLayoutThread( rootnode ); } //============================================================================= //============================================================================= //================ //CG_OptimizeStatusBarFile //================ char *CG_OptimizeStatusBarFile( char *path, qboolean skip_include ) { int length, f; char *temp_buffer; char *opt_buffer; char *parse, *token, *toinclude; int optimized_length, included_length; int fi, fi_length; char fipath[MAX_QPATH], shortname[MAX_QPATH]; // load the file length = trap_FS_FOpenFile( path, &f, FS_READ ); if( length == -1 ) return NULL; if( !length ) { trap_FS_FCloseFile( f ); return NULL; } // alloc a temp buffer according to size temp_buffer = CG_Malloc( length + 1 ); // load layout file in memory trap_FS_Read( temp_buffer, length, f ); trap_FS_FCloseFile( f ); // first pass: scan buffer line by line and check for include lines // if found compute needed length for included files // else count token length as Com_Parse as skipped comments and stuff like that parse=temp_buffer; optimized_length=0; included_length=0; while ( parse ) { token=COM_ParseExt2( &parse, qtrue, qfalse ); if( (!Q_stricmp( token, "include" )) && skip_include == qfalse) { toinclude=COM_ParseExt2( &parse, qtrue, qfalse ); //if( cg_debug_HUD && cg_debug_HUD->integer ) //CG_Printf( "included: %s \n", toinclude ); Q_strncpyz( shortname, toinclude, sizeof(shortname) ); Q_snprintfz( fipath, sizeof(fipath), "huds/inc/%s", shortname ); COM_ReplaceExtension( fipath, ".hud", sizeof(fipath) ); fi_length = trap_FS_FOpenFile( fipath, &fi, FS_READ ); if( fi_length == -1 ) { // failed to include file CG_Printf( "HUD: Failed to include hud subfile: %s \n", path ); } if( fi_length > 0) { // not an empty file // we have the size we can close it included_length+=fi_length; } trap_FS_FCloseFile( fi ); } else { // not an include line // simply count token size for optimized hud layout optimized_length+=strlen( token ) + 1 /*for spaces */; } } // second pass: we now have the needed size // alloc optimized buffer opt_buffer = CG_Malloc( optimized_length + included_length + 1 ); // reparse all file and copy it parse=temp_buffer; while ( parse ) { token=COM_ParseExt2( &parse, qtrue, qfalse ); if( (!Q_stricmp( token, "include" )) && skip_include == qfalse) { toinclude=COM_ParseExt2( &parse, qtrue, qfalse ); Q_strncpyz( shortname, toinclude, sizeof(shortname) ); Q_snprintfz( fipath, sizeof(fipath), "huds/inc/%s", shortname ); COM_ReplaceExtension( fipath, ".hud", sizeof(fipath) ); fi_length = trap_FS_FOpenFile( fipath, &fi, FS_READ ); if( fi_length == -1 ) { // failed to include file CG_Printf( "HUD: Failed to include hud subfile: %s \n", path ); } if( fi_length > 0) { char *fi_parse; char *include_buffer; // not an empty file if( cg_debug_HUD && cg_debug_HUD->integer ) CG_Printf( "HUD: Including sub hud file: %s \n", toinclude ); // reparse all lines from included file to skip include commands // alloc a temp buffer according to size include_buffer = CG_Malloc( fi_length + 1 ); // load included layout file in memory trap_FS_Read( include_buffer, fi_length, fi ); fi_parse=include_buffer; while ( fi_parse ) { token=COM_ParseExt2( &fi_parse, qtrue, qfalse ); if( !Q_stricmp( token, "include" ) ) { // skip recursive include toinclude=COM_ParseExt2( &fi_parse, qtrue, qfalse ); CG_Printf( "HUD: No recursive include allowed: huds/inc/%s \n", toinclude ); } else { // normal token strcat( opt_buffer, token ); strcat( opt_buffer, " "); } } // release memory CG_Free( include_buffer ); } // close included file trap_FS_FCloseFile( fi ); } else { // normal token strcat( opt_buffer, token ); strcat( opt_buffer, " "); } } // free temp buffer CG_Free( temp_buffer ); return opt_buffer; } //================ //CG_LoadStatusBarFile //================ void CG_LoadStatusBarFile( char *s ) { char path[MAX_QPATH], shortname[MAX_QPATH]; char *opt; if( !s || !s[0] ) return; // strip extension and add local path Q_strncpyz( shortname, s, sizeof(shortname) ); Q_snprintfz( path, sizeof(path), "huds/%s", shortname ); COM_ReplaceExtension( path, ".hud", sizeof(path) ); opt=CG_OptimizeStatusBarFile( path, qfalse ); if( opt == NULL ) { CG_Printf("HUD: failed to load huds/%s file\n", shortname ); return; } // load the new status bar program CG_ParseLayoutScript( opt, cg.statusBar ); // set up layout font as default system font layout_cursor_font = trap_SCR_RegisterFont( DEFAULT_FONT_SMALL ); } //================ //CG_LoadStatusBar //================ void CG_LoadStatusBar( void ) { // always load default first. Custom second if needed if( cg_debug_HUD && cg_debug_HUD->integer ) CG_Printf("HUD: Loading default clientHUD huds/%s\n", cg_clientHUD->dvalue ); CG_LoadStatusBarFile( cg_clientHUD->dvalue ); if( Q_stricmp( cg_clientHUD->string, cg_clientHUD->dvalue ) ) { if( cg_debug_HUD && cg_debug_HUD->integer ) CG_Printf("HUD: Loading custom clientHUD huds/%s\n", cg_clientHUD->string ); CG_LoadStatusBarFile( cg_clientHUD->string ); } }