summaryrefslogtreecommitdiff
path: root/src/fpsgame/scoreboard.cpp
diff options
context:
space:
mode:
authorxolatile2025-07-16 23:07:43 +0200
committerxolatile2025-07-16 23:07:43 +0200
commit7256502afa0babe60fcafbd2888cd3e33c3f9b6b (patch)
tree8a8495662a69bdadc4b5d9152656b9f02a44d668 /src/fpsgame/scoreboard.cpp
parentbc596ac9d4cdd00abf537b88d3c544be161330cc (diff)
downloadxolatile-badassbug-7256502afa0babe60fcafbd2888cd3e33c3f9b6b.tar.xz
xolatile-badassbug-7256502afa0babe60fcafbd2888cd3e33c3f9b6b.tar.zst
Source code, broken...
Diffstat (limited to 'src/fpsgame/scoreboard.cpp')
-rw-r--r--src/fpsgame/scoreboard.cpp559
1 files changed, 559 insertions, 0 deletions
diff --git a/src/fpsgame/scoreboard.cpp b/src/fpsgame/scoreboard.cpp
new file mode 100644
index 0000000..ec99dda
--- /dev/null
+++ b/src/fpsgame/scoreboard.cpp
@@ -0,0 +1,559 @@
+// creation of scoreboard
+#include "game.h"
+
+namespace game
+{
+ VARP(scoreboard2d, 0, 1, 1);
+ VARP(showservinfo, 0, 1, 1);
+ VARP(showclientnum, 0, 0, 1);
+ VARP(showpj, 0, 0, 1);
+ VARP(showping, 0, 1, 2);
+ VARP(showspectators, 0, 1, 1);
+ VARP(showspectatorping, 0, 0, 1);
+ VARP(highlightscore, 0, 1, 1);
+ VARP(showconnecting, 0, 0, 1);
+ VARP(hidefrags, 0, 1, 1);
+ VARP(showdeaths, 0, 0, 1);
+
+ static hashset<teaminfo> 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(m_ctf || m_collect)
+ {
+ if(a->flags > b->flags) return true;
+ if(a->flags < b->flags) return false;
+ }
+ if(a->frags > b->frags) return true;
+ if(a->frags < b->frags) return false;
+ return strcmp(a->name, b->name) < 0;
+ }
+
+ void getbestplayers(vector<fpsent *> &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<const char *> &best)
+ {
+ if(cmode && cmode->hidefrags())
+ {
+ vector<teamscore> teamscores;
+ cmode->getteamscores(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<fpsent *> players;
+ };
+ static vector<scoregroup *> groups;
+ static vector<fpsent *> 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 if(cmode && cmode->hidefrags()) g.score = cmode->getteamscore(o->team);
+ 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)
+ {
+ 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(!cmode || !cmode->hidefrags() || !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();
+ }
+
+ 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+1<numgroups && (k+1)%2) g.space(2);
+ else g.poplist(); // horizontal
+ }
+
+ if(showspectators && spectators.length())
+ {
+ if(showclientnum || player1->privilege>=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+1<spectators.length() && (i+1)%3) g.space(1);
+ else g.poplist();
+ }
+ }
+ }
+ }
+
+ struct scoreboardgui : g3d_callback
+ {
+ bool showing;
+ vec menupos;
+ int menustart;
+
+ scoreboardgui() : showing(false) {}
+
+ void show(bool on)
+ {
+ if(!showing && on)
+ {
+ menupos = menuinfrontofplayer();
+ menustart = starttime();
+ }
+ showing = on;
+ }
+
+ void gui(g3d_gui &g, bool firstpass)
+ {
+ g.start(menustart, 0.03f, NULL, false);
+ renderscoreboard(g, firstpass);
+ g.end();
+ }
+
+ void render()
+ {
+ if(showing) g3d_addgui(this, menupos, (scoreboard2d ? GUI_FORCE_2D : GUI_2D | GUI_FOLLOW) | GUI_BOTTOM);
+ }
+
+ } scoreboard;
+
+ void g3d_gamemenus()
+ {
+ scoreboard.render();
+ }
+
+ VARFN(scoreboard, showscoreboard, 0, 0, 1, scoreboard.show(showscoreboard!=0));
+
+ void showscores(bool on)
+ {
+ showscoreboard = on ? 1 : 0;
+ scoreboard.show(on);
+ }
+ ICOMMAND(showscores, "D", (int *down), showscores(*down!=0));
+
+ VARP(hudscore, 0, 0, 1);
+ FVARP(hudscorescale, 1e-3f, 1.0f, 1e3f);
+ VARP(hudscorealign, -1, 0, 1);
+ FVARP(hudscorex, 0, 0.50f, 1);
+ FVARP(hudscorey, 0, 0.03f, 1);
+ HVARP(hudscoreplayercolour, 0, 0x60A0FF, 0xFFFFFF);
+ HVARP(hudscoreenemycolour, 0, 0xFF4040, 0xFFFFFF);
+ VARP(hudscorealpha, 0, 255, 255);
+ VARP(hudscoresep, 0, 200, 1000);
+
+ void drawhudscore(int w, int h)
+ {
+ int numgroups = groupplayers();
+ if(!numgroups) return;
+
+ scoregroup *g = groups[0];
+ int score = INT_MIN, score2 = INT_MIN;
+ bool best = false;
+ if(m_teammode)
+ {
+ score = g->score;
+ 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();
+ }
+}
+