// creation of scoreboard #include "game.h" namespace game { VARP(scoreboard2d, 0, 1, 1); VARP(showservinfo, 0, 1, 1); VARP(showclientnum, 0, 1, 1); VARP(showpj, 0, 0, 1); VARP(showping, 0, 1, 2); VARP(showspectators, 0, 1, 1); VARP(showspectatorping, 0, 1, 1); VARP(highlightscore, 0, 1, 1); VARP(showconnecting, 0, 0, 1); VARP(hidefrags, 0, 0, 1); VARP(showdeaths, 0, 1, 1); VARP(showdamagedealt, 0, 1, 1); static hashset teaminfos; void clearteaminfo() { teaminfos.clear(); } void setteaminfo(const char *team, int frags) { teaminfo *t = teaminfos.access(team); if(!t) { t = &teaminfos[team]; copystring(t->team, team, sizeof(t->team)); } t->frags = frags; } static inline bool playersort(const fpsent *a, const fpsent *b) { if(a->state==CS_SPECTATOR) { if(b->state==CS_SPECTATOR) return strcmp(a->name, b->name) < 0; else return false; } else if(b->state==CS_SPECTATOR) return true; if(a->frags > b->frags) return true; if(a->frags < b->frags) return false; return strcmp(a->name, b->name) < 0; } void getbestplayers(vector &best) { loopv(players) { fpsent *o = players[i]; if(o->state!=CS_SPECTATOR) best.add(o); } best.sort(playersort); while(best.length() > 1 && best.last()->frags < best[0]->frags) best.drop(); } void getbestteams(vector &best) { if(!hidefrags) { vector teamscores; teamscores.sort(teamscore::compare); while(teamscores.length() > 1 && teamscores.last().score < teamscores[0].score) teamscores.drop(); loopv(teamscores) best.add(teamscores[i].team); } else { int bestfrags = INT_MIN; enumerate(teaminfos, teaminfo, t, bestfrags = max(bestfrags, t.frags)); if(bestfrags <= 0) loopv(players) { fpsent *o = players[i]; if(o->state!=CS_SPECTATOR && !teaminfos.access(o->team) && best.htfind(o->team) < 0) { bestfrags = 0; best.add(o->team); } } enumerate(teaminfos, teaminfo, t, if(t.frags >= bestfrags) best.add(t.team)); } } struct scoregroup : teamscore { vector players; }; static vector groups; static vector spectators; static inline bool scoregroupcmp(const scoregroup *x, const scoregroup *y) { if(!x->team) { if(y->team) return false; } else if(!y->team) return true; if(x->score > y->score) return true; if(x->score < y->score) return false; if(x->players.length() > y->players.length()) return true; if(x->players.length() < y->players.length()) return false; return x->team && y->team && strcmp(x->team, y->team) < 0; } static int groupplayers() { int numgroups = 0; spectators.setsize(0); loopv(players) { fpsent *o = players[i]; if(!showconnecting && !o->name[0]) continue; if(o->state==CS_SPECTATOR) { spectators.add(o); continue; } const char *team = m_teammode && o->team[0] ? o->team : NULL; bool found = false; loopj(numgroups) { scoregroup &g = *groups[j]; if(team!=g.team && (!team || !g.team || strcmp(team, g.team))) continue; g.players.add(o); found = true; } if(found) continue; if(numgroups>=groups.length()) groups.add(new scoregroup); scoregroup &g = *groups[numgroups++]; g.team = team; if(!team) g.score = 0; else { teaminfo *ti = teaminfos.access(team); g.score = ti ? ti->frags : 0; } g.players.setsize(0); g.players.add(o); } loopi(numgroups) groups[i]->players.sort(playersort); spectators.sort(playersort); groups.sort(scoregroupcmp, 0, numgroups); return numgroups; } int statuscolor(fpsent *d, int color) { if(d->privilege) { color = d->privilege>=PRIV_ADMIN ? 0xFF8000 : (d->privilege>=PRIV_AUTH ? 0xC040C0 : 0x40FF80); if(d->state==CS_DEAD) color = (color>>1)&0x7F7F7F; } else if(d->state==CS_DEAD) color = 0x606060; return color; } void renderscoreboard(g3d_gui &g, bool firstpass) { (void) firstpass; const ENetAddress *address = connectedpeer(); if(showservinfo && address) { string hostname; if(enet_address_get_host_ip(address, hostname, sizeof(hostname)) >= 0) { if(servinfo[0]) g.titlef("%.25s", 0xFFFF80, NULL, servinfo); else g.titlef("%s:%d", 0xFFFF80, NULL, hostname, address->port); } } g.pushlist(); g.spring(); g.text(server::modename(gamemode), 0xFFFF80); g.separator(); const char *mname = getclientmap(); g.text(mname[0] ? mname : "[new map]", 0xFFFF80); extern int gamespeed; if(gamespeed != 100) { g.separator(); g.textf("%d.%02dx", 0xFFFF80, NULL, gamespeed/100, gamespeed%100); } if(m_timed && mname[0] && (maplimit >= 0 || intermission)) { g.separator(); if(intermission) g.text("intermission", 0xFFFF80); else { int secs = max(maplimit-lastmillis+999, 0)/1000, mins = secs/60; secs %= 60; g.pushlist(); g.strut(mins >= 10 ? 4.5f : 3.5f); g.textf("%d:%02d", 0xFFFF80, NULL, mins, secs); g.poplist(); } } if(ispaused()) { g.separator(); g.text("paused", 0xFFFF80); } g.spring(); g.poplist(); g.separator(); int numgroups = groupplayers(); loopk(numgroups) { if((k%2)==0) g.pushlist(); // horizontal scoregroup &sg = *groups[k]; int bgcolor = sg.team && m_teammode ? (isteam(player1->team, sg.team) ? 0x3030C0 : 0xC03030) : 0, fgcolor = 0xFFFF80; g.pushlist(); // vertical g.pushlist(); // horizontal #define loopscoregroup(o, b) \ loopv(sg.players) { \ \ fpsent *o = sg.players[i]; \ b; \ } g.pushlist(); if(sg.team && m_teammode) { g.pushlist(); g.background(bgcolor, numgroups>1 ? 3 : 5); g.strut(1); g.poplist(); } g.text("", 0, " "); loopscoregroup(o, { if(o==player1 && highlightscore && (multiplayer(false) || demoplayback || players.length() > 1)) { g.pushlist(); g.background(0x808080, numgroups>1 ? 3 : 5); } const playermodelinfo &mdl = getplayermodelinfo(o); const char *icon = sg.team && m_teammode ? (isteam(player1->team, sg.team) ? mdl.blueicon : mdl.redicon) : mdl.ffaicon; g.text("", 0, icon); if(o==player1 && highlightscore && (multiplayer(false) || demoplayback || players.length() > 1)) g.poplist(); }); g.poplist(); if(sg.team && m_teammode) { g.pushlist(); // vertical if(sg.score>=10000) g.textf("%s: WIN", fgcolor, NULL, sg.team); else g.textf("%s: %d", fgcolor, NULL, sg.team, sg.score); g.pushlist(); // horizontal } if(!hidefrags) { g.pushlist(); g.strut(6); g.text("frags", fgcolor); loopscoregroup(o, g.textf("%d", 0xFFFFDD, NULL, o->frags)); g.poplist(); } if(showdeaths) { g.pushlist(); g.strut(6); g.text("deaths", fgcolor); loopscoregroup(o, g.textf("%d", 0xFFFFDD, NULL, o->deaths)); g.poplist(); } if(showdamagedealt) { g.pushlist(); g.strut(7); g.text("damage", fgcolor); loopscoregroup(o, g.textf("%d", 0xFFFFDD, NULL, o->totaldamage)); g.poplist(); } g.pushlist(); g.text("name", fgcolor); g.strut(12); loopscoregroup(o, { g.textf("%s ", statuscolor(o, 0xFFFFDD), NULL, colorname(o)); }); g.poplist(); if(multiplayer(false) || demoplayback) { if(showpj || showping) g.space(1); if(showpj && showping <= 1) { g.pushlist(); g.strut(6); g.text("pj", fgcolor); loopscoregroup(o, { if(o->state==CS_LAGGED) g.text("LAG", 0xFFFFDD); else g.textf("%d", 0xFFFFDD, NULL, o->plag); }); g.poplist(); } if(showping > 1) { g.pushlist(); g.strut(6); g.pushlist(); g.text("ping", fgcolor); g.space(1); g.spring(); g.text("pj", fgcolor); g.poplist(); loopscoregroup(o, { fpsent *p = o->ownernum >= 0 ? getclient(o->ownernum) : o; if(!p) p = o; g.pushlist(); if(p->state==CS_LAGGED) g.text("LAG", 0xFFFFDD); else { g.textf("%d", 0xFFFFDD, NULL, p->ping); g.space(1); g.spring(); g.textf("%d", 0xFFFFDD, NULL, o->plag); } g.poplist(); }); g.poplist(); } else if(showping) { g.pushlist(); g.text("ping", fgcolor); g.strut(6); loopscoregroup(o, { fpsent *p = o->ownernum >= 0 ? getclient(o->ownernum) : o; if(!p) p = o; if(!showpj && p->state==CS_LAGGED) g.text("LAG", 0xFFFFDD); else g.textf("%d", 0xFFFFDD, NULL, p->ping); }); g.poplist(); } } if(showclientnum || player1->privilege>=PRIV_MASTER) { g.space(1); g.pushlist(); g.text("cn", fgcolor); loopscoregroup(o, g.textf("%d", 0xFFFFDD, NULL, o->clientnum)); g.poplist(); } if(sg.team && m_teammode) { g.poplist(); // horizontal g.poplist(); // vertical } g.poplist(); // horizontal g.poplist(); // vertical if(k+1privilege>=PRIV_MASTER) { g.pushlist(); g.pushlist(); g.text("spectator", 0xFFFF80, " "); g.strut(12); loopv(spectators) { fpsent *o = spectators[i]; if(o==player1 && highlightscore) { g.pushlist(); g.background(0x808080, 3); } g.text(colorname(o), statuscolor(o, 0xFFFFDD), "spectator"); if(o==player1 && highlightscore) g.poplist(); } g.poplist(); if((multiplayer(false) || demoplayback) && showspectatorping) { g.space(1); g.pushlist(); g.text("ping", 0xFFFF80); g.strut(6); loopv(spectators) { fpsent *o = spectators[i]; fpsent *p = o->ownernum >= 0 ? getclient(o->ownernum) : o; if(!p) p = o; if(p->state==CS_LAGGED) g.text("LAG", 0xFFFFDD); else g.textf("%d", 0xFFFFDD, NULL, p->ping); } g.poplist(); } g.space(1); g.pushlist(); g.text("cn", 0xFFFF80); loopv(spectators) g.textf("%d", 0xFFFFDD, NULL, spectators[i]->clientnum); g.poplist(); g.poplist(); } else { g.textf("%d spectator%s", 0xFFFF80, " ", spectators.length(), spectators.length()!=1 ? "s" : ""); loopv(spectators) { if((i%3)==0) { g.pushlist(); g.text("", 0xFFFFDD, "spectator"); } fpsent *o = spectators[i]; if(o==player1 && highlightscore) { g.pushlist(); g.background(0x808080); } g.text(colorname(o), statuscolor(o, 0xFFFFDD)); if(o==player1 && highlightscore) g.poplist(); if(i+1score; best = isteam(player1->team, g->team); if(numgroups > 1) { if(best) score2 = groups[1]->score; else for(int i = 1; i < groups.length(); ++i) if(isteam(player1->team, groups[i]->team)) { score2 = groups[i]->score; break; } if(score2 == INT_MIN) { fpsent *p = followingplayer(player1); if(p->state==CS_SPECTATOR) score2 = groups[1]->score; } } } else { fpsent *p = followingplayer(player1); score = g->players[0]->frags; best = p == g->players[0]; if(g->players.length() > 1) { if(best || p->state==CS_SPECTATOR) score2 = g->players[1]->frags; else score2 = p->frags; } } if(score == score2 && !best) best = true; score = clamp(score, -999, 9999); defformatstring(buf, "%d", score); int tw = 0, th = 0; text_bounds(buf, tw, th); string buf2; int tw2 = 0, th2 = 0; if(score2 > INT_MIN) { score2 = clamp(score2, -999, 9999); formatstring(buf2, "%d", score2); text_bounds(buf2, tw2, th2); } int fw = 0, fh = 0; text_bounds("00", fw, fh); fw = max(fw, max(tw, tw2)); vec2 offset = vec2(hudscorex, hudscorey).mul(vec2(w, h).div(hudscorescale)); if(hudscorealign == 1) offset.x -= 2*fw + hudscoresep; else if(hudscorealign == 0) offset.x -= (2*fw + hudscoresep) / 2.0f; vec2 offset2 = offset; offset.x += (fw-tw)/2.0f; offset.y -= th/2.0f; offset2.x += fw + hudscoresep + (fw-tw2)/2.0f; offset2.y -= th2/2.0f; pushhudmatrix(); hudmatrix.scale(hudscorescale, hudscorescale, 1); flushhudmatrix(); int color = hudscoreplayercolour, color2 = hudscoreenemycolour; if(!best) swap(color, color2); draw_text(buf, int(offset.x), int(offset.y), (color>>16)&0xFF, (color>>8)&0xFF, color&0xFF, hudscorealpha); if(score2 > INT_MIN) draw_text(buf2, int(offset2.x), int(offset2.y), (color2>>16)&0xFF, (color2>>8)&0xFF, color2&0xFF, hudscorealpha); pophudmatrix(); } }