#include "game.h" namespace game { bool intermission = false; int maptime = 0, maprealtime = 0, maplimit = -1; int respawnent = -1; int lasthit = 0, lastspawnattempt = 0; int following = -1, followdir = 0; fpsent *player1 = NULL; // our client vector players; // other clients int savedammo[NUMGUNS]; bool clientoption(const char *arg) { return false; } void taunt() { if(player1->state!=CS_ALIVE || player1->physstatelasttaunt<1000) return; player1->lasttaunt = lastmillis; addmsg(N_TAUNT, "rc", player1); } COMMAND(taunt, ""); ICOMMAND(getfollow, "", (), { fpsent *f = followingplayer(); intret(f ? f->clientnum : -1); }); void follow(char *arg) { if(arg[0] ? player1->state==CS_SPECTATOR : following>=0) { following = arg[0] ? parseplayer(arg) : -1; if(following==player1->clientnum) following = -1; followdir = 0; conoutf("follow %s", following>=0 ? "on" : "off"); } } COMMAND(follow, "s"); void nextfollow(int dir) { if(player1->state!=CS_SPECTATOR || clients.empty()) { stopfollowing(); return; } int cur = following >= 0 ? following : (dir < 0 ? clients.length() - 1 : 0); loopv(clients) { cur = (cur + dir + clients.length()) % clients.length(); if(clients[cur] && clients[cur]->state!=CS_SPECTATOR) { if(following<0) conoutf("follow on"); following = cur; followdir = dir; return; } } stopfollowing(); } ICOMMAND(nextfollow, "i", (int *dir), nextfollow(*dir < 0 ? -1 : 1)); const char *getclientmap() { return clientmap; } void resetgamestate() { clearprojectiles(); clearbouncers(); } fpsent *spawnstate(fpsent *d) // reset player state not persistent accross spawns { d->respawn(); d->spawnstate(gamemode); return d; } void respawnself() { if(ispaused()) return; if(m_mp(gamemode)) { int seq = (player1->lifesequence<<16)|((lastmillis/1000)&0xFFFF); if(player1->respawned!=seq) { addmsg(N_TRYSPAWN, "rc", player1); player1->respawned = seq; } } else { spawnplayer(player1); showscores(false); lasthit = 0; if(cmode) cmode->respawned(player1); } } fpsent *pointatplayer() { loopv(players) if(players[i] != player1 && intersect(players[i], player1->o, worldpos)) return players[i]; return NULL; } void stopfollowing() { if(following<0) return; following = -1; followdir = 0; conoutf("follow off"); } fpsent *followingplayer(fpsent *fallback) { if(player1->state!=CS_SPECTATOR || following<0) return fallback; fpsent *target = getclient(following); if(target && target->state!=CS_SPECTATOR) return target; return fallback; } fpsent *hudplayer() { if(thirdperson && allowthirdperson()) return player1; return followingplayer(player1); } void setupcamera() { fpsent *target = followingplayer(); if(target) { player1->yaw = target->yaw; player1->pitch = target->state==CS_DEAD ? 0 : target->pitch; player1->o = target->o; player1->resetinterp(); } } bool allowthirdperson(bool msg) { return player1->state==CS_SPECTATOR || player1->state==CS_EDITING || m_edit || !multiplayer(msg); } ICOMMAND(allowthirdperson, "b", (int *msg), intret(allowthirdperson(*msg!=0) ? 1 : 0)); bool detachcamera() { fpsent *d = hudplayer(); return d->state==CS_DEAD; } bool collidecamera() { switch(player1->state) { case CS_EDITING: return false; case CS_SPECTATOR: return followingplayer()!=NULL; } return true; } VARP(smoothmove, 0, 75, 100); VARP(smoothdist, 0, 32, 64); void predictplayer(fpsent *d, bool move) { d->o = d->newpos; d->yaw = d->newyaw; d->pitch = d->newpitch; d->roll = d->newroll; if(move) { moveplayer(d, 1, false); d->newpos = d->o; } float k = 1.0f - float(lastmillis - d->smoothmillis)/smoothmove; if(k>0) { d->o.add(vec(d->deltapos).mul(k)); d->yaw += d->deltayaw*k; if(d->yaw<0) d->yaw += 360; else if(d->yaw>=360) d->yaw -= 360; d->pitch += d->deltapitch*k; d->roll += d->deltaroll*k; } } void otherplayers(int curtime) { loopv(players) { fpsent *d = players[i]; if(d == player1 || d->ai) continue; if(d->state==CS_DEAD && d->ragdoll) moveragdoll(d); else if(!intermission) { if(lastmillis - d->lastaction >= d->gunwait) d->gunwait = 0; if(d->quadmillis) entities::checkquad(curtime, d); } const int lagtime = totalmillis-d->lastupdate; if(!lagtime || intermission) continue; else if(lagtime>1000 && d->state==CS_ALIVE) { d->state = CS_LAGGED; continue; } if(d->state==CS_ALIVE || d->state==CS_EDITING) { if(smoothmove && d->smoothmillis>0) predictplayer(d, true); else moveplayer(d, 1, false); } else if(d->state==CS_DEAD && !d->ragdoll && lastmillis-d->lastpain<2000) moveplayer(d, 1, true); } } void checkslowmo() { static int lastslowmohealth = 0; server::forcegamespeed(intermission ? 100 : clamp(player1->health, 25, 200)); if(player1->healthmaxhealth && lastmillis-max(maptime, lastslowmohealth)>player1->health*player1->health/2) { lastslowmohealth = lastmillis; player1->health++; } } void updateworld() // main game update loop { if(!maptime) { maptime = lastmillis; maprealtime = totalmillis; return; } if(!curtime) { gets2c(); if(player1->clientnum>=0) c2sinfo(); return; } physicsframe(); ai::navigate(); if(player1->state != CS_DEAD && !intermission) { if(player1->quadmillis) entities::checkquad(curtime, player1); } updateweapons(curtime); otherplayers(curtime); ai::update(); moveragdolls(); gets2c(); if(connected) { if(player1->state == CS_DEAD) { if(player1->ragdoll) moveragdoll(player1); else if(lastmillis-player1->lastpain<2000) { player1->move = player1->strafe = 0; moveplayer(player1, 10, true); } } else if(!intermission) { if(player1->ragdoll) cleanragdoll(player1); moveplayer(player1, 10, true); swayhudgun(curtime); entities::checkitems(player1); if(cmode) cmode->checkitems(player1); } } if(player1->clientnum>=0) c2sinfo(); // do this last, to reduce the effective frame lag } float proximityscore(float x, float lower, float upper) { if(x <= lower) return 1.0f; if(x >= upper) return 0.0f; float a = x - lower, b = x - upper; return (b * b) / (a * a + b * b); } static inline float harmonicmean(float a, float b) { return a + b > 0 ? 2 * a * b / (a + b) : 0.0f; } // avoid spawning near other players float ratespawn(dynent *d, const extentity &e) { fpsent *p = (fpsent *)d; vec loc = vec(e.o).addz(p->eyeheight); float maxrange = !m_noitems ? 400.0f : (cmode ? 300.0f : 110.0f); float minplayerdist = maxrange; loopv(players) { const fpsent *o = players[i]; if(o == p) { if(m_noitems || (o->state != CS_ALIVE && lastmillis - o->lastpain > 3000)) continue; } else if(o->state != CS_ALIVE || isteam(o->team, p->team)) continue; vec dir = vec(o->o).sub(loc); float dist = dir.squaredlen(); if(dist >= minplayerdist*minplayerdist) continue; dist = sqrtf(dist); dir.mul(1/dist); // scale actual distance if not in line of sight if(raycube(loc, dir, dist) < dist) dist *= 1.5f; minplayerdist = min(minplayerdist, dist); } float rating = 1.0f - proximityscore(minplayerdist, 80.0f, maxrange); return cmode ? harmonicmean(rating, cmode->ratespawn(p, e)) : rating; } void pickgamespawn(fpsent *d) { int ent = d == player1 && respawnent >= 0 ? respawnent : -1; int tag = cmode ? cmode->getspawngroup(d) : 0; findplayerspawn(d, ent, tag); } void spawnplayer(fpsent *d) // place at random spawn { pickgamespawn(d); spawnstate(d); if(d==player1) { if(editmode) d->state = CS_EDITING; else if(d->state != CS_SPECTATOR) d->state = CS_ALIVE; } else d->state = CS_ALIVE; } VARP(spawnwait, 0, 0, 1000); void respawn() { if(player1->state==CS_DEAD) { player1->attacking = false; int wait = cmode ? cmode->respawnwait(player1) : 0; if(wait>0) { lastspawnattempt = lastmillis; //conoutf(CON_GAMEINFO, "\f2you must wait %d second%s before respawn!", wait, wait!=1 ? "s" : ""); return; } if(lastmillis < player1->lastpain + spawnwait) return; respawnself(); } } COMMAND(respawn, ""); // inputs VARP(attackspawn, 0, 1, 1); void doattack(bool on) { if(!connected || intermission) return; if((player1->attacking = on) && attackspawn) respawn(); } VARP(jumpspawn, 0, 1, 1); bool canjump() { if(!connected || intermission) return false; if(jumpspawn) respawn(); return player1->state!=CS_DEAD; } bool allowmove(physent *d) { if(d->type!=ENT_PLAYER) return true; return !((fpsent *)d)->lasttaunt || lastmillis-((fpsent *)d)->lasttaunt>=1000; } VARP(hitsound, 0, 0, 1); void damaged(int damage, fpsent *d, fpsent *actor, bool local) { if((d->state!=CS_ALIVE && d->state != CS_LAGGED && d->state != CS_SPAWNING) || intermission) return; if(local) damage = d->dodamage(damage); else if(actor==player1) return; fpsent *h = hudplayer(); if(h!=player1 && actor==h && d!=actor) { if(hitsound && lasthit != lastmillis) playsound(S_HIT); lasthit = lastmillis; } if(d==h) { damageblend(damage); damagecompass(damage, actor->o); } damageeffect(damage, d, d!=h); ai::damaged(d, actor); if(d->health<=0) { if(local) killed(d, actor); } else if(d==h) playsound(S_PAIN6); else playsound(S_PAIN1+rnd(5), &d->o); } VARP(deathscore, 0, 1, 1); void deathstate(fpsent *d, bool restore) { d->state = CS_DEAD; d->lastpain = lastmillis; if(!restore) d->deaths++; //~{ //~gibeffect(max(-d->health, 0), d->vel, d); //~d->deaths++; //~} if(d==player1) { if(deathscore) showscores(true); disablezoom(); if(!restore) loopi(NUMGUNS) savedammo[i] = player1->ammo[i]; d->attacking = false; //d->pitch = 0; d->roll = 0; playsound(S_DIE1+rnd(2)); } else { d->move = d->strafe = 0; d->resetinterp(); d->smoothmillis = 0; playsound(S_DIE1+rnd(2), &d->o); } } VARP(teamcolorfrags, 0, 1, 1); /// xolatile: HUD frag messages #define fragmessageduration (2000) string hudfragger, hudfragged; int hudfraggun, hudfragmillis; void sethudfragdata(char *fragger, char *fragged, int gunid) { copystring(hudfragger, fragger ? fragger : ""); copystring(hudfragged, fragged); hudfraggun = gunid; hudfragmillis = lastmillis; } void killed(fpsent *d, fpsent *actor) { if(d->state==CS_EDITING) { d->editstate = CS_DEAD; d->deaths++; if(d!=player1) d->resetinterp(); return; } else if((d->state!=CS_ALIVE && d->state != CS_LAGGED && d->state != CS_SPAWNING) || intermission) return; if(cmode) cmode->died(d, actor); fpsent *h = followingplayer(player1); int contype = d==h || actor==h ? CON_FRAG_SELF : CON_FRAG_OTHER; const char *dname = "", *aname = ""; if(m_teammode && teamcolorfrags) { dname = teamcolorname(d, "you"); aname = teamcolorname(actor, "you"); } else { dname = colorname(d, NULL, "", "", "you"); aname = colorname(actor, NULL, "", "", "you"); } if(actor->type==ENT_AI) conoutf(contype, "\f2%s got killed by %s!", dname, aname); else if(d==actor || actor->type==ENT_INANIMATE) conoutf(contype, "\f2%s suicided%s", dname, d==player1 ? "!" : ""); else if(isteam(d->team, actor->team)) { contype |= CON_TEAMKILL; if(actor==player1) conoutf(contype, "\f6%s fragged a teammate (%s)", aname, dname); else if(d==player1) conoutf(contype, "\f6%s got fragged by a teammate (%s)", dname, aname); else conoutf(contype, "\f2%s fragged a teammate (%s)", aname, dname); } else { if(d==player1) { conoutf(contype, "\f2%s got fragged by %s", dname, aname); sethudfragdata(actor->name, d->name, actor->gunselect); } else { conoutf(contype, "\f2%s fragged %s", aname, dname); sethudfragdata(actor->name, d->name, actor->gunselect); } } deathstate(d); ai::killed(d, actor); } void timeupdate(int secs) { server::timeupdate(secs); if(secs > 0) { maplimit = lastmillis + secs*1000; } else { intermission = true; player1->attacking = false; if(cmode) cmode->gameover(); conoutf(CON_GAMEINFO, "\f2intermission:"); conoutf(CON_GAMEINFO, "\f2game has ended!"); conoutf(CON_GAMEINFO, "\f2player frags: %d, deaths: %d", player1->frags, player1->deaths); int accuracy = (player1->totaldamage*100)/max(player1->totalshots, 1); conoutf(CON_GAMEINFO, "\f2player total damage dealt: %d, damage wasted: %d, accuracy(%%): %d", player1->totaldamage, player1->totalshots-player1->totaldamage, accuracy); showscores(true); disablezoom(); execident("intermission"); } } ICOMMAND(getfrags, "", (), intret(player1->frags)); ICOMMAND(getflags, "", (), intret(player1->flags)); ICOMMAND(getdeaths, "", (), intret(player1->deaths)); ICOMMAND(getaccuracy, "", (), intret((player1->totaldamage*100)/max(player1->totalshots, 1))); ICOMMAND(gettotaldamage, "", (), intret(player1->totaldamage)); ICOMMAND(gettotalshots, "", (), intret(player1->totalshots)); vector clients; fpsent *newclient(int cn) // ensure valid entity { if(cn < 0 || cn > max(0xFF, MAXCLIENTS + MAXBOTS)) { neterr("clientnum", false); return NULL; } if(cn == player1->clientnum) return player1; while(cn >= clients.length()) clients.add(NULL); if(!clients[cn]) { fpsent *d = new fpsent; d->clientnum = cn; clients[cn] = d; players.add(d); } return clients[cn]; } fpsent *getclient(int cn) // ensure valid entity { if(cn == player1->clientnum) return player1; return clients.inrange(cn) ? clients[cn] : NULL; } void clientdisconnected(int cn, bool notify) { if(!clients.inrange(cn)) return; if(following==cn) { if(followdir) nextfollow(followdir); else stopfollowing(); } unignore(cn); fpsent *d = clients[cn]; if(!d) return; if(notify && d->name[0]) conoutf("\f4leave:\f7 %s", colorname(d)); removeweapons(d); removetrackedparticles(d); removetrackeddynlights(d); if(cmode) cmode->removeplayer(d); players.removeobj(d); DELETEP(clients[cn]); cleardynentcache(); } void clearclients(bool notify) { loopv(clients) if(clients[i]) clientdisconnected(i, notify); } void initclient() { player1 = spawnstate(new fpsent); filtertext(player1->name, "Anonymous", false, false, MAXNAMELEN); players.add(player1); } VARP(showmodeinfo, 0, 1, 1); void startgame() { clearprojectiles(); clearbouncers(); clearragdolls(); clearteaminfo(); // reset perma-state loopv(players) { fpsent *d = players[i]; d->frags = d->flags = 0; d->deaths = 0; d->totaldamage = 0; d->totalshots = 0; d->maxhealth = 100; d->maxarmour = 50; d->lifesequence = -1; d->respawned = d->suicided = -2; } setclientmode(); intermission = false; maptime = maprealtime = 0; maplimit = -1; if(cmode) { cmode->preload(); cmode->setup(); } conoutf(CON_GAMEINFO, "\f2game mode is %s", server::modename(gamemode)); const char *info = m_valid(gamemode) ? gamemodes[gamemode - STARTGAMEMODE].info : NULL; if(showmodeinfo && info) conoutf(CON_GAMEINFO, "\f0%s", info); showscores(false); disablezoom(); lasthit = 0; execident("mapstart"); } void loadingmap(const char *name) { execident("playsong"); } void startmap(const char *name) // called just after a map load { pwreset(); ai::savewaypoints(); ai::clearwaypoints(true); respawnent = -1; // so we don't respawn at an old spot if(!m_mp(gamemode)) spawnplayer(player1); else findplayerspawn(player1, -1); entities::resetspawns(); copystring(clientmap, name ? name : ""); sendmapinfo(); } const char *getmapinfo() { return showmodeinfo && m_valid(gamemode) ? gamemodes[gamemode - STARTGAMEMODE].info : NULL; } const char *getscreenshotinfo() { return server::modename(gamemode, NULL); } void physicstrigger(physent *d, bool local, int floorlevel, int waterlevel, int material) { if(d->type==ENT_INANIMATE) return; if (waterlevel>0) { if(material!=MAT_LAVA) playsound(S_SPLASH1, d==player1 ? NULL : &d->o); } else if(waterlevel<0) playsound(material==MAT_LAVA ? S_BURN : S_SPLASH2, d==player1 ? NULL : &d->o); if (floorlevel>0) { if(d==player1 || d->type!=ENT_PLAYER || ((fpsent *)d)->ai) msgsound(S_JUMP, d); } else if(floorlevel<0) { if(d==player1 || d->type!=ENT_PLAYER || ((fpsent *)d)->ai) msgsound(S_LAND, d); } } void dynentcollide(physent *d, physent *o, const vec &dir) { return; } void msgsound(int n, physent *d) { if(!d || d==player1) { addmsg(N_SOUND, "ci", d, n); playsound(n); } else { if(d->type==ENT_PLAYER && ((fpsent *)d)->ai) addmsg(N_SOUND, "ci", d, n); playsound(n, &d->o); } } int numdynents() { return players.length(); } dynent *iterdynents(int i) { if(iname; if(alt && d != player1 && !strcmp(name, alt)) return true; loopv(players) if(d!=players[i] && !strcmp(name, players[i]->name)) return true; return false; } static string cname[3]; static int cidx = 0; const char *colorname(fpsent *d, const char *name, const char *prefix, const char *suffix, const char *alt) { if(!name) name = alt && d == player1 ? alt : d->name; bool dup = !name[0] || duplicatename(d, name, alt) || d->aitype != AI_NONE; if(dup || prefix[0] || suffix[0]) { cidx = (cidx+1)%3; if(dup) formatstring(cname[cidx], d->aitype == AI_NONE ? "%s%s \fs\f5(%d)\fr%s" : "%s%s \fs\f5[%d]\fr%s", prefix, name, d->clientnum, suffix); else formatstring(cname[cidx], "%s%s%s", prefix, name, suffix); return cname[cidx]; } return name; } VARP(teamcolortext, 0, 1, 1); const char *teamcolorname(fpsent *d, const char *alt) { if(!teamcolortext || !m_teammode || d->state==CS_SPECTATOR) return colorname(d, NULL, "", "", alt); return colorname(d, NULL, isteam(d->team, player1->team) ? "\fs\f1" : "\fs\f3", "\fr", alt); } const char *teamcolor(const char *name, bool sameteam, const char *alt) { if(!teamcolortext || !m_teammode) return sameteam || !alt ? name : alt; cidx = (cidx+1)%3; formatstring(cname[cidx], sameteam ? "\fs\f1%s\fr" : "\fs\f3%s\fr", sameteam || !alt ? name : alt); return cname[cidx]; } const char *teamcolor(const char *name, const char *team, const char *alt) { return teamcolor(name, team && isteam(team, player1->team), alt); } VARP(teamsounds, 0, 1, 1); void teamsound(bool sameteam, int n, const vec *loc) { playsound(n, loc, NULL, teamsounds ? (m_teammode && sameteam ? SND_USE_ALT : SND_NO_ALT) : 0); } void teamsound(fpsent *d, int n, const vec *loc) { teamsound(isteam(d->team, player1->team), n, loc); } void suicide(physent *d) { if(d==player1 || (d->type==ENT_PLAYER && ((fpsent *)d)->ai)) { if(d->state!=CS_ALIVE) return; fpsent *pl = (fpsent *)d; if(!m_mp(gamemode)) killed(pl, pl); else { int seq = (pl->lifesequence<<16)|((lastmillis/1000)&0xFFFF); if(pl->suicided!=seq) { addmsg(N_SUICIDE, "rc", pl); pl->suicided = seq; } } } } ICOMMAND(suicide, "", (), suicide(player1)); void drawicon(int icon, float x, float y, float sz) { settexture("packages/hud/items.png"); float tsz = 0.25f, tx = tsz*(icon%4), ty = tsz*(icon/4); gle::defvertex(2); gle::deftexcoord0(); gle::begin(GL_TRIANGLE_STRIP); gle::attribf(x, y); gle::attribf(tx, ty); gle::attribf(x+sz, y); gle::attribf(tx+tsz, ty); gle::attribf(x, y+sz); gle::attribf(tx, ty+tsz); gle::attribf(x+sz, y+sz); gle::attribf(tx+tsz, ty+tsz); gle::end(); } float abovegameplayhud(int w, int h) { switch(hudplayer()->state) { case CS_EDITING: case CS_SPECTATOR: return 1; default: return 1650.0f/1800.0f; } } int ammohudup[3] = { GUN_CG, GUN_RL, GUN_GL }, ammohuddown[3] = { GUN_RIFLE, GUN_SG, GUN_PISTOL }, ammohudcycle[7] = { -1, -1, -1, -1, -1, -1, -1 }; ICOMMAND(ammohudup, "V", (tagval *args, int numargs), { loopi(3) ammohudup[i] = i < numargs ? getweapon(args[i].getstr()) : -1; }); ICOMMAND(ammohuddown, "V", (tagval *args, int numargs), { loopi(3) ammohuddown[i] = i < numargs ? getweapon(args[i].getstr()) : -1; }); ICOMMAND(ammohudcycle, "V", (tagval *args, int numargs), { loopi(7) ammohudcycle[i] = i < numargs ? getweapon(args[i].getstr()) : -1; }); VARP(ammohud, 0, 1, 1); void drawammohud(fpsent *d) { float x = HICON_X + 2*HICON_STEP, y = HICON_Y, sz = HICON_SIZE; pushhudmatrix(); hudmatrix.scale(1/3.2f, 1/3.2f, 1); flushhudmatrix(); float xup = (x+sz)*3.2f, yup = y*3.2f + 0.1f*sz; loopi(3) { int gun = ammohudup[i]; if(gun < GUN_FIST || gun > GUN_PISTOL || gun == d->gunselect || !d->ammo[gun]) continue; drawicon(HICON_FIST+gun, xup, yup, sz); yup += sz; } float xdown = x*3.2f - sz, ydown = (y+sz)*3.2f - 0.1f*sz; loopi(3) { int gun = ammohuddown[3-i-1]; if(gun < GUN_FIST || gun > GUN_PISTOL || gun == d->gunselect || !d->ammo[gun]) continue; ydown -= sz; drawicon(HICON_FIST+gun, xdown, ydown, sz); } int offset = 0, num = 0; loopi(7) { int gun = ammohudcycle[i]; if(gun < GUN_FIST || gun > GUN_PISTOL) continue; if(gun == d->gunselect) offset = i + 1; else if(d->ammo[gun]) num++; } float xcycle = (x+sz/2)*3.2f + 0.5f*num*sz, ycycle = y*3.2f-sz; loopi(7) { int gun = ammohudcycle[(i + offset)%7]; if(gun < GUN_FIST || gun > GUN_PISTOL || gun == d->gunselect || !d->ammo[gun]) continue; xcycle -= sz; drawicon(HICON_FIST+gun, xcycle, ycycle, sz); } pophudmatrix(); } //~void drawspeedometer(fpsent *d, int w, int h) { //~int speedforreal = (int) (sqrtf(d->vel.squaredlen()) + 1.0f); //~speedforreal = (speedforreal == 1) ? 0 : speedforreal; //~vec colour = vec(255, 255, 255); //~float realw = 0; //~float realh = 0; //~if (speedometercolor) { //~if (speedforreal==0) colour = vec(60, 60, 60); //~else if (speedforreal>0 && speedforreal<=60) colour = vec(240, 30, 30); //~else if (speedforreal>60 && speedforreal<=120) colour = vec(180, 90, 60); //~else if (speedforreal>120 && speedforreal<=180) colour = vec(180, 180, 30); //~else if (speedforreal>180 && speedforreal<=240) colour = vec(90, 180, 60); //~else colour = vec(30, 240, 30); //~} //~defformatstring(speedstring, "%d", speedforreal); //~text_boundsf(speedstring, realw, realh); //~vec2 offset = vec2(speedometerx, speedometery).mul(vec2(w, h).div(speedometerscale)); //~offset.x -= realw/2.0f; //~offset.y -= realh/2.0f; //~pushhudmatrix(); //~hudmatrix.scale(speedometerscale, speedometerscale, 1); //~flushhudmatrix(); //~draw_text(speedstring, int(offset.x), int(offset.y), colour.x, colour.y, colour.z, speedometeralpha); //~pophudmatrix(); //~} void hudquad(float x, float y, float w, float h, float r = 1, float g = 1, float b = 1, float tx = 0, float ty = 0, float tw = 1, float th = 1) { gle::defvertex(2); gle::deftexcoord0(); gle::colorf(r, g, b); gle::begin(GL_TRIANGLE_STRIP); //~gle::begin(GL_QUADS); gle::attribf(x, y); gle::attribf(tx, ty); gle::attribf(x+w, y); gle::attribf(tx + tw, ty); gle::attribf(x, y+h); gle::attribf(tx, ty + th); gle::attribf(x+w, y+h); gle::attribf(tx + tw, ty + th); gle::end(); } VARP(healthcolors, 0, 1, 1); VARP(hudhealth, 0, 1, 1); FVARP(hudhealthx, 0, 0, 1); FVARP(hudhealthy, 0, 1, 1); FVARP(hudhealthscale, 0.1, 1.0, 1.0); void drawhudhealth(fpsent *d, int w, int h) { pushhudmatrix(); hudmatrix.scale(hudhealthscale, hudhealthscale, 1); flushhudmatrix(); bvec healthcolor = bvec::hexcolor(healthcolors && !m_insta ? (d->state==CS_DEAD ? 0x808080 : (d->health<=25 ? 0xc02020 : (d->health<=50 ? 0xc08020 : (d->health<=75 ? 0xc0c040 : (d->health<=100 ? 0xFFFFFF : 0x4080c0))))) : 0xFFFFFF); const float proportion = (w/4.0f)/600.0f; const float healthbarw = 600*proportion; const float healthbarh = 113*proportion; vec2 offset = vec2(hudhealthx, hudhealthy).mul(vec2(w-healthbarw, h-healthbarh).div(hudhealthscale)); settexture("packages/hud/health_bar_base.png"); float hp = (float)d->health/d->maxhealth; hudquad(offset.x, offset.y, hp*healthbarw, healthbarh, healthcolor.r, healthcolor.g, healthcolor.b, 0, 0, hp, 1); settexture("packages/hud/health_bar_over.png"); hudquad(offset.x, offset.y, healthbarw, healthbarh); if (d->quadmillis) { settexture("packages/hud/health_bar_quad.png"); hudquad(offset.x, offset.y, healthbarw, healthbarh); } defformatstring(health, "%d", d->state==CS_DEAD ? 0 : d->health); float tw=0, th=0; text_boundsf(health, tw, th); draw_text(health, offset.x+(125*proportion-tw)/2, offset.y+(healthbarh-th)/2, healthcolor.r, healthcolor.g, healthcolor.b); pophudmatrix(); } VARP(hudmaxhealth, 0, 1, 1); FVARP(hudmaxhealthx, 0, 0.207, 1); FVARP(hudmaxhealthy, 0, 0.97, 1); FVARP(hudmaxhealthscale, 0.1, 1.0, 1.0); void drawhudmaxhealth(fpsent *d, int w, int h) { pushhudmatrix(); hudmatrix.scale(hudmaxhealthscale, hudmaxhealthscale, 1); flushhudmatrix(); const float proportion = (w/15.0f)/160.0f; const float healthboostw = 160*proportion; const float healthboosth = 78*proportion; float hb = (float)d->maxhealth/100.0f-1.0f; vec2 offset = vec2(hudmaxhealthx, hudmaxhealthy).mul(vec2(w-healthboostw, h-healthboosth).div(hudhealthscale)); settexture("packages/hud/health_boost_base.png"); hudquad(offset.x, offset.y, hb*healthboostw, healthboosth, 0.3f, 0.6f, 0.9f, 0, 0, hb, 1); settexture("packages/hud/health_boost_over.png"); hudquad(offset.x, offset.y, healthboostw, healthboosth); if (d->quadmillis) { settexture("packages/hud/health_boost_quad.png"); hudquad(offset.x, offset.y, healthboostw, healthboosth); } pophudmatrix(); } VARP(armourcolors, 0, 1, 1); VARP(hudarmour, 0, 1, 1); FVARP(hudarmourx, 0, 1, 1); FVARP(hudarmoury, 0, 1, 1); FVARP(hudarmourscale, 0.1, 1.0, 1.0); void drawhudarmour(fpsent *d, int w, int h) { pushhudmatrix(); hudmatrix.scale(hudarmourscale, hudarmourscale, 1); flushhudmatrix(); bvec armourcolor = bvec::hexcolor(d->armourtype == A_BLUE ? 0x83ade5 : (d->armourtype == A_GREEN ? 0x77f29e : (d->armourtype == A_YELLOW ? 0xf5f19b : 0xffffff))); const float proportion = (w/4.0f)/600.0f; const float armourbarw = 600*proportion; const float armourbarh = 113*proportion; vec2 offset = vec2(hudarmourx, hudarmoury).mul(vec2(w-armourbarw, h-armourbarh).div(hudarmourscale)); settexture("packages/hud/armour_bar_base.png"); float ap = (float)d->armour/d->maxarmour; hudquad(offset.x, offset.y, ap*armourbarw, armourbarh, armourcolor.r, armourcolor.g, armourcolor.b, 0, 0, ap, 1); settexture("packages/hud/armour_bar_over.png"); hudquad(offset.x, offset.y, armourbarw, armourbarh); if (d->quadmillis) { settexture("packages/hud/armour_bar_quad.png"); hudquad(offset.x, offset.y, armourbarw, armourbarh); } defformatstring(armour, "%d", d->state==CS_DEAD ? 0 : d->armour); float tw=0, th=0; text_boundsf(armour, tw, th); draw_text(armour, offset.x+(2*(600-63)*proportion-tw)/2, offset.y+(armourbarh-th)/2, armourcolor.r, armourcolor.g, armourcolor.b); pophudmatrix(); } void drawhudicons(fpsent *d, int w, int h) { //~defformatstring(health, "%d", d->state==CS_DEAD ? 0 : d->health); //~bvec healthcolor = bvec::hexcolor(healthcolors && !m_insta ? (d->state==CS_DEAD ? 0x808080 : (d->health<=25 ? 0xFF0000 : (d->health<=50 ? 0xFF8000 : (d->health<=100 ? 0xFFFFFF : 0x40C0FF)))) : 0xFFFFFF); //~draw_text(health, (HICON_X + HICON_SIZE + HICON_SPACE)/2, HICON_TEXTY/2, healthcolor.r, healthcolor.g, healthcolor.b); //~if(d->state!=CS_DEAD) //~{ //~if(d->armour) draw_textf("%d", (HICON_X + HICON_STEP + HICON_SIZE + HICON_SPACE)/2, HICON_TEXTY/2, d->armour); //~draw_textf("%d", (HICON_X + 2*HICON_STEP + HICON_SIZE + HICON_SPACE)/2, HICON_TEXTY/2, d->ammo[d->gunselect]); //~} //~if(d->state != CS_DEAD && d->maxhealth > 100) //~{ //~float scale = 0.66f; //~pushhudmatrix(); //~hudmatrix.scale(scale, scale, 1); //~flushhudmatrix(); //~float width, height; //~text_boundsf(health, width, height); //~draw_textf("/%d", (HICON_X + HICON_SIZE + HICON_SPACE + width*2)/scale, (HICON_TEXTY + height)/scale, d->maxhealth); //~pophudmatrix(); //~} glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); if(hudhealth) drawhudhealth(d, w, h); if(hudmaxhealth) drawhudmaxhealth(d, w, h); if(hudarmour) drawhudarmour(d, w, h); if(d->state!=CS_DEAD) { drawicon(HICON_FIST+d->gunselect, HICON_X + 2*HICON_STEP, HICON_Y); if(ammohud) drawammohud(d); } } VARP(gameclock, 0, 0, 1); FVARP(gameclockscale, 1e-3f, 0.75f, 1e3f); HVARP(gameclockcolour, 0, 0xFFFFFF, 0xFFFFFF); VARP(gameclockalpha, 0, 255, 255); HVARP(gameclocklowcolour, 0, 0xFFC040, 0xFFFFFF); VARP(gameclockalign, -1, 0, 1); FVARP(gameclockx, 0, 0.50f, 1); FVARP(gameclocky, 0, 0.03f, 1); void drawgameclock(int w, int h) { int secs = max(maplimit-lastmillis + 999, 0)/1000, mins = secs/60; secs %= 60; defformatstring(buf, "%d:%02d", mins, secs); int tw = 0, th = 0; text_bounds(buf, tw, th); vec2 offset = vec2(gameclockx, gameclocky).mul(vec2(w, h).div(gameclockscale)); if(gameclockalign == 1) offset.x -= tw; else if(gameclockalign == 0) offset.x -= tw/2.0f; offset.y -= th/2.0f; pushhudmatrix(); hudmatrix.scale(gameclockscale, gameclockscale, 1); flushhudmatrix(); int color = mins < 1 ? gameclocklowcolour : gameclockcolour; draw_text(buf, int(offset.x), int(offset.y), (color>>16)&0xFF, (color>>8)&0xFF, color&0xFF, gameclockalpha); pophudmatrix(); } extern int hudscore; extern void drawhudscore(int w, int h); VARP(ammobar, 0, 0, 1); VARP(ammobaralign, -1, 0, 1); VARP(ammobarhorizontal, 0, 0, 1); VARP(ammobarflip, 0, 0, 1); VARP(ammobarhideempty, 0, 1, 1); VARP(ammobarsep, 0, 20, 500); VARP(ammobarcountsep, 0, 20, 500); FVARP(ammobarcountscale, 0.5, 1.5, 2); FVARP(ammobarx, 0, 0.025f, 1.0f); FVARP(ammobary, 0, 0.5f, 1.0f); FVARP(ammobarscale, 0.1f, 0.5f, 1.0f); void drawammobarcounter(const vec2 ¢er, const fpsent *p, int gun) { vec2 icondrawpos = vec2(center).sub(HICON_SIZE / 2); int alpha = p->ammo[gun] ? 0xFF : 0x7F; gle::color(bvec(0xFF, 0xFF, 0xFF), alpha); drawicon(HICON_FIST + gun, icondrawpos.x, icondrawpos.y); int fw, fh; text_bounds("000", fw, fh); float labeloffset = HICON_SIZE / 2.0f + ammobarcountsep + ammobarcountscale * (ammobarhorizontal ? fh : fw) / 2.0f; vec2 offsetdir = (ammobarhorizontal ? vec2(0, 1) : vec2(1, 0)).mul(ammobarflip ? -1 : 1); vec2 labelorigin = vec2(offsetdir).mul(labeloffset).add(center); pushhudmatrix(); hudmatrix.translate(labelorigin.x, labelorigin.y, 0); hudmatrix.scale(ammobarcountscale, ammobarcountscale, 1); flushhudmatrix(); defformatstring(label, "%d", p->ammo[gun]); int tw, th; text_bounds(label, tw, th); vec2 textdrawpos = vec2(-tw, -th).div(2); float ammoratio = (float)p->ammo[gun] / itemstats[gun-GUN_SG].add; bvec color = bvec::hexcolor(p->ammo[gun] == 0 || ammoratio >= 1.0f ? 0xFFFFFF : (ammoratio >= 0.5f ? 0xFFC040 : 0xFF0000)); draw_text(label, textdrawpos.x, textdrawpos.y, color.r, color.g, color.b, alpha); /// ALIGN STUFF AND ADD COMMAND FOR IT... //~text_bounds(label, movew, moveh); //~moved = movew + 6; //~defformatstring(damagedealt, "| %d ", pwdamagedealt[gun]); //~draw_text(damagedealt, textdrawpos.x+moved, textdrawpos.y, color.r, color.g, color.b, alpha); //~text_bounds(damagedealt, movew, moveh); //~moved += movew; //~defformatstring(accuracy, "| %d %%", pwaccuracy[gun]); //~draw_text(accuracy, textdrawpos.x+moved, textdrawpos.y, color.r, color.g, color.b, alpha); pophudmatrix(); } static inline bool ammobargunvisible(const fpsent *d, int gun) { return d->ammo[gun] > 0 || d->gunselect == gun; } void drawammobar(int w, int h, fpsent *p) { if(m_insta) return; int NUMPLAYERGUNS = GUN_PISTOL - GUN_SG + 1; int numvisibleguns = NUMPLAYERGUNS; if(ammobarhideempty) loopi(NUMPLAYERGUNS) if(!ammobargunvisible(p, GUN_SG + i)) numvisibleguns--; vec2 origin = vec2(ammobarx, ammobary).mul(vec2(w, h).div(ammobarscale)); vec2 offsetdir = ammobarhorizontal ? vec2(1, 0) : vec2(0, 1); float stepsize = HICON_SIZE + ammobarsep; float initialoffset = (ammobaralign - 1) * (numvisibleguns - 1) * stepsize / 2; pushhudmatrix(); hudmatrix.scale(ammobarscale, ammobarscale, 1); flushhudmatrix(); int numskippedguns = 0; loopi(NUMPLAYERGUNS) if(ammobargunvisible(p, GUN_SG + i) || !ammobarhideempty) { float offset = initialoffset + (i - numskippedguns) * stepsize; vec2 drawpos = vec2(offsetdir).mul(offset).add(origin); drawammobarcounter(drawpos, p, GUN_SG + i); } else numskippedguns++; pophudmatrix(); } VARP(speedometer, 0, 1, 1); FVARP(speedometerx, 0.0, 0.5, 1.0); FVARP(speedometery, 0.0, 0.6, 1.0); FVARP(speedometerscale, 0.1, 0.5, 1.0); VARP(speedometercolor, 0, 1, 1); FVARP(speedometeralpha, 0.0, 0.5, 1.0); void drawspeedometer(fpsent *d, int w, int h) { int speedforreal = (int) (sqrtf(d->vel.squaredlen()) + 1.0f); speedforreal = (speedforreal == 1) ? 0 : speedforreal; vec colour = vec(255, 255, 255); float realw = 0; float realh = 0; if (speedometercolor) { if (speedforreal==0) colour = vec(60, 60, 60); else if (speedforreal>0 && speedforreal<=60) colour = vec(240, 30, 30); else if (speedforreal>60 && speedforreal<=120) colour = vec(180, 90, 60); else if (speedforreal>120 && speedforreal<=180) colour = vec(180, 180, 30); else if (speedforreal>180 && speedforreal<=240) colour = vec(90, 180, 60); else colour = vec(30, 240, 30); } defformatstring(speedstring, "%d", speedforreal); text_boundsf(speedstring, realw, realh); vec2 offset = vec2(speedometerx, speedometery).mul(vec2(w, h).div(speedometerscale)); offset.x -= realw/2.0f; offset.y -= realh/2.0f; pushhudmatrix(); hudmatrix.scale(speedometerscale, speedometerscale, 1); flushhudmatrix(); const int speedow = 220; const int speedoh = 101; settexture("packages/hud/speedometer.png"); hudquad(offset.x+realw/2.0f-speedow/2, offset.y+realh/2.0f-speedoh/2, speedow, speedoh); draw_text(speedstring, int(offset.x), int(offset.y), colour.x, colour.y, colour.z, (int)(speedometeralpha*255.0f)); pophudmatrix(); } //~void newhud(int w, int h) //new SauerEnhanced HUD //~{ //~if(player1->state==CS_DEAD || player1->state==CS_SPECTATOR) return; //~glPushMatrix(); //~glScalef(1/1.2f, 1/1.2f, 1); //~pushhudmatrix(); //~hudmatrix.scale(w/1800.0f, h/1650.0f, 1); //~hudmatrix.scale(1.0f/1.2f, 1.0f/1.2f, 1); //~flushhudmatrix(); //~if(!m_insta) draw_textf("%d", 80, h*1.2f-128, player1->state==CS_DEAD ? 0 : player1->health); //~defformatstring(ammo, "%d", player1->ammo[player1->gunselect]); //~int wb, hb; //~text_bounds(ammo, wb, hb); //~draw_textf("%d", w*1.2f-wb-80, h*1.2f-128, player1->ammo[player1->gunselect]); //~hudmatrix.ortho(0, w, h, 0, -1, 1); //~resethudmatrix(); //~hudshader->set(); //~gle::colorf(1, 1, 1); //~gle::defvertex(2); //~gle::deftexcoord0(); //~if(player1->quadmillis) //~{ //~gle::begin(GL_QUADS); //~loopj(numdecals) //~{ //~float hsz = decals[j].size, hx = clamp(decals[j].x, hsz, w-hsz), hy = clamp(decals[j].y, hsz, h-hsz), side = decals[j].side; //~gle::attribf(hx-hsz, hy-hsz); gle::attribf(side, 0); //~gle::attribf(hx+hsz, hy-hsz); gle::attribf(1-side, 0); //~gle::attribf(hx+hsz, hy+hsz); gle::attribf(1-side, 1); //~gle::attribf(hx-hsz, hy+hsz); gle::attribf(side, 1); //~} //~gle::end(); //~settexture("packages/hud/hud_quaddamage_left.png"); //QuadDamage left glow //~gle::begin(GL_QUADS); //~gle::attribf(0, h*1.2f-207); gle::attribf(0.0f, 0.0f); //~gle::attribf(539, h*1.2f-207); gle::attribf(1.0f, 0.0f); //~gle::attribf(539, h*1.2f); gle::attribf(1.0f, 1.0f); //~gle::attribf(0, h*1.2f); gle::attribf(0.0f, 1.0f); //~gle::end(); //~settexture("packages/hud/hud_quaddamage_right.png"); //QuadDamage right glow //~gle::begin(GL_QUADS); //~gle::attribf(w*1.2f-135, h*1.2f-207); gle::attribf(0.0f, 0.0f); //~gle::attribf(w*1.2f, h*1.2f-207); gle::attribf(1.0f, 0.0f); //~gle::attribf(w*1.2f, h*1.2f); gle::attribf(1.0f, 1.0f); //~gle::attribf(w*1.2f-135, h*1.2f); gle::attribf(0.0f, 1.0f); //~gle::end(); //~} //~if(player1->maxhealth > 100) //~{ //~settexture("packages/hud/hud_megahealth.png"); //HealthBoost indicator //~gle::begin(GL_QUADS); //~gle::attribf(0, h*1.2f-207); gle::attribf(0.0f, 0.0f); //~gle::attribf(539, h*1.2f-207); gle::attribf(1.0f, 0.0f); //~gle::attribf(539, h*1.2f); gle::attribf(1.0f, 1.0f); //~gle::attribf(0, h*1.2f); gle::attribf(0.0f, 1.0f); //~gle::end(); //~} //~gle::begin(GL_QUADS); //~gle::attribf(w/2-speedow/2, h/2-speedoh/2); gle::attribf(0.0f, 1.0f); //~gle::attribf(w/2, h/2-speedoh/2); gle::attribf(1.0f, 1.0f); //~gle::attribf(w/2, h/2); gle::attribf(1.0f, 1.0f); //~gle::attribf(w/2-speedow/2, h/2); gle::attribf(0.0f, 1.0f); //~gle::end(); //~int health = (player1->health*100)/player1->maxhealth, //~armour = (player1->armour*100)/200, //~hh = (health*101)/100; //~float hs = (health*1.0f)/100; //~if(player1->health > 0 && !m_insta) //~{ //~settexture("packages/hud/hud_health.png"); //Health bar //~hudquad(4000, 600, 97, 56); //~gle::begin(GL_QUADS); //~gle::attribf(47, h*1.2f-hh-56); gle::attribf(0.0f, 1.0f-hs); //~gle::attribf(97, h*1.2f-hh-56); gle::attribf(1.0f, 1.0f-hs); //~gle::attribf(97, h*1.2f-57); gle::attribf(1.0f, 1.0f); //~gle::attribf(47, h*1.2f-57); gle::attribf(0.0f, 1.0f); //~gle::end(); //~} //~if(player1->armour > 0) //~{ //~settexture("packages/hud/hud_armour.png"); //Armour bar //~gle::begin(GL_QUADS); //~gle::attribf(130, h*1.2f-62);gle::attribf(0.0f, 0.0f); //~gle::attribf(130+ah, h*1.2f-62);gle::attribf(as, 0.0f); //~gle::attribf(130+ah, h*1.2f-44);gle::attribf(as, 1.0f); //~gle::attribf(130, h*1.2f-44);gle::attribf(0.0f, 1.0f); //~gle::end(); //~} //~if(!m_insta) //~{ //~settexture("packages/hud/hud_left.png"); //left HUD //~gle::begin(GL_QUADS); //~gle::attribf(0, h*1.2f-207);gle::attribf(0.0f, 0.0f); //~gle::attribf(539, h*1.2f-207);gle::attribf(1.0f, 0.0f); //~gle::attribf(539, h*1.2f );gle::attribf(1.0f, 1.0f); //~gle::attribf(0, h*1.2f );gle::attribf(0.0f, 1.0f); //~gle::end(); //~} //~settexture("packages/hud/hud_right.png"); //right HUD //~gle::begin(GL_QUADS); //~gle::attribf(w*1.2f-135, h*1.2f-207);gle::attribf(0.0f, 0.0f); //~gle::attribf(w*1.2f, h*1.2f-207);gle::attribf(1.0f, 0.0f); //~gle::attribf(w*1.2f, h*1.2f );gle::attribf(1.0f, 1.0f); //~gle::attribf(w*1.2f-135, h*1.2f );gle::attribf(0.0f, 1.0f); //~gle::end(); //~int maxammo = 0; //~switch(player1->gunselect) //~{ //~case GUN_FIST: //~maxammo = 1; //~break; //~case GUN_RL: //~case GUN_RIFLE: //~maxammo = m_insta ? 100 : 15; //~break; //~case GUN_SG: //~case GUN_GL: //~maxammo = 30; //~break; //~case GUN_CG: //~maxammo = 60; //~break; //~case GUN_PISTOL: //~maxammo = 120; //~break; //~} //~int curammo = (player1->ammo[player1->gunselect]*100)/maxammo, //~amh = (curammo*101)/100; //~float ams = (curammo*1.0f)/100; //~if(player1->ammo[player1->gunselect] > 0) //~{ //~settexture("packages/hud/hud_health.png"); //Ammo bar //~gle::begin(GL_QUADS); //~gle::attribf(w*1.2f-47, h*1.2f-amh-56);gle::attribf(0.0f, 1.0f-ams); //~gle::attribf(w*1.2f-97, h*1.2f-amh-56);gle::attribf(1.0f, 1.0f-ams); //~gle::attribf(w*1.2f-97, h*1.2f-57 );gle::attribf(1.0f, 1.0f); //~gle::attribf(w*1.2f-47, h*1.2f-57 );gle::attribf(0.0f, 1.0f); //~gle::end(); //~} //~glPopMatrix(); //~pophudmatrix(); //~} void gameplayhud(int w, int h) { //~newhud(w, h); pushhudmatrix(); hudmatrix.scale(h/1800.0f, h/1800.0f, 1); flushhudmatrix(); if(player1->state==CS_SPECTATOR) { int pw, ph, tw, th, fw, fh; text_bounds(" ", pw, ph); text_bounds("SPECTATOR", tw, th); th = max(th, ph); fpsent *f = followingplayer(); text_bounds(f ? colorname(f) : " ", fw, fh); fh = max(fh, ph); draw_text("SPECTATOR", w*1800/h - tw - pw, 1650 - th - fh); if(f) { int color = statuscolor(f, 0xFFFFFF); draw_text(colorname(f), w*1800/h - fw - pw, 1650 - fh, (color>>16)&0xFF, (color>>8)&0xFF, color&0xFF); } } fpsent *d = hudplayer(); if(d->state!=CS_EDITING) { if(cmode) cmode->drawhud(d, w, h); } pophudmatrix(); if(d->state!=CS_EDITING && d->state!=CS_SPECTATOR) drawhudicons(d, w, h); if(d->state!=CS_EDITING && d->state!=CS_SPECTATOR && d->state!=CS_DEAD) { if(ammobar) drawammobar(w, h, d); if(speedometer) drawspeedometer(d, w, h); } if(!m_edit) { if(gameclock) drawgameclock(w, h); if(hudscore) drawhudscore(w, h); } /// Frag message. if((hudfragmillis+fragmessageduration > lastmillis) && (lastmillis>fragmessageduration)) { vec2 center = vec2(0.5*w, 0.2*h); float width = 0, height = 0; pushhudmatrix(); hudmatrix.scale(0.8, 0.8, 1); flushhudmatrix(); draw_textf("%s", center.x, center.y, hudfragger); text_boundsf(hudfragger, width, height); drawicon(HICON_FIST+hudfraggun, center.x+width, center.y, height); draw_textf("%s", center.x+width+height, center.y, hudfragged); pophudmatrix(); } } int clipconsole(int w, int h) { if(cmode) return cmode->clipconsole(w, h); return 0; } VARP(teamcrosshair, 0, 1, 1); VARP(hitcrosshair, 0, 425, 1000); const char *defaultcrosshair(int index) { switch(index) { case 2: return "data/hit.png"; case 1: return "data/teammate.png"; default: return "data/crosshair.png"; } } int selectcrosshair(vec &color) { fpsent *d = hudplayer(); if(d->state==CS_SPECTATOR || d->state==CS_DEAD) return -1; if(d->state!=CS_ALIVE) return 0; int crosshair = 0; if(lasthit && lastmillis - lasthit < hitcrosshair) crosshair = 2; else if(teamcrosshair) { dynent *o = intersectclosest(d->o, worldpos, d); if(o && o->type==ENT_PLAYER && isteam(((fpsent *)o)->team, d->team)) { crosshair = 1; color = vec(0, 0, 1); } } if(crosshair!=1 && !editmode && !m_insta) { if(d->health<=25) color = vec(1, 0, 0); else if(d->health<=50) color = vec(1, 0.5f, 0); } if(d->gunwait) color.mul(0.5f); return crosshair; } void lighteffects(dynent *e, vec &color, vec &dir) { #if 0 fpsent *d = (fpsent *)e; if(d->state!=CS_DEAD && d->quadmillis) { float t = 0.5f + 0.5f*sinf(2*M_PI*lastmillis/1000.0f); color.y = color.y*(1-t) + t; } #endif } int maxsoundradius(int n) { switch(n) { case S_JUMP: case S_LAND: case S_WEAPLOAD: case S_ITEMAMMO: case S_ITEMHEALTH: case S_ITEMARMOUR: case S_ITEMPUP: case S_ITEMSPAWN: case S_NOAMMO: case S_PUPOUT: return 340; default: return 500; } } bool serverinfostartcolumn(g3d_gui *g, int i) { static const char * const names[] = { "ping ", "players ", "mode ", "map ", "time ", "master ", "host ", "port ", "description " }; static const float struts[] = { 7, 7, 12.5f, 14, 7, 8, 14, 7, 24.5f }; if(size_t(i) >= sizeof(names)/sizeof(names[0])) return false; g->pushlist(); g->text(names[i], 0xFFFF80, !i ? " " : NULL); if(struts[i]) g->strut(struts[i]); g->mergehits(true); return true; } void serverinfoendcolumn(g3d_gui *g, int i) { g->mergehits(false); g->column(i); g->poplist(); } const char *mastermodecolor(int n, const char *unknown) { return (n>=MM_START && size_t(n-MM_START)=MM_START && size_t(n-MM_START) &attr, int np) { if(ping < 0 || attr.empty() || attr[0]!=PROTOCOL_VERSION) { switch(i) { case 0: if(g->button(" ", 0xFFFFDD, "serverunk")&G3D_UP) return true; break; case 1: case 2: case 3: case 4: case 5: if(g->button(" ", 0xFFFFDD)&G3D_UP) return true; break; case 6: if(g->buttonf("%s ", 0xFFFFDD, NULL, name)&G3D_UP) return true; break; case 7: if(g->buttonf("%d ", 0xFFFFDD, NULL, port)&G3D_UP) return true; break; case 8: if(ping < 0) { if(g->button(sdesc, 0xFFFFDD)&G3D_UP) return true; } else if(g->buttonf("[%s protocol] ", 0xFFFFDD, NULL, attr.empty() ? "unknown" : (attr[0] < PROTOCOL_VERSION ? "older" : "newer"))&G3D_UP) return true; break; } return false; } switch(i) { case 0: { const char *icon = attr.inrange(3) && np >= attr[3] ? "serverfull" : (attr.inrange(4) ? mastermodeicon(attr[4], "serverunk") : "serverunk"); if(g->buttonf("%d ", 0xFFFFDD, icon, ping)&G3D_UP) return true; break; } case 1: if(attr.length()>=4) { if(g->buttonf(np >= attr[3] ? "\f3%d/%d " : "%d/%d ", 0xFFFFDD, NULL, np, attr[3])&G3D_UP) return true; } else if(g->buttonf("%d ", 0xFFFFDD, NULL, np)&G3D_UP) return true; break; case 2: if(g->buttonf("%s ", 0xFFFFDD, NULL, attr.length()>=2 ? server::modename(attr[1], "") : "")&G3D_UP) return true; break; case 3: if(g->buttonf("%.25s ", 0xFFFFDD, NULL, map)&G3D_UP) return true; break; case 4: if(attr.length()>=3 && attr[2] > 0) { int secs = clamp(attr[2], 0, 59*60+59), mins = secs/60; secs %= 60; if(g->buttonf("%d:%02d ", 0xFFFFDD, NULL, mins, secs)&G3D_UP) return true; } else if(g->buttonf(" ", 0xFFFFDD)&G3D_UP) return true; break; case 5: if(g->buttonf("%s%s ", 0xFFFFDD, NULL, attr.length()>=5 ? mastermodecolor(attr[4], "") : "", attr.length()>=5 ? server::mastermodename(attr[4], "") : "")&G3D_UP) return true; break; case 6: if(g->buttonf("%s ", 0xFFFFDD, NULL, name)&G3D_UP) return true; break; case 7: if(g->buttonf("%d ", 0xFFFFDD, NULL, port)&G3D_UP) return true; break; case 8: if(g->buttonf("%.25s", 0xFFFFDD, NULL, sdesc)&G3D_UP) return true; break; } return false; } // any data written into this vector will get saved with the map data. Must take care to do own versioning, and endianess if applicable. Will not get called when loading maps from other games, so provide defaults. void writegamedata(vector &extras) {} void readgamedata(vector &extras) {} const char *savedconfig() { return "config.cfg"; } const char *restoreconfig() { return "restore.cfg"; } const char *defaultconfig() { return "data/defaults.cfg"; } const char *autoexec() { return "autoexec.cfg"; } const char *savedservers() { return "servers.cfg"; } void loadconfigs() { execident("playsong"); execfile("auth.cfg", false); } }