diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 854e8530..f8b121d5 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -6,7 +6,6 @@ int nextmode = 0; // nextmode becomes gamemode after next map load VAR(gamemode, 1, 0, 0); -VARF(followmode, 0, 1, 1, if(player1->state==CS_SPECTATE) followplayer (player1->followplayercn)); // follow(0) or embody(1) flaginfo flaginfos[2]; @@ -821,59 +820,51 @@ void refreshsopmenu(void *menu, bool init) } } -void followplayer(int j) +void spectate(int mode) // set new spect mode { - if(player1->state==CS_ALIVE) return; - player1->state = CS_SPECTATE; // fix CS_DEAD state + if(!player1->isspectating()) return; showscores(false); - player1->spectating = (followmode==1) ? SM_EMBODYPLAYER : SM_FOLLOWPLAYER; - if(players.length()) + switch(mode) { - if(j!=-1) + case SM_FOLLOW1ST: + case SM_FOLLOW3RD: + case SM_FOLLOW3RD_TRANSPARENT: { - if(players.inrange(j) && players[j] && players[j]->state == CS_ALIVE) + if(players.length()) { - player1->followplayercn = j; - return; + player1->followplayercn %= players.length(); + break; } } - loopv(players) + case SM_FLY: { - player1->followplayercn = (player1->followplayercn+1+i) % players.length(); - ASSERT(player1->followplayercn < players.length()); - if(players[player1->followplayercn] && players[player1->followplayercn]->state == CS_ALIVE) return; + if(players.inrange(player1->followplayercn)) // set spectator location to last followed player + { + playerent *f = players[player1->followplayercn]; + player1->o = f->o; + player1->yaw = f->yaw; + player1->pitch = 0.0f; + } + else entinmap(player1); // or drop 'em at a random place + break; } + default: break; } - player1->spectating = SM_FLY; + player1->spectating = mode; } -void spectate() + +void togglespect() // cycle through all spectating modes { - if(player1->state==CS_ALIVE) return; - showscores(false); - if(player1->spectating != SM_NONE && players.inrange(player1->followplayercn)) - { - // set spectator location to last followed player - playerent *f = players[player1->followplayercn]; - player1->o = f->o; - player1->yaw = f->yaw; - } - player1->spectating = SM_FLY; + int mode ; + if(player1->spectating == SM_NONE) mode = SM_FOLLOW1ST; // start with 1st person spect + else mode = SM_FOLLOW1ST + (((player1->spectating) % (SM_NUM-SM_FOLLOW1ST))); + if(mode != player1->spectating) spectate(mode); + conoutf("%i", mode); } -void toggledeathcam() -{ - showscores(false); - if(player1->spectating == SM_FOLLOWPLAYER) followplayer(); - else - { - player1->spectating = (player1->spectating+1) % SM_NUM; - if(player1->spectating==SM_FOLLOWPLAYER) followplayer(); - } -} - -COMMAND(followplayer, ARG_1INT); -COMMAND(spectate, ARG_NONE); +COMMAND(spectate, ARG_1INT); +COMMAND(togglespect, ARG_NONE); int isalive() { return player1->state==CS_ALIVE ? 1 : 0; } COMMANDN(alive, isalive, ARG_NONE); diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index f6983a3f..ed41a704 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -653,7 +653,8 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) if(demoplayback) { player1->state = CS_SPECTATE; - player1->spectating = SM_EMBODYPLAYER; + player1->spectating = SM_NONE; + togglespect(); } else { diff --git a/source/src/entity.h b/source/src/entity.h index 195174ef..0f796331 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -89,7 +89,7 @@ static inline int magsize(int gun) { return guns[gun].magsize; } enum { ENT_PLAYER = 0, ENT_BOT, ENT_CAMERA, ENT_BOUNCE }; enum { CS_ALIVE = 0, CS_DEAD, CS_SPAWNING, CS_LAGGED, CS_EDITING, CS_SPECTATE }; enum { CR_DEFAULT = 0, CR_MASTER, CR_ADMIN }; -enum { SM_NONE = 0, SM_FLY, SM_FOLLOWPLAYER, SM_EMBODYPLAYER, SM_NUM }; +enum { SM_NONE = 0, SM_FOLLOW1ST, SM_FOLLOW3RD, SM_FOLLOW3RD_TRANSPARENT, SM_FLY, SM_NUM }; struct physent { @@ -406,8 +406,23 @@ struct playerent : dynent, playerstate void setprimary(int w) { primweap = weapons[(primary = w)]; } void setnextprimary(int w) { nextprimweap = weapons[(nextprimary = w)]; } bool isspectating() { return state==CS_SPECTATE || (state==CS_DEAD && spectating); } + + /* + static struct playerent *cameraplayer() + { + extern playerent *player1; + extern vectorplayers; + if(player1->isspectating()) + { + if(player1->spectating==SM_FOLLOW1ST && players.inrange(player1->followplayercn)) return players[player1->followplayercn]; + else return NULL; + } + else return player1; + }*/ }; + + class CBot; struct botent : playerent diff --git a/source/src/physics.cpp b/source/src/physics.cpp index 87f1e0e1..5dddf30d 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -515,13 +515,9 @@ void attack(bool on) void jumpn(bool on) { if(intermission) return; - if(player1->state==CS_DEAD || player1->state==CS_SPECTATE) + if(player1->isspectating()) { - if(on) - { - showscores(false); - player1->spectating = SM_FLY; - } + if(on) togglespect(); } else if(player1->crouching) return; else player1->jumpnext = on; diff --git a/source/src/protos.h b/source/src/protos.h index 7d0d6516..c961d60e 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -180,7 +180,7 @@ extern void gl_drawframe(int w, int h, float changelod, float curfps); extern void clearminimap(); extern void rendercursor(int x, int y, int w); extern void renderaboveheadicon(playerent *p); -extern void drawcrosshair(bool showteamwarning); +extern void drawcrosshair(playerent *p, bool showteamwarning); extern void drawscope(); extern float dynfov(); @@ -288,8 +288,7 @@ extern void serveropcommand(int cmd, int arg1); extern void refreshsopmenu(void *menu, bool init); extern char *colorname(playerent *d, int num = 0, char *name = NULL, const char *prefix = ""); extern char *colorping(int ping); -extern void toggledeathcam(); -extern void followplayer(int j = -1); +extern void togglespect(); struct votedisplayinfo { diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index e572ab3a..381345f0 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -309,6 +309,7 @@ void recomputecamera() { if((player1->state==CS_SPECTATE || player1->state==CS_DEAD) && !editmode) { + bool followvalid = players.inrange(player1->followplayercn) && players[player1->followplayercn]; switch(player1->spectating) { case SM_NONE: @@ -327,13 +328,13 @@ void recomputecamera() case SM_FLY: camera1 = player1; break; - case SM_FOLLOWPLAYER: - case SM_EMBODYPLAYER: - if(!players.inrange(player1->followplayercn) || !players[player1->followplayercn]) - { - toggledeathcam(); - return; - } + case SM_FOLLOW1ST: + if(!followvalid) { togglespect(); return; } + camera1 = players[player1->followplayercn]; + break; + case SM_FOLLOW3RD: + case SM_FOLLOW3RD_TRANSPARENT: + if(!followvalid) { togglespect(); return; } playerent *p = players[player1->followplayercn]; static physent followcam; static playerent *lastplayer; @@ -350,7 +351,7 @@ void recomputecamera() followcam.o = p->o; // move camera into the desired direction using physics to avoid getting stuck in map geometry - if(player1->spectating == SM_FOLLOWPLAYER) + if(player1->spectating == SM_FOLLOW3RD) { followcam.vel.x = -(float)(cosf(RAD*(p->yaw-90)))*p->radius*1.5f; followcam.vel.y = -(float)(sinf(RAD*(p->yaw-90)))*p->radius*1.5f; @@ -663,8 +664,11 @@ void drawhudgun(int w, int h, float aspect, int farplane) { sethudgunperspective(true); - if(hudgun && player1->state==CS_ALIVE) player1->weaponsel->renderhudmodel(); - //if(hudgun && player1->state==CS_SPECTATE && players[player1->followplayercn]) players[player1->followplayercn]->weaponsel->renderhudmodel(); + if(hudgun && !player1->isspectating() && camera1->type==ENT_PLAYER) + { + playerent *p = (playerent *)camera1; + if(p->state==CS_ALIVE) p->weaponsel->renderhudmodel(); + } rendermenumdl(); sethudgunperspective(false); diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index 1173cd35..27e23aff 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -120,7 +120,7 @@ void loadcrosshair(char *c) COMMAND(loadcrosshair, ARG_1STR); -void drawcrosshair(bool showteamwarning) +void drawcrosshair(playerent *p, bool showteamwarning) { glBlendFunc(GL_ONE, GL_ONE); static Texture *teammatetex = NULL; @@ -132,14 +132,14 @@ void drawcrosshair(bool showteamwarning) if(crosshairfx) { if(showteamwarning) glColor3ub(255, 0, 0); - else if(player1->gunwait) glColor3ub(128,128,128); + else if(p->gunwait) glColor3ub(128,128,128); else if(!m_osok) { - if(player1->health<=25) glColor3ub(255,0,0); - else if(player1->health<=50) glColor3ub(255,128,0); + if(p->health<=25) glColor3ub(255,0,0); + else if(p->health<=50) glColor3ub(255,128,0); } } - float chsize = (float)crosshairsize * (player1->weaponsel->type==GUN_ASSAULT && player1->weaponsel->shots > 3 ? 1.4f : 1.0f) * (showteamwarning ? 2.0f : 1.0f); + float chsize = (float)crosshairsize * (p->weaponsel->type==GUN_ASSAULT && p->weaponsel->shots > 3 ? 1.4f : 1.0f) * (showteamwarning ? 2.0f : 1.0f); glTexCoord2i(0, 0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 - chsize); glTexCoord2i(1, 0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 - chsize); glTexCoord2i(1, 1); glVertex2f(VIRTW/2 + chsize, VIRTH/2 + chsize); @@ -375,6 +375,8 @@ void drawteamicons(int w, int h) void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater) { + playerent *p = camera1->type==ENT_PLAYER ? (playerent *)camera1 : player1; + glDisable(GL_DEPTH_TEST); glMatrixMode(GL_MODELVIEW); @@ -406,15 +408,15 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat bool menu = menuvisible(); bool command = getcurcommand() ? true : false; - if((player1->state==CS_ALIVE || player1->state==CS_EDITING) && !player1->weaponsel->reloading && !menu) + if((p->state==CS_ALIVE || p->state==CS_EDITING) && !p->weaponsel->reloading && !menu) { - bool drawteamwarning = crosshairteamsign && targetplayer && isteam(targetplayer->team, player1->team) && targetplayer->state!=CS_DEAD; - player1->weaponsel->renderaimhelp(drawteamwarning); + bool drawteamwarning = crosshairteamsign && targetplayer && isteam(targetplayer->team, p->team) && targetplayer->state!=CS_ALIVE; + p->weaponsel->renderaimhelp(drawteamwarning); } drawdmgindicator(); - if(player1->state==CS_ALIVE && !hidehudequipment) drawequipicons(); + if(p->state==CS_ALIVE && !hidehudequipment) drawequipicons(); glMatrixMode(GL_MODELVIEW); if(!menu && (!hideradar || showmap)) drawradar(w, h); @@ -472,7 +474,8 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat if(!hidehudmsgs) hudmsgs.render(); - if(!hidespecthud && player1->state==CS_DEAD && player1->spectating==SM_NONE) + /* + if(!hidespecthud && p->state==CS_DEAD && p->spectating==SM_NONE) { glLoadIdentity(); glOrtho(0, VIRTW*3/2, VIRTH*3/2, 0, -1, 1); @@ -480,24 +483,25 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat draw_textf("F7 spectate", left, top); draw_textf("F8 follow player", left, top+80); } + */ - if(!hidespecthud && (player1->state==CS_SPECTATE || (player1->state==CS_DEAD && player1->spectating!=SM_NONE))) + if(!hidespecthud && (p->state==CS_SPECTATE || (p->state==CS_DEAD && p->spectating!=SM_NONE))) { glLoadIdentity(); glOrtho(0, VIRTW, VIRTH, 0, -1, 1); draw_text("SPECTATING", VIRTW/40, VIRTH/10*7); } - if(player1->state==CS_ALIVE) + if(p->state==CS_ALIVE) { glLoadIdentity(); glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); if(!hidehudequipment) { - draw_textf("%d", 90, 827, player1->health); - if(player1->armour) draw_textf("%d", 390, 827, player1->armour); - player1->weaponsel->renderstats(); + draw_textf("%d", 90, 827, p->health); + if(p->armour) draw_textf("%d", 390, 827, p->armour); + p->weaponsel->renderstats(); } if(m_ctf && !hidectfhud) @@ -514,12 +518,12 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat } // big flag-stolen icon - flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; - if(f.state==CTFF_STOLEN && f.actor == player1 && f.ack) + flaginfo &f = flaginfos[team_opposite(team_int(p->team))]; + if(f.state==CTFF_STOLEN && f.actor == p && f.ack) { glColor4f(1.0f, 1.0f, 1.0f, (sinf(lastmillis/100.0f)+1.0f) / 2.0f); glEnable(GL_BLEND); - drawctficon(VIRTW-225-10, VIRTH*5/8, 225, team_opposite(team_int(player1->team)), 1, 1/2.0f); + drawctficon(VIRTW-225-10, VIRTH*5/8, 225, team_opposite(team_int(p->team)), 1, 1/2.0f); glDisable(GL_BLEND); } } diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index 71b9a281..a71e34ea 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -447,7 +447,6 @@ void renderclient(playerent *d, const char *mdlname, const char *vwepname, int t vec o(d->o); o.z -= d->dyneyeheight(); int basetime = -((int)(size_t)d&0xFFF); - //if(player1->state == CS_SPECTATE && d->clientnum == player1->followplayercn) return; // don't draw followed player (or see below for "see through") if(d->state==CS_DEAD) { if(d==player1 && d->allowmove()) return; @@ -484,8 +483,7 @@ void renderclient(playerent *d, const char *mdlname, const char *vwepname, int t } // FIXME: while networked my state as spectator seems to stay CS_DEAD, not CS_SPECTATE // flowtron: I fixed this for following at least (see followplayer()) - //if((player1->state == CS_DEAD || player1->state == CS_SPECTATE) && d->clientnum == player1->followplayercn && player1->spectating == SM_EMBODYPLAYER) anim |= ANIM_TRANSLUCENT; // see through followed player - if(player1->state == CS_SPECTATE && d->clientnum == player1->followplayercn && player1->spectating == SM_EMBODYPLAYER) anim |= ANIM_TRANSLUCENT; // see through followed player + if(player1->isspectating() && d->clientnum == player1->followplayercn && player1->spectating == SM_FOLLOW3RD_TRANSPARENT) anim |= ANIM_TRANSLUCENT; // see through followed player rendermodel(mdlname, anim, tex, 1.5f, o, d->yaw+90, d->pitch/4, speed, basetime, d, a); if(isteam(player1->team, d->team)) renderaboveheadicon(d); } @@ -514,7 +512,7 @@ void renderclient(playerent *d) void renderclients() { playerent *d; - loopv(players) if((d = players[i]) && d->state!=CS_SPAWNING) renderclient(d); + loopv(players) if((d = players[i]) && d->state!=CS_SPAWNING && (!player1->isspectating() || player1->spectating != SM_FOLLOW1ST || player1->followplayercn != i)) renderclient(d); if(player1->state==CS_DEAD || (reflecting && !refracting)) renderclient(player1); } diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index 284a2822..287b97ea 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -58,6 +58,7 @@ void shiftweapon(int s) selectweapon(player1->weapons[trygun]); } } + else if(player1->isspectating()) { (player1->followplayercn += s) %= players.length(); } } int currentprimary() { return player1->primweap->type; } @@ -516,7 +517,7 @@ void weapon::onselecting() } void weapon::renderhudmodel() { renderhudmodel(owner->lastaction); } -void weapon::renderaimhelp(bool teamwarning) { drawcrosshair(teamwarning); } +void weapon::renderaimhelp(bool teamwarning) { drawcrosshair(owner, teamwarning); } int weapon::dynspread() { return info.spread; } float weapon::dynrecoil() { return info.recoil; } bool weapon::selectable() { return this != owner->weaponsel && owner->state == CS_ALIVE && !owner->weaponchanging; } @@ -863,7 +864,7 @@ void sniperrifle::onselecting() { weapon::onselecting(); scoped = false; } void sniperrifle::ondeselecting() { scoped = false; } void sniperrifle::onownerdies() { scoped = false; } void sniperrifle::renderhudmodel() { if(!scoped) weapon::renderhudmodel(); } -void sniperrifle::renderaimhelp(bool teamwarning) { if(scoped) drawscope(); if(teamwarning) drawcrosshair(teamwarning); } +void sniperrifle::renderaimhelp(bool teamwarning) { if(scoped) drawscope(); if(teamwarning) drawcrosshair(owner, teamwarning); } void sniperrifle::setscope(bool enable) { if(this == owner->weaponsel && !reloading && owner->state == CS_ALIVE) scoped = enable; }