#include "../plugin.h" #define DEFAULTHUDNAME "ftehud.hud" #define MAX_ELEMENTS 128 int K_UPARROW; int K_DOWNARROW; int K_LEFTARROW; int K_RIGHTARROW; int K_ESCAPE; int K_MOUSE1; int K_MOUSE2; int K_HOME; int K_SHIFT; int K_MWHEELDOWN; int K_MWHEELUP; int K_PAGEUP; int K_PAGEDOWN; #define MAX_CL_STATS 32 #define STAT_HEALTH 0 #define STAT_WEAPON 2 #define STAT_AMMO 3 #define STAT_ARMOR 4 #define STAT_WEAPONFRAME 5 #define STAT_SHELLS 6 #define STAT_NAILS 7 #define STAT_ROCKETS 8 #define STAT_CELLS 9 #define STAT_ACTIVEWEAPON 10 #define STAT_TOTALSECRETS 11 #define STAT_TOTALMONSTERS 12 #define STAT_SECRETS 13 // bumped on client side by svc_foundsecret #define STAT_MONSTERS 14 // bumped by svc_killedmonster #define STAT_ITEMS 15 //some engines can use more. //any mod specific ones should be 31 and downwards rather than upwards. #define IT_GUN1 (1<<0) #define IT_GUN2 (1<<1) //the code assumes these are linear. #define IT_GUN3 (1<<2) //be careful with strange mods. #define IT_GUN4 (1<<3) #define IT_GUN5 (1<<4) #define IT_GUN6 (1<<5) #define IT_GUN7 (1<<6) #define IT_GUN8 (1<<7) //quake doesn't normally use this. #define IT_AMMO1 (1<<8) #define IT_AMMO2 (1<<9) #define IT_AMMO3 (1<<10) #define IT_AMMO4 (1<<11) #define IT_GUN0 (1<<12) #define IT_ARMOR1 (1<<13) #define IT_ARMOR2 (1<<14) #define IT_ARMOR3 (1<<15) #define IT_SUPERHEALTH (1<<16) #define IT_PUP1 (1<<17) #define IT_PUP2 (1<<18) #define IT_PUP3 (1<<19) #define IT_PUP4 (1<<20) #define IT_PUP5 (1<<21) #define IT_PUP6 (1<<22) #define IT_RUNE1 (1<<23) #define IT_RUNE2 (1<<24) #define IT_RUNE3 (1<<25) #define IT_RUNE4 (1<<26) //these are linear and treated the same #define numpups 6 //the names of the cvars, as they will appear on the console #define UI_NOSBAR "ui_defaultsbar" #define UI_NOIBAR "ui_noibar" #define UI_NOFLASH "ui_nosbarflash" static char *weaponabbreviation[] = { //the postfix for the weapon anims "shotgun", "sshotgun", "nailgun", "snailgun", "rlaunch", //grenades actually. "srlaunch", "lightng" }; #define numweaps (sizeof(weaponabbreviation) / sizeof(char *)) static char *pupabbr[] = { //the postfix for the powerup anims "key1", "key2", "invis", "invul", "suit", "quad" }; static char *pupabbr2[] = { //the postfix for the powerup anims "key1", "key2", "invis", "invuln", "suit", "quad" }; //0 = owned, 1 selected, 2-7 flashing static qhandle_t con_chars; static qhandle_t pic_weapon[8][numweaps]; static qhandle_t sbarback, ibarback; //0 = owned, 1-6 flashing static qhandle_t pic_pup[7][numpups]; static qhandle_t pic_armour[3]; static qhandle_t pic_ammo[4]; static qhandle_t pic_rune[4]; static qhandle_t pic_num[13]; static qhandle_t pic_anum[11]; //faces static qhandle_t pic_face[5]; static qhandle_t pic_facep[5]; static qhandle_t pic_facequad; static qhandle_t pic_faceinvis; static qhandle_t pic_faceinvisinvuln; static qhandle_t pic_faceinvuln; static qhandle_t pic_faceinvulnquad; static int currenttime; static int gotweapontime[numweaps]; static int gotpuptime[numpups]; float sbarminx; float sbarminy; float sbarscalex; float sbarscaley; float sbaralpha; int sbartype; int sbarindex; static int hudedit; enum { DZ_BOTTOMLEFT, DZ_BOTTOMRIGHT }; typedef void drawelementfnc_t(void); typedef struct { float defaultx; //used if couldn't load a config float defaulty; int defaultzone; float defaultalpha; drawelementfnc_t *DrawElement; int subtype; } huddefaultelement_t; drawelementfnc_t Hud_SBar; drawelementfnc_t Hud_StatSmall; drawelementfnc_t Hud_StatBig; drawelementfnc_t Hud_ArmourPic; drawelementfnc_t Hud_HealthPic; drawelementfnc_t Hud_CurrentAmmoPic; drawelementfnc_t Hud_IBar; drawelementfnc_t Hud_Weapon; drawelementfnc_t Hud_W_Lightning; drawelementfnc_t Hud_Powerup; drawelementfnc_t Hud_Rune; drawelementfnc_t Hud_Ammo; drawelementfnc_t Hud_ScoreCard; drawelementfnc_t Hud_ScoreName; drawelementfnc_t Hud_Blackness; drawelementfnc_t Hud_TeamScore; drawelementfnc_t Hud_TeamName; // TODO: more elements // - generalized graphic elements // - cvar controlled small and big numbers // - alias controlled graphic elements (both +/-showscores like and alias calling?) // - Q2-style current weapon icon int statsremap[] = { STAT_HEALTH, STAT_ARMOR, STAT_AMMO, STAT_SHELLS, STAT_NAILS, STAT_ROCKETS, STAT_NAILS }; typedef struct { drawelementfnc_t *draw; char *name; int width, height; int maxsubtype; } drawelement_t; drawelement_t drawelement[] = { {Hud_SBar, "Status bar", 320, 24, 0}, {Hud_StatSmall, "Stat (small)", 8*3, 8, 6}, // equal to sizeof(statsremap)/sizeof(statsremap[0])-1 {Hud_StatBig, "Stat (big)", 24*3, 24, 6}, // equal to sizeof(statsremap)/sizeof(statsremap[0])-1 {Hud_ArmourPic, "Armor pic", 24, 24, 0}, {Hud_HealthPic, "Health pic", 24, 24, 0}, {Hud_CurrentAmmoPic, "Ammo pic", 24, 24, 0}, {Hud_IBar, "Info bar", 320, 24, 0}, {Hud_Weapon, "Weapon pic", 24, 16, 6}, {Hud_W_Lightning, "Shaft pic", 24, 16, 0}, {Hud_Powerup, "Powerup pic", 16, 16, 5}, {Hud_Rune, "Rune pic", 8, 16, 3}, {Hud_Ammo, "Ammo display", 42, 11, 3}, {Hud_Blackness, "Blackness", 16, 16, 10}, {Hud_ScoreCard, "Scorecard", 32, 8, 16}, {Hud_ScoreName, "Scorename", 128, 8, 8}, {Hud_TeamScore, "TeamScore", 32, 8, 8}, {Hud_TeamName, "TeamName", 128, 8, 8} }; huddefaultelement_t hedefaulttype[] = { { 0, -24, DZ_BOTTOMLEFT, 0.3f, Hud_SBar }, { 0, -24, DZ_BOTTOMLEFT, 1, Hud_ArmourPic }, { 24, -24, DZ_BOTTOMLEFT, 1, Hud_StatBig, 1 }, { 112, -24, DZ_BOTTOMLEFT, 1, Hud_HealthPic }, { 24*6, -24, DZ_BOTTOMLEFT, 1, Hud_StatBig, 0 }, { 224, -24, DZ_BOTTOMLEFT, 1, Hud_CurrentAmmoPic }, { 248, -24, DZ_BOTTOMLEFT, 1, Hud_StatBig, 2 }, { 0, -48, DZ_BOTTOMLEFT, 0.3f, Hud_IBar }, { 0, -40, DZ_BOTTOMLEFT, 1, Hud_Weapon, 0 }, { 24, -40, DZ_BOTTOMLEFT, 1, Hud_Weapon, 1 }, { 48, -40, DZ_BOTTOMLEFT, 1, Hud_Weapon, 2 }, { 72, -40, DZ_BOTTOMLEFT, 1, Hud_Weapon, 3 }, { 96, -40, DZ_BOTTOMLEFT, 1, Hud_Weapon, 4 }, { 120, -40, DZ_BOTTOMLEFT, 1, Hud_Weapon, 5 }, { 146, -40, DZ_BOTTOMLEFT, 1, Hud_W_Lightning }, { 194, -40, DZ_BOTTOMLEFT, 0.3f, Hud_Powerup, 0 }, { 208, -40, DZ_BOTTOMLEFT, 0.3f, Hud_Powerup, 1 }, { 224, -40, DZ_BOTTOMLEFT, 1, Hud_Powerup, 2 }, { 240, -40, DZ_BOTTOMLEFT, 1, Hud_Powerup, 3 }, { 256, -40, DZ_BOTTOMLEFT, 1, Hud_Powerup, 4 }, { 272, -40, DZ_BOTTOMLEFT, 1, Hud_Powerup, 5 }, { 288, -40, DZ_BOTTOMLEFT, 0.3f, Hud_Rune, 0 }, { 296, -40, DZ_BOTTOMLEFT, 0.3f, Hud_Rune, 1 }, { 304, -40, DZ_BOTTOMLEFT, 0.3f, Hud_Rune, 2 }, { 312, -40, DZ_BOTTOMLEFT, 0.3f, Hud_Rune, 3 }, { 48*0+3, -48, DZ_BOTTOMLEFT, 1, Hud_Ammo, 0 }, { 48*1+3, -48, DZ_BOTTOMLEFT, 1, Hud_Ammo, 1 }, { 48*2+3, -48, DZ_BOTTOMLEFT, 1, Hud_Ammo, 2 }, { 48*3+3, -48, DZ_BOTTOMLEFT, 1, Hud_Ammo, 3 }, { 42*3, -48, DZ_BOTTOMLEFT, 1, Hud_ScoreCard } }; typedef struct { int type; int subtype; float x, y; float scalex; float scaley; float alpha; } hudelement_t; hudelement_t element[MAX_ELEMENTS]; //look - Spike used a constant - that's a turn up for the books! int numelements; int currentitem; int hoveritem; qboolean mousedown, shiftdown; float mouseofsx, mouseofsy; qboolean context; void UI_DrawPic(qhandle_t pic, int x, int y, int width, int height) { Draw_Image((float)x*sbarscalex+sbarminx, (float)y*sbarscaley+sbarminy, (float)width*sbarscalex, (float)height*sbarscaley, 0, 0, 1, 1, pic); } void UI_DrawChar(unsigned int c, int x, int y) { static float size = 1.0f/16.0f; float s1 = size * (c&15); float t1 = size * (c>>4); Draw_Image((float)x*sbarscalex+sbarminx, (float)y*sbarscaley+sbarminy, 8*sbarscalex, 8*sbarscaley, s1, t1, s1+size, t1+size, con_chars); } void UI_DrawString(char *s, int x, int y) { while(*s) { UI_DrawChar((unsigned int)*s++, x, y); x+=8; } } void UI_DrawBigNumber(int num, int x, int y, qboolean red) { char *s; int len; s = va("%i", num); len = strlen(s); if (len < 3) x += 24*(3-len); else s += len-3; if (red) { while(*s) { if (*s == '-') UI_DrawPic (pic_anum[10], x, y, 24, 24); else UI_DrawPic (pic_anum[*s-'0'], x, y, 24, 24); s++; x+=24; } } else { while(*s) { if (*s == '-') UI_DrawPic (pic_num[10], x, y, 24, 24); else UI_DrawPic (pic_num[*s-'0'], x, y, 24, 24); s++; x+=24; } } } void SBar_FlushAll(void) { numelements = 0; } static int idxforfunc(drawelementfnc_t *fnc) { int i; for (i = 0; i < sizeof(drawelement)/sizeof(drawelement[0]); i++) { if (drawelement[i].draw == fnc) return i; } return -10000; //try and crash } void SBar_ReloadDefaults(void) { int i; for (i = 0; i < sizeof(hedefaulttype)/sizeof(hedefaulttype[0]); i++) { if (hedefaulttype[i].defaultalpha) { if (numelements >= MAX_ELEMENTS) break; element[numelements].type = idxforfunc(hedefaulttype[i].DrawElement); element[numelements].alpha = hedefaulttype[i].defaultalpha; element[numelements].scalex = 1; element[numelements].scaley = 1; element[numelements].subtype = hedefaulttype[i].subtype; switch(hedefaulttype[i].defaultzone) { case DZ_BOTTOMLEFT: element[numelements].x = hedefaulttype[i].defaultx; element[numelements].y = 480+hedefaulttype[i].defaulty; break; case DZ_BOTTOMRIGHT: element[numelements].x = 640+hedefaulttype[i].defaultx; element[numelements].y = 480+hedefaulttype[i].defaulty; break; } numelements++; } } } void UI_SbarInit(void) { int i; int j; //main bar (add cvars later) ibarback = Draw_LoadImage("ibar", false); sbarback = Draw_LoadImage("sbar", false); con_chars = Draw_LoadImage("conchars", false); //load images. for (i = 0; i < 10; i++) { pic_num[i] = Draw_LoadImage(va("num_%i", i), false); pic_anum[i] = Draw_LoadImage(va("anum_%i", i), false); } pic_num[10] = Draw_LoadImage("num_minus", false); pic_anum[10] = Draw_LoadImage("anum_minus", false); pic_num[11] = Draw_LoadImage("num_colon", false); pic_num[12] = Draw_LoadImage("num_slash", false); for (i = 0; i < numweaps; i++) { gotweapontime[i] = 0; pic_weapon[0][i] = Draw_LoadImage(va("inv_%s", weaponabbreviation[i]), false); pic_weapon[1][i] = Draw_LoadImage(va("inv2_%s", weaponabbreviation[i]), false); for (j = 0; j < 5; j++) { pic_weapon[2+j][i] = Draw_LoadImage(va("inva%i_%s", j+1, weaponabbreviation[i]), false); } } for (i = 0; i < numpups; i++) { gotpuptime[i] = 0; pic_pup[0][i] = Draw_LoadImage(va("sb_%s", pupabbr2[i]), false); for (j = 0; j < 5; j++) { pic_pup[1+j][i] = Draw_LoadImage(va("sba%i_%s", j+1, pupabbr[i]), false); } } pic_armour[0] = Draw_LoadImage("sb_armor1", false); pic_armour[1] = Draw_LoadImage("sb_armor2", false); pic_armour[2] = Draw_LoadImage("sb_armor3", false); pic_ammo[0] = Draw_LoadImage("sb_shells", false); pic_ammo[1] = Draw_LoadImage("sb_nails", false); pic_ammo[2] = Draw_LoadImage("sb_rocket", false); pic_ammo[3] = Draw_LoadImage("sb_cells", false); pic_rune[0] = Draw_LoadImage("sb_sigil1", false); pic_rune[1] = Draw_LoadImage("sb_sigil2", false); pic_rune[2] = Draw_LoadImage("sb_sigil3", false); pic_rune[3] = Draw_LoadImage("sb_sigil4", false); pic_face[0] = Draw_LoadImage("face1", false); pic_face[1] = Draw_LoadImage("face2", false); pic_face[2] = Draw_LoadImage("face3", false); pic_face[3] = Draw_LoadImage("face4", false); pic_face[4] = Draw_LoadImage("face5", false); pic_facep[0] = Draw_LoadImage("face_p1", false); pic_facep[1] = Draw_LoadImage("face_p2", false); pic_facep[2] = Draw_LoadImage("face_p3", false); pic_facep[3] = Draw_LoadImage("face_p4", false); pic_facep[4] = Draw_LoadImage("face_p5", false); pic_facequad = Draw_LoadImage("face_quad", false); pic_faceinvis = Draw_LoadImage("face_invis", false); pic_faceinvisinvuln = Draw_LoadImage("face_inv2", false); pic_faceinvuln = Draw_LoadImage("face_invul1", false); pic_faceinvulnquad = Draw_LoadImage("face_invul2", false); SBar_FlushAll(); SBar_ReloadDefaults(); } unsigned int stats[MAX_CL_STATS]; void Hud_SBar(void) { UI_DrawPic(sbarback, 0, 0, 320, 24); } void Hud_ArmourPic(void) { if (stats[STAT_ITEMS] & IT_ARMOR3) UI_DrawPic(pic_armour[2], 0, 0, 24, 24); else if (stats[STAT_ITEMS] & IT_ARMOR2) UI_DrawPic(pic_armour[1], 0, 0, 24, 24); else if (stats[STAT_ITEMS] & IT_ARMOR1 || hudedit) UI_DrawPic(pic_armour[0], 0, 0, 24, 24); } void Hud_HealthPic(void) { int hl; if (stats[STAT_ITEMS] & IT_PUP3) { //invisability if (stats[STAT_ITEMS] & IT_PUP4) UI_DrawPic(pic_faceinvisinvuln, 0, 0, 24, 24); else UI_DrawPic(pic_faceinvis, 0, 0, 24, 24); return; } if (stats[STAT_ITEMS] & IT_PUP4) { //invuln if (stats[STAT_ITEMS] & IT_PUP6) UI_DrawPic(pic_faceinvulnquad, 0, 0, 24, 24); else UI_DrawPic(pic_faceinvuln, 0, 0, 24, 24); return; } if (stats[STAT_ITEMS] & IT_PUP6) { UI_DrawPic(pic_facequad, 0, 0, 24, 24); return; } hl = stats[STAT_HEALTH]/20; if (hl > 4) hl = 4; if (hl < 0) hl = 0; //FIXME // if (innpain) // UI_DrawPic(pic_facep[4-hl], 0, 0, 24, 24); // else UI_DrawPic(pic_face[4-hl], 0, 0, 24, 24); } void Hud_StatBig(void) { int i = stats[statsremap[sbartype]]; UI_DrawBigNumber(i, 0, 0, i < 25); } void Hud_StatSmall(void) { int i = stats[statsremap[sbartype]]; // TODO: need some sort of options thing to change between brown/white/gold text UI_DrawChar(i%10+18, 19, 0); i/=10; if (i) UI_DrawChar(i%10+18, 11, 0); i/=10; if (i) UI_DrawChar(i%10+18, 3, 0); } void Hud_CurrentAmmoPic(void) { if ((stats[STAT_ITEMS] & IT_AMMO1)) UI_DrawPic(pic_ammo[0], 0, 0, 24, 24); else if (stats[STAT_ITEMS] & IT_AMMO2) UI_DrawPic(pic_ammo[1], 0, 0, 24, 24); else if (stats[STAT_ITEMS] & IT_AMMO3) UI_DrawPic(pic_ammo[2], 0, 0, 24, 24); else if (stats[STAT_ITEMS] & IT_AMMO4 || hudedit) UI_DrawPic(pic_ammo[3], 0, 0, 24, 24); } void Hud_IBar(void) { UI_DrawPic(ibarback, 0, 0, 320, 24); } void Hud_Weapon(void) { int flash; if (!(stats[STAT_ITEMS] & (IT_GUN1 << sbartype)) && !hudedit) { gotweapontime[sbartype] = 0; return; } if (!gotweapontime[sbartype]) gotweapontime[sbartype] = currenttime; flash = (currenttime - gotweapontime[sbartype])/100; if (flash < 0) //errr... whoops... flash = 0; if (flash > 10) { if (stats[STAT_ACTIVEWEAPON] & (IT_GUN1 << sbartype)) flash = 1; //selected. else flash = 0; } else flash = (flash%5) + 2; UI_DrawPic(pic_weapon[flash][sbartype], 0, 0, 24, 16); } void Hud_W_HalfLightning(void) //left half only (needed due to LG icon being twice as wide) { int flash; int wnum = 6; if (!(stats[STAT_ITEMS] & (IT_GUN1 << wnum)) && !hudedit) { gotweapontime[wnum] = 0; return; } if (!gotweapontime[wnum]) gotweapontime[wnum] = currenttime; flash = (currenttime - gotweapontime[wnum])/100; if (flash < 0) //errr... whoops... flash = 0; if (flash > 10) { if (stats[STAT_ACTIVEWEAPON] & (IT_GUN1 << wnum)) flash = 1; //selected. else flash = 0; } else flash = (flash%5) + 2; Draw_Image(sbarminx, sbarminy, (float)24*sbarscalex, (float)16*sbarscaley, 0, 0, 0.5, 1, pic_weapon[flash][wnum]); } void Hud_W_Lightning(void) { int flash; int wnum = 6; if (!(stats[STAT_ITEMS] & (IT_GUN1 << wnum)) && !hudedit) { gotweapontime[wnum] = 0; return; } if (!gotweapontime[wnum]) gotweapontime[wnum] = currenttime; flash = (currenttime - gotweapontime[wnum])/100; if (flash < 0) //errr... whoops... flash = 0; if (flash > 10) { if (stats[STAT_ACTIVEWEAPON] & (IT_GUN1 << wnum)) flash = 1; //selected. else flash = 0; } else flash = (flash%5) + 2; UI_DrawPic(pic_weapon[flash][wnum], 0, 0, 48, 16); } void Hud_Powerup(void) { int flash; if (!(stats[STAT_ITEMS] & (IT_PUP1 << sbartype)) && !hudedit) return; if (!gotpuptime[sbartype]) gotpuptime[sbartype] = currenttime; flash = (currenttime - gotpuptime[sbartype])/100; if (flash < 0) //errr... whoops... flash = 0; if (flash > 10) { flash = 0; } else flash = (flash%5) + 2; UI_DrawPic(pic_pup[flash][sbartype], 0, 0, 16, 16); } void Hud_Rune(void) { if (!(stats[STAT_ITEMS] & (IT_RUNE1 << sbartype)) && !hudedit) return; UI_DrawPic(pic_rune[sbartype], 0, 0, 8, 16); } void Hud_Ammo(void) { int num; Draw_Image(sbarminx, sbarminy, (float)42*sbarscalex, (float)11*sbarscaley, (3+(sbartype*48))/320.0f, 0, (3+(sbartype*48)+42)/320.0f, 11/24.0f, ibarback); num = stats[STAT_SHELLS+sbartype]; if (hudedit) num = 999; UI_DrawChar(num%10+18, 19, 0); num/=10; if (num) UI_DrawChar(num%10+18, 11, 0); num/=10; if (num) UI_DrawChar(num%10+18, 3, 0); } float pc[16][3] = { /* {235, 235, 235}, {143, 111, 035}, {139, 139, 203}, {107, 107, 015}, {127, 000, 000}, {175, 103, 035}, {255, 243, 027}, {227, 179, 151}, {171, 139, 163}, {187, 115, 159}, {219, 195, 187}, {111, 131, 123}, {255, 243, 027}, {000, 000, 255}, {247, 211, 139} */ {0.922, 0.922, 0.922}, {0.560, 0.436, 0.137}, {0.545, 0.545, 0.796}, {0.420, 0.420, 0.059}, {0.498, 0.000, 0.000}, {0.686, 0.404, 0.137}, {1.000, 0.953, 0.106}, {0.890, 0.702, 0.592}, {0.671, 0.545, 0.639}, {0.733, 0.451, 0.624}, {0.859, 0.765, 0.733}, {0.436, 0.514, 0.482}, {1.000, 0.953, 0.106}, {0.000, 0.000, 1.000}, {0.969, 0.827, 0.545} }; int numsortedplayers; int playerlocal; int sortedplayers[32]; plugclientinfo_t players[32]; void SortPlayers(void) { int i, j; int temp; numsortedplayers = 0; playerlocal = -1; for (i = 0; i < 32; i++) { if (GetPlayerInfo(i, &players[i])>0) playerlocal = i; if (players[i].spectator) continue; if (*players[i].name != 0) sortedplayers[numsortedplayers++] = i; } for (i = 0; i < numsortedplayers; i++) { for (j = i+1; j < numsortedplayers; j++) { if (players[sortedplayers[i]].frags < players[sortedplayers[j]].frags) { temp = sortedplayers[j]; sortedplayers[j] = sortedplayers[i]; sortedplayers[i] = temp; } } } } typedef struct { char name[8]; int frags; int tc; int bc; } teams_t; teams_t team[32]; int numsortedteams; void SortTeams(void) { teams_t temp; int i, j; numsortedplayers = 0; playerlocal = -1; for (i = 0; i < 32; i++) { if (GetPlayerInfo(i, &players[i])>0) playerlocal = i; if (players[i].spectator) continue; if (*players[i].name != 0) sortedplayers[numsortedplayers++] = i; for (j = 0; j < numsortedteams; j++) { if (!strcmp(team[j].name, players[i].name)) { team[j].frags += players[i].frags; while(j > 0) { if (team[j-1].frags < team[j].frags) { memcpy(&temp, &team[j], sizeof(teams_t)); memcpy(&team[j], &team[j-1], sizeof(teams_t)); memcpy(&team[j-1], &temp, sizeof(teams_t)); j--; } else break; } } } if (j == numsortedteams) { strlcpy(team[j].name, player[i].name, sizeof(team[j].name)); team[j].frags = players[i].frags; team[j].tc = players[i].tc; team[j].bc = players[i].bc; numsortedteams++; } } } void Hud_ScoreCard(void) { int frags, tc, bc, p; int brackets; char number[6]; char *num; if (hudedit) { frags = sbartype; tc = 0; bc = 0; brackets = 1; } else { SortPlayers(); if (sbartype>=numsortedplayers) return; p = sortedplayers[sbartype]; bc = players[p].bottomcolour; tc = players[p].topcolour; frags = players[p].frags; brackets = p==playerlocal; } Draw_Colour4f(pc[tc][0], pc[tc][1], pc[tc][2], sbaralpha); Draw_Fill(sbarminx, sbarminy, (float)32*sbarscalex, (float)4*sbarscaley); Draw_Colour4f(pc[bc][0], pc[bc][1], pc[bc][2], sbaralpha); Draw_Fill(sbarminx, sbarminy+4*sbarscaley, (float)32*sbarscalex, (float)4*sbarscaley); Draw_Colour4f(1, 1, 1, sbaralpha); if (brackets) { UI_DrawChar(16, 0, 0); UI_DrawChar(17, 24, 0); } snprintf(number, sizeof(number), "%-3i", frags); UI_DrawChar(number[0], 4, 0); UI_DrawChar(number[1], 12, 0); UI_DrawChar(number[2], 20, 0); Draw_Colour4f(1,1,1,1); } void Hud_ScoreName(void) { int p; char *name; if (hudedit) { name = va("Player %i", sbartype); } else { SortPlayers(); if (sbartype>=numsortedplayers) return; p = sortedplayers[sbartype]; name = players[p].name; } UI_DrawString(name, 0, 0); } void Hud_TeamCard(void) { int frags, tc, bc, p; int brackets; char number[6]; char *num; if (hudedit) { frags = sbartype; tc = 0; bc = 0; brackets = 1; } else { SortPlayers(); if (sbartype>=numsortedplayers) return; p = sortedplayers[sbartype]; bc = players[p].bottomcolour; tc = players[p].topcolour; frags = players[p].frags; brackets = p==playerlocal; } Draw_Colour4f(pc[tc][0], pc[tc][1], pc[tc][2], sbaralpha); Draw_Fill(sbarminx, sbarminy, (float)32*sbarscalex, (float)4*sbarscaley); Draw_Colour4f(pc[bc][0], pc[bc][1], pc[bc][2], sbaralpha); Draw_Fill(sbarminx, sbarminy+4*sbarscaley, (float)32*sbarscalex, (float)4*sbarscaley); Draw_Colour4f(1, 1, 1, sbaralpha); if (brackets) { UI_DrawChar(16, 0, 0); UI_DrawChar(17, 24, 0); } snprintf(number, sizeof(number), "%-3i", frags); UI_DrawChar(number[0], 4, 0); UI_DrawChar(number[1], 12, 0); UI_DrawChar(number[2], 20, 0); Draw_Colour4f(1,1,1,1); } void Hud_TeamCard(void) { int frags, tc, bc, p; int brackets; char number[6]; char *num; if (hudedit) { frags = sbartype; tc = 0; bc = 0; brackets = 1; } else { SortTeams(); if (sbartype>=numsortedteams) return; p = sortedteams[sbartype]; bc = teams[p].bottomcolour; tc = teams[p].topcolour; frags = teams[p].frags; brackets = p==playerlocal; } Draw_Colour4f(pc[tc][0], pc[tc][1], pc[tc][2], sbaralpha); Draw_Fill(sbarminx, sbarminy, (float)32*sbarscalex, (float)4*sbarscaley); Draw_Colour4f(pc[bc][0], pc[bc][1], pc[bc][2], sbaralpha); Draw_Fill(sbarminx, sbarminy+4*sbarscaley, (float)32*sbarscalex, (float)4*sbarscaley); Draw_Colour4f(1, 1, 1, sbaralpha); if (brackets) { UI_DrawChar(16, 0, 0); UI_DrawChar(17, 24, 0); } snprintf(number, sizeof(number), "%-3i", frags); UI_DrawChar(number[0], 4, 0); UI_DrawChar(number[1], 12, 0); UI_DrawChar(number[2], 20, 0); Draw_Colour4f(1,1,1,1); } //fixme: draw dark blobs void Hud_Blackness(void) { Draw_Colour4f(0, 0, 0, sbartype/10.0f); if (hudedit) { if (sbarindex == currentitem) { float j = ((currenttime % 1000) - 500) / 500.0f; if (j < 0) j = -j; j/=3; Draw_Colour4f(j, 0, 0, sbartype/10.0f); } else if (sbarindex == hoveritem) { Draw_Colour4f(0.0, 0.2, 0.0, sbartype/10.0f); } } Draw_Fill(sbarminx, sbarminy, (float)16*sbarscalex, (float)16*sbarscaley); Draw_Colour4f(1,1,1,1); } void UI_DrawHandles(int *arg, int i) { int mt; float vsx, vsy; vsx = arg[3]/640.0f; vsy = arg[4]/480.0f; sbarminx = arg[1] + element[i].x*vsx; sbarminy = arg[2] + element[i].y*vsy; sbarscalex = element[i].scalex*vsx; sbarscaley = element[i].scaley*vsy; mt = element[i].type; sbartype = element[i].subtype; sbaralpha = element[i].alpha; Draw_Colour4f(1, 0, 0, 1); Draw_Fill(sbarminx+drawelement[mt].width*sbarscalex-((sbarscalex<0)?0:(vsx*4)), sbarminy-((sbarscaley>=0)?0:(vsy*4)), (float)4*vsx, (float)4*vsy); Draw_Colour4f(0, 1, 0, 1); Draw_Fill(sbarminx+drawelement[mt].width*sbarscalex-((sbarscalex<0)?0:(vsx*4)), sbarminy+drawelement[mt].height*sbarscaley-((sbarscaley<0)?0:(vsy*4)), (float)4*vsx, (float)4*vsy); } //draw cody of sbar //arg[0] is playernum //arg[1]/arg[2] is x/y start of subwindow //arg[3]/arg[4] is width/height of subwindow int UI_StatusBar(int *arg) { int i; float vsx, vsy; if (arg[5]) return; CL_GetStats(arg[0], stats, sizeof(stats)/sizeof(int)); vsx = arg[3]/640.0f; vsy = arg[4]/480.0f; for (i = 0; i < numelements; i++) { sbarminx = arg[1] + element[i].x*vsx; sbarminy = arg[2] + element[i].y*vsy; sbarscalex = element[i].scalex*vsx; sbarscaley = element[i].scaley*vsy; sbartype = element[i].subtype; sbaralpha = element[i].alpha; drawelement[element[i].type].draw(); } return true; } int UI_StatusBarEdit(int *arg) // seperated so further improvements to editor view can be done { int i; float vsx, vsy; qboolean clrset = false; CL_GetStats(arg[0], stats, sizeof(stats)/sizeof(int)); vsx = arg[3]/640.0f; vsy = arg[4]/480.0f; for (i = 0; i < numelements; i++) { if (i == currentitem) { float j = ((currenttime % 1000) - 500) / 500.0f; if (j < 0) j = -j; Draw_Colour3f(1.0, j, j); clrset = true; } else if (i == hoveritem) { Draw_Colour3f(0.0, 1.0, 0.0); clrset = true; } sbarminx = arg[1] + element[i].x*vsx; sbarminy = arg[2] + element[i].y*vsy; sbarscalex = element[i].scalex*vsx; sbarscaley = element[i].scaley*vsy; sbartype = element[i].subtype; sbaralpha = element[i].alpha; sbarindex = i; drawelement[element[i].type].draw(); if (clrset) { Draw_Colour3f(1.0, 1.0, 1.0); clrset = false; } } return true; } int UI_ScoreBoard(int *arg) { int i; if (!arg[5]) return; sbarminx = 320; sbarminy = 48; sbarscalex = 1; sbarscaley = 1; sbaralpha = 1; SortPlayers(); for (i = 0; i < numsortedplayers; i++) { sbartype = i; Hud_ScoreCard(); UI_DrawString(players[sortedplayers[i]].name, 40, 0); sbarminy += 16; } } #define HUD_VERSION 52345 void PutFloat(float f, char sep, qhandle_t handle) { char *buffer; buffer = va("%f%c", f, sep); FS_Write(handle, buffer, strlen(buffer)); } void PutInteger(int i, char sep, qhandle_t handle) { char *buffer; buffer = va("%i%c", i, sep); FS_Write(handle, buffer, strlen(buffer)); } void Hud_Save(char *fname) { int i; qhandle_t handle; if (!*fname) fname = DEFAULTHUDNAME; if (FS_Open(fname, &handle, 2)<0) { Con_Printf("Couldn't open %s\n", fname); return; } PutInteger(HUD_VERSION, '\n', handle); PutInteger(numelements, '\n', handle); for (i = 0; i < numelements; i++) { PutFloat(element[i].x, ' ', handle); PutFloat(element[i].y, ' ', handle); PutFloat(element[i].scalex, ' ', handle); PutFloat(element[i].scaley, ' ', handle); PutInteger(element[i].type, ' ', handle); PutInteger(element[i].subtype, ' ', handle); PutFloat(element[i].alpha, '\n', handle); } FS_Close(handle); } float GetFloat(char **f, qhandle_t handle) { char *ts; while(**f <= ' ' && **f != 0) (*f)++; while(*f[0] == '/' && *f[1] == '/') { while(**f != '\n' && **f != 0) (*f)++; while(**f <= ' ' && **f != 0) (*f)++; } ts = *f; while (**f>' ') (*f)++; return (float)atof(ts); } int GetInteger(char **f, qhandle_t handle) { char *ts; while(**f <= ' ' && **f != 0) (*f)++; while(*f[0] == '/' && *f[1] == '/') { while(**f != '\n' && **f != 0) (*f)++; while(**f <= ' ' && **f != 0) (*f)++; } ts = *f; while (**f>' ') (*f)++; return atoi(ts); } void Hud_Load(char *fname) { char file[16384]; char *p; int len; int i; qhandle_t handle; int ver; float x, y, sx, sy, a; int type, subtype; if (!*fname) fname = DEFAULTHUDNAME; len = FS_Open(fname, &handle, 1); if (len < 0) { Con_Printf("Couldn't load file\n"); return; } if (len > 16383) len = 16383; FS_Read(handle, file, len); file[len] = 0; FS_Close(handle); p = file; ver = GetInteger(&p, handle); if (ver != HUD_VERSION) { Con_Printf("Hud version doesn't match (%i != %i)\n", ver, HUD_VERSION); return; } numelements = GetInteger(&p, handle); if (numelements > MAX_ELEMENTS) { numelements = 0; Con_Printf("Hud has too many elements\n"); return; } for (i = 0; i < numelements; i++) { x = GetFloat(&p, handle); y = GetFloat(&p, handle); sx = GetFloat(&p, handle); sy = GetFloat(&p, handle); type = GetInteger(&p, handle); subtype = GetInteger(&p, handle); a = GetFloat(&p, handle); if (type<0 || type>=sizeof(drawelement)/sizeof(drawelement[0])) { numelements--; i--; continue; } element[i].x = x; element[i].y = y; element[i].scalex = sx; element[i].scaley = sy; element[i].alpha = a; element[i].type = type; element[i].subtype = subtype; } } // FindItemUnderMouse: given mouse coordinates, finds element number under mouse // returns -1 if no element found int FindItemUnderMouse(int mx, int my) { int i; int rv; rv = -1; for (i = 0; i < numelements; i++) { if (element[i].scalex < 0) { if (element[i].x < mx) continue; if (element[i].x + element[i].scalex*drawelement[element[i].type].width > mx) continue; } else { if (element[i].x > mx) continue; if (element[i].x + element[i].scalex*drawelement[element[i].type].width < mx) continue; } if (element[i].scaley < 0) { if (element[i].y < my) continue; if (element[i].y + element[i].scaley*drawelement[element[i].type].height > my) continue; } else { if (element[i].y > my) continue; if (element[i].y + element[i].scaley*drawelement[element[i].type].height < my) continue; } rv = i; } return rv; // no element found } void DrawContextMenu(int mx, int my) { int i; int y; Draw_Colour4f(0, 0, 0, 0.4); Draw_Fill((mouseofsx-8)*sbarscalex, (mouseofsy-8)*sbarscaley, (float)112*sbarscalex, (float)(9*8)*sbarscaley); Draw_Colour4f(1,1,1,1); sbarminx = mouseofsx*sbarscalex; sbarminy = mouseofsy*sbarscaley; my -= mouseofsy; my/=8; my--; y = 0; UI_DrawString("CONTEXT MENU", 0, y); y+=8; UI_DrawString("------------", 0, y); y+=8; Draw_Colour4f(1-(my--)!=0,1,1,1); UI_DrawString("Clone", 0, y); y+=8; Draw_Colour4f(1-(my--)!=0,1,1,1); UI_DrawString("Snap To Grid", 0, y); y+=8; Draw_Colour4f(1-(my--)!=0,1,1,1); UI_DrawString("Save", 0, y); y+=8; Draw_Colour4f(1-(my--)!=0,1,1,1); UI_DrawString("Reload", 0, y); y+=8; Draw_Colour4f(1-(my--)!=0,1,1,1); UI_DrawString("Defaults", 0, y); y+=8; Draw_Colour4f(1,1,1,1); } void UI_KeyPress(int key, int mx, int my) { int i; if (key == K_ESCAPE) { Menu_Control(0); return; } if (context) { i = my - mouseofsy; i /= 8; context = false; switch(i) { case 2: //clone if (numelements==MAX_ELEMENTS) return; //too many memcpy(element+numelements, element+currentitem, sizeof(hudelement_t)); currentitem = numelements; numelements++; break; case 3: //snap shiftdown ^= 1; break; case 4: //save Hud_Save(NULL); break; case 5: //reload Hud_Load(NULL); break; case 6: //defaults SBar_FlushAll(); SBar_ReloadDefaults(); break; } return; } if (key == K_MOUSE1) { //figure out which one our cursor is over... mousedown = false; i = FindItemUnderMouse(mx, my); if (i != -1) { int oldcurrent; float big; oldcurrent = currentitem; currentitem = i; mouseofsx = mx - element[i].x; mouseofsy = my - element[i].y; mousedown |= 1; if (element[i].scalex < 0) { if (mx > element[i].x+4) return; } else { big = element[i].scalex*drawelement[element[i].type].width; if (mx-element[i].x+4 < big) return; } if (my < element[i].y+4) { if (currentitem == oldcurrent) UI_KeyPress('d', 0, 0); return; } if (element[i].scaley < 0) { if (my > element[i].y+4) return; } else { big = element[i].scaley*drawelement[element[i].type].height; if (my-element[i].y+4 < big) return; } mouseofsx = mx - element[i].scalex*drawelement[element[i].type].width; mouseofsy = my - element[i].scaley*drawelement[element[i].type].height; mousedown |= 2; } } // TODO: extra buttons // - toggle clip to edges and clip to other controls // - maybe toggle snap to grid instead of holding shift with mouse? else if (key == K_MOUSE2) { mousedown = false; //perhaps not logically true, but it's safest this way. context = true; mouseofsx = mx; mouseofsy = my; } else if (key == 'n') { currentitem++; if (currentitem >= numelements) currentitem = 0; } else if (key == 'm') { currentitem--; if (currentitem < 0) currentitem = numelements ? numelements - 1 : 0; } else if (key == 'i') { if (numelements==MAX_ELEMENTS) return; //too many element[numelements].scalex = 1; element[numelements].scaley = 1; element[numelements].alpha = 1; numelements++; } else if (currentitem < numelements) { if (key == K_SHIFT) shiftdown = true; else if (key == 'd') { mousedown = false; memcpy(element+currentitem, element+currentitem+1, sizeof(element[0]) * (numelements - currentitem-1)); numelements--; currentitem = 0; } else if (key == K_PAGEUP) { //send to back hudelement_t temp; memcpy(&temp, element+currentitem, sizeof(temp)); memmove(element+1, element, sizeof(hudelement_t) * (currentitem)); memcpy(element, &temp, sizeof(hudelement_t)); currentitem = 0; } else if (key == K_PAGEDOWN) { //bring to front hudelement_t temp; memcpy(&temp, element+currentitem, sizeof(temp)); memcpy(element+currentitem, element+currentitem+1, sizeof(element[0]) * (numelements - currentitem-1)); currentitem = numelements - 1; memcpy(element+currentitem , &temp, sizeof(hudelement_t)); } else if (key == 'q') { element[currentitem].type--; if (element[currentitem].type < 0) element[currentitem].type = sizeof(drawelement)/sizeof(drawelement[0])-1; } else if (key == 'w') { element[currentitem].type++; if (element[currentitem].type >= sizeof(drawelement)/sizeof(drawelement[0])) element[currentitem].type = 0; } else if (key == ',' || key == K_MWHEELUP) { element[currentitem].subtype--; if (element[currentitem].subtype < 0) element[currentitem].subtype = drawelement[element[currentitem].type].maxsubtype; } else if (key == '.' || key == K_MWHEELDOWN) { element[currentitem].subtype++; if (element[currentitem].subtype > drawelement[element[currentitem].type].maxsubtype) element[currentitem].subtype = 0; } else if (key == K_UPARROW) { element[currentitem].y-=shiftdown?8:1; } else if (key == K_DOWNARROW) { element[currentitem].y+=shiftdown?8:1; } else if (key == K_LEFTARROW) { element[currentitem].x-=shiftdown?8:1; } else if (key == K_RIGHTARROW) { element[currentitem].x+=shiftdown?8:1; } else if (key == K_HOME) { element[currentitem].scalex=1.0f; element[currentitem].scaley=1.0f; element[currentitem].alpha=1.0f; } else if (key == '+') { element[currentitem].scalex*=1.1f; element[currentitem].scaley*=1.1f; } else if (key == '-') { element[currentitem].scalex/=1.1f; element[currentitem].scaley/=1.1f; } } } int Plug_MenuEvent(int *args) { int altargs[5]; args[2]=(int)(args[2]*640.0f/vid.width); args[3]=(int)(args[3]*480.0f/vid.height); switch(args[0]) { case 0: //draw // TODO: some sort of element property display if (context) { } else if (mousedown) { if (mousedown & 2) //2 is 'on the scaler' { float w = args[2] - mouseofsx; float h = args[3] - mouseofsy; if (shiftdown || (mousedown & 4)) //4 is mouse2 { w -= (int)w & 7; h -= (int)h & 7; } if (w < 8 && w > -8) w = 8; if (h < 8 && h > -8) h = 8; element[currentitem].scalex = w/drawelement[element[currentitem].type].width; element[currentitem].scaley = h/drawelement[element[currentitem].type].height; } else { element[currentitem].x = args[2] - mouseofsx; element[currentitem].y = args[3] - mouseofsy; if (shiftdown || (mousedown & 4)) //4 is mouse2 { element[currentitem].x -= (int)element[currentitem].x & 7; element[currentitem].y -= (int)element[currentitem].y & 7; } } } else hoveritem = FindItemUnderMouse(args[2], args[3]); // this could possibly slow some things down... altargs[0] = 0; altargs[1] = 0; altargs[2] = 0; altargs[3] = vid.width; altargs[4] = vid.height; if (hudedit) UI_StatusBarEdit(altargs); else UI_StatusBar(altargs); //draw it using the same function (we're lazy) UI_DrawHandles(altargs, currentitem); if (context) { sbarscalex = vid.width/640.0f; sbarscaley = vid.height/480.0f; DrawContextMenu(args[2], args[3]); } break; case 1: //keydown UI_KeyPress(args[1], args[2], args[3]); break; case 2: //keyup if (args[1] == K_MOUSE1) mousedown = false; else if (args[1] == K_SHIFT) shiftdown = false; break; case 3: //menu closed (this is called even if we change it). hudedit = false; break; case 4: //mousemove break; } return 0; } int Plug_Tick(int *args) { currenttime = args[0]; return true; } int Plug_ExecuteCommand(int *args) { char cmd[256]; Cmd_Argv(0, cmd, sizeof(cmd)); if (!strcmp("sbar_edit", cmd)) { Menu_Control(1); mousedown=false; hudedit=true; return 1; } if (!strcmp("sbar_save", cmd)) { Cmd_Argv(1, cmd, sizeof(cmd)); Hud_Save(cmd); mousedown=false; return 1; } if (!strcmp("sbar_load", cmd)) { Cmd_Argv(1, cmd, sizeof(cmd)); Hud_Load(cmd); mousedown=false; return 1; } if (!strcmp("sbar_defaults", cmd)) { Cmd_Argv(1, cmd, sizeof(cmd)); SBar_FlushAll(); SBar_ReloadDefaults(); mousedown=false; return 1; } return 0; } int Plug_Init(int *args) { if (Plug_Export("Tick", Plug_Tick) && Plug_Export("SbarBase", UI_StatusBar) && Plug_Export("SbarOverlay", UI_ScoreBoard) && Plug_Export("ExecuteCommand", Plug_ExecuteCommand) && Plug_Export("MenuEvent", Plug_MenuEvent)) { K_UPARROW = Key_GetKeyCode("uparrow"); K_DOWNARROW = Key_GetKeyCode("downarrow"); K_LEFTARROW = Key_GetKeyCode("leftarrow"); K_RIGHTARROW = Key_GetKeyCode("rightarrow"); K_ESCAPE = Key_GetKeyCode("escape"); K_HOME = Key_GetKeyCode("home"); K_MOUSE1 = Key_GetKeyCode("mouse1"); K_MOUSE2 = Key_GetKeyCode("mouse2"); K_MWHEELDOWN = Key_GetKeyCode("mwheeldown"); K_MWHEELUP = Key_GetKeyCode("mwheelup"); K_SHIFT = Key_GetKeyCode("shift"); K_PAGEUP = Key_GetKeyCode("pgup"); K_PAGEDOWN = Key_GetKeyCode("pgdn"); Cmd_AddCommand("sbar_edit"); if (BUILTINISVALID(FS_Write)) Cmd_AddCommand("sbar_save"); if (BUILTINISVALID(FS_Read)) Cmd_AddCommand("sbar_load"); Cmd_AddCommand("sbar_defaults"); UI_SbarInit(); if (BUILTINISVALID(FS_Read)) Hud_Load(""); return 1; } return 0; }