From 7d1741eeea52b2403acd7837da10e24aace60038 Mon Sep 17 00:00:00 2001 From: div0 Date: Thu, 21 Aug 2008 15:32:44 +0000 Subject: [PATCH] Race: send stuff to people spectating someone too (incomplete) New spectator mode display (csqc) Spectating now allows changing spectating speed (using mouse wheel) git-svn-id: svn://svn.icculus.org/nexuiz/trunk@4136 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/client/Defs.qc | 3 ++ data/qcsrc/client/Main.qc | 28 +++++++++- data/qcsrc/client/miscfunctions.qc | 11 +++- data/qcsrc/client/sbar.qc | 83 +++++++++++++++++++++++++++--- data/qcsrc/common/constants.qh | 4 +- data/qcsrc/server/cl_client.qc | 16 ++++++ data/qcsrc/server/cl_physics.qc | 22 ++++++++ data/qcsrc/server/miscfunctions.qc | 20 +++++++ data/qcsrc/server/race.qc | 56 ++++++++++---------- data/qcsrc/server/teamplay.qc | 43 ++++------------ 10 files changed, 215 insertions(+), 71 deletions(-) diff --git a/data/qcsrc/client/Defs.qc b/data/qcsrc/client/Defs.qc index a8930c5a2..57640c195 100644 --- a/data/qcsrc/client/Defs.qc +++ b/data/qcsrc/client/Defs.qc @@ -199,3 +199,6 @@ float race_othercheckpointdelta; float race_othercheckpointlapsdelta; string race_othercheckpointenemy; float sb_showscores_force; + +// Spectating +float spectatee_status; diff --git a/data/qcsrc/client/Main.qc b/data/qcsrc/client/Main.qc index 891507afd..e4eff13ae 100644 --- a/data/qcsrc/client/Main.qc +++ b/data/qcsrc/client/Main.qc @@ -644,6 +644,28 @@ void Net_ReadRace() } } +void Net_ReadForceScoreboard() +{ + sb_showscores_force = ReadByte(); +} + +void Net_Reset() +{ + race_laptime = 0; + race_checkpointtime = 0; +} + +void Net_ReadSpectating() +{ + float newspectatee_status; + newspectatee_status = ReadByte(); + if(newspectatee_status == player_localentnum) + newspectatee_status = -1; // observing + if(newspectatee_status != spectatee_status) + Net_Reset(); + spectatee_status = newspectatee_status; +} + // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer. // You must ALWAYS first acquire the temporary ID, which is sent as a byte. // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event. @@ -679,7 +701,11 @@ float CSQC_Parse_TempEntity() bHandled = true; break; case TE_CSQC_FORCESCOREBOARD: - sb_showscores_force = true; + Net_ReadForceScoreboard(); + bHandled = true; + break; + case TE_CSQC_SPECTATING: + Net_ReadSpectating(); bHandled = true; break; default: diff --git a/data/qcsrc/client/miscfunctions.qc b/data/qcsrc/client/miscfunctions.qc index 4342c7722..18aed9f5b 100644 --- a/data/qcsrc/client/miscfunctions.qc +++ b/data/qcsrc/client/miscfunctions.qc @@ -141,7 +141,16 @@ void CSQC_CheckEngine() vector Sbar_GetFontsize() { if(csqc_flags & CSQC_FLAG_READPICTURE) - return stov(cvar_string("sbar_fontsize")); + { + vector v; + v = stov(cvar_string("sbar_fontsize")); + if(v_x == 0) + v = '8 8 0'; + if(v_y == 0) + v_y = v_x; + v_z = 0; + return v; + } return '8 8 0' ; } diff --git a/data/qcsrc/client/sbar.qc b/data/qcsrc/client/sbar.qc index 0bdbd17e9..0e0210253 100644 --- a/data/qcsrc/client/sbar.qc +++ b/data/qcsrc/client/sbar.qc @@ -742,12 +742,6 @@ void Sbar_DrawScoreboard() lastpingstime = time; } - sbar_fontsize = Sbar_GetFontsize(); - if(sbar_fontsize_x == 0) - sbar_fontsize = '8 8 0'; - if(sbar_fontsize_y == 0) - sbar_fontsize_y = sbar_fontsize_x; - xmin = vid_conwidth / 5; ymin = 20; @@ -1325,11 +1319,86 @@ void Sbar_Draw (void) float x, fade; float stat_items; + sbar_fontsize = Sbar_GetFontsize(); + + if(spectatee_status) + { + string s; + vector o; + o = '1 0 0' * vid_conwidth; + if(spectatee_status == -1) + s = "^1Observing"; + else + s = strcat("^1Spectating ^7", getplayerkey(spectatee_status - 1, "name")); + dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0) + drawcolorcodedstring( + o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE), + s, + sbar_fontsize, + sbar_alpha_fg, + 0 + ); + o += sbar_fontsize_y * '0 1 0'; + + if(spectatee_status == -1) + s = "^1Press ^3primary fire^1 to spectate"; + else + s = "^1Press ^3primary fire^1 for another player"; + dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0) + drawcolorcodedstring( + o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE), + s, + sbar_fontsize, + sbar_alpha_fg, + 0 + ); + o += sbar_fontsize_y * '0 1 0'; + + if(spectatee_status != -1) + s = "^1Use ^3weapon switching^1 to change the speed"; + else + s = "^1Press ^3secondary fire^1 to observe"; + dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0) + drawcolorcodedstring( + o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE), + s, + sbar_fontsize, + sbar_alpha_fg, + 0 + ); + o += sbar_fontsize_y * '0 1 0'; + + if(gametype == GAME_ARENA) + s = "^1Wait for your turn to join"; + else if(gametype == GAME_LMS) + { + entity sk; + sk = playerslots[player_localentnum - 1]; + if(sk.(scores[ps_primary]) >= 666) + s = "^1Match has already begun"; + else if(sk.(scores[ps_primary]) > 0) + s = "^1You have no more lives left"; + else + s = "^1Press ^7jump^1 to join"; + } + else + s = "^1Press ^7jump^1 to join"; + dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0) + drawcolorcodedstring( + o - sbar_fontsize_x * '1 0 0' * stringwidth(s, TRUE), + s, + sbar_fontsize, + sbar_alpha_fg, + 0 + ); + o += sbar_fontsize_y * '0 1 0'; + } + //Sbar_SortFrags(); Sbar_UpdatePlayerTeams(); sb_lines = 24; - + if (sb_showscores) Sbar_DrawScoreboard(); else if (intermission == 1) diff --git a/data/qcsrc/common/constants.qh b/data/qcsrc/common/constants.qh index f21584835..4adde5353 100644 --- a/data/qcsrc/common/constants.qh +++ b/data/qcsrc/common/constants.qh @@ -9,7 +9,8 @@ // Revision 8: race // Revision 9: race delta // Revision 10: scoreboard force -#define CSQC_REVISION 9 +// Revision 11: scoreboard unforce; spectator support beginning +#define CSQC_REVISION 11 // probably put these in common/ // so server/ and client/ can be synced better @@ -189,6 +190,7 @@ const float TE_CSQC_CONFIG = 107; const float TE_CSQC_SCORESINFO = 108; const float TE_CSQC_RACE = 109; const float TE_CSQC_FORCESCOREBOARD = 110; +const float TE_CSQC_SPECTATING = 111; const float STAT_KH_KEYS = 32; const float STAT_CTF_STATE = 33; diff --git a/data/qcsrc/server/cl_client.qc b/data/qcsrc/server/cl_client.qc index e0ec3361e..2f4d2cd31 100644 --- a/data/qcsrc/server/cl_client.qc +++ b/data/qcsrc/server/cl_client.qc @@ -1920,6 +1920,7 @@ Called every frame for each client before the physics are run */ void() ctf_setstatus; .float vote_nagtime; +.float spectatee_status; void PlayerPreThink (void) { if(blockSpectators) @@ -2187,6 +2188,21 @@ void PlayerPreThink (void) } else if(self.classname == "spectator") { SpectatorThink(); } + + float oldspectatee_status; + oldspectatee_status = self.spectatee_status; + if(self.classname == "spectator") + self.spectatee_status = num_for_edict(self.enemy); + else if(self.classname == "observer") + self.spectatee_status = num_for_edict(self); + else + self.spectatee_status = 0; + if(self.spectatee_status != oldspectatee_status) + { + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_SPECTATING); + WriteByte(MSG_ONE, self.spectatee_status); + } } diff --git a/data/qcsrc/server/cl_physics.qc b/data/qcsrc/server/cl_physics.qc index ffcdc89a6..871029898 100644 --- a/data/qcsrc/server/cl_physics.qc +++ b/data/qcsrc/server/cl_physics.qc @@ -14,6 +14,7 @@ float sv_airaccel_qw; .float lastflags; .float lastground; .float wasFlying; +.float spectatorspeed; #define SHTEST_DELTA 15 .float shtest_next; @@ -206,7 +207,28 @@ void SV_PlayerPhysics() } if(self.flags & FL_NOTARGET) + { maxspd_mod = cvar("sv_spectator_speed_multiplier"); + if(!self.spectatorspeed) + self.spectatorspeed = maxspd_mod; + if(self.impulse && self.impulse <= 12) + { + if(self.lastflags & FL_NOTARGET) + { + if(self.impulse == 10) + self.spectatorspeed = bound(1, self.spectatorspeed + 0.5, 5); + else if(self.impulse == 11) + self.spectatorspeed = maxspd_mod; + else if(self.impulse == 12) + self.spectatorspeed = bound(1, self.spectatorspeed - 0.5, 5); + else if(self.impulse >= 1 && self.impulse <= 9) + self.spectatorspeed = 1 + 0.5 * (self.impulse - 1); + } // otherwise just clear + self.impulse = 0; + print("impulse\n"); + } + maxspd_mod = self.spectatorspeed; + } spd = sv_maxspeed * maxspd_mod * swampspd_mod; diff --git a/data/qcsrc/server/miscfunctions.qc b/data/qcsrc/server/miscfunctions.qc index 2d1145534..0440e77af 100644 --- a/data/qcsrc/server/miscfunctions.qc +++ b/data/qcsrc/server/miscfunctions.qc @@ -110,6 +110,8 @@ void move_out_of_solid(entity e) } string STR_PLAYER = "player"; +string STR_SPECTATOR = "spectator"; +string STR_OBSERVER = "observer"; #if 0 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; ) @@ -1137,3 +1139,21 @@ void precache() ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE); } } + +#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) \ + entity varname; \ + varname = msg_entity; \ + FOR_EACH_REALCLIENT(msg_entity) \ + if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) \ + statement \ + msg_entity = varname +#define WRITESPECTATABLE_MSG_ONE(statement) \ + WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement) +#define WRITESPECTATABLE(msg,statement) \ + if(msg == MSG_ONE) \ + { \ + WRITESPECTATABLE_MSG_ONE(statement); \ + } \ + else \ + statement \ + float WRITESPECTATABLE_workaround = 0 diff --git a/data/qcsrc/server/race.qc b/data/qcsrc/server/race.qc index 026d20d11..bd871a75c 100644 --- a/data/qcsrc/server/race.qc +++ b/data/qcsrc/server/race.qc @@ -38,9 +38,6 @@ void race_SendNextCheckpoint(entity e) string recordholder; float cp; - if(clienttype(e) != CLIENTTYPE_REAL) - return; - if(!e.race_laptime) return; @@ -55,12 +52,14 @@ void race_SendNextCheckpoint(entity e) recordholder = ""; msg_entity = e; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); - WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_NEXT_QUALIFYING); - WriteByte(MSG_ONE, cp); // checkpoint the player will be at next - WriteShort(MSG_ONE, recordtime); - WriteString(MSG_ONE, recordholder); + WRITESPECTATABLE_MSG_ONE({ + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_NEXT_QUALIFYING); + WriteByte(MSG_ONE, cp); // checkpoint the player will be at next + WriteShort(MSG_ONE, recordtime); + WriteString(MSG_ONE, recordholder); + }); } void race_SendTime(entity e, float cp, float t, float tvalid) @@ -95,11 +94,13 @@ void race_SendTime(entity e, float cp, float t, float tvalid) e.race_completed = 1; MAKE_INDEPENDENT_PLAYER(e); bprint(e.netname, "^7 has finished the race.\n"); + // TODO support spectators with this (e.g. set a flag for forced sbar) if(clienttype(e) == CLIENTTYPE_REAL) { msg_entity = e; WriteByte(MSG_ONE, SVC_TEMPENTITY); WriteByte(MSG_ONE, TE_CSQC_FORCESCOREBOARD); + WriteByte(MSG_ONE, 1); // he can still move, but will see the scoreboard now } } @@ -173,11 +174,10 @@ void race_SendTime(entity e, float cp, float t, float tvalid) recordholder = ""; } - if(clienttype(e) == CLIENTTYPE_REAL) + msg_entity = e; + if(g_race_qualifying) { - msg_entity = e; - if(g_race_qualifying) - { + WRITESPECTATABLE_MSG_ONE({ WriteByte(MSG_ONE, SVC_TEMPENTITY); WriteByte(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING); @@ -185,7 +185,7 @@ void race_SendTime(entity e, float cp, float t, float tvalid) WriteShort(MSG_ONE, t); // time to that intermediate WriteShort(MSG_ONE, recordtime); // previously best time WriteString(MSG_ONE, recordholder); // record holder - } + }); } } else // RACE! Not Qualifying @@ -202,9 +202,8 @@ void race_SendTime(entity e, float cp, float t, float tvalid) else lself = lother = othtime = 0; - if(clienttype(e) == CLIENTTYPE_REAL) - { - msg_entity = e; + msg_entity = e; + WRITESPECTATABLE_MSG_ONE({ WriteByte(MSG_ONE, SVC_TEMPENTITY); WriteByte(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE); @@ -221,16 +220,14 @@ void race_SendTime(entity e, float cp, float t, float tvalid) WriteByte(MSG_ONE, lself - lother); WriteString(MSG_ONE, oth.netname); // record holder } - } + }); race_checkpoint_lastplayers[cp] = e; race_checkpoint_lasttimes[cp] = time; race_checkpoint_lastlaps[cp] = lself; - if(clienttype(oth) == CLIENTTYPE_REAL) - if(oth.flags & FL_CLIENT) - { - msg_entity = oth; + msg_entity = oth; + WRITESPECTATABLE_MSG_ONE({ WriteByte(MSG_ONE, SVC_TEMPENTITY); WriteByte(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT); @@ -247,7 +244,7 @@ void race_SendTime(entity e, float cp, float t, float tvalid) WriteByte(MSG_ONE, lother - lself); WriteString(MSG_ONE, e.netname); // record holder } - } + }); } } @@ -256,13 +253,12 @@ void race_ClearTime(entity e) e.race_checkpoint = -1; e.race_laptime = 0; - if(clienttype(e) != CLIENTTYPE_REAL) - return; - msg_entity = e; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); - WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_CLEAR); // next + WRITESPECTATABLE_MSG_ONE({ + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_CLEAR); // next + }); } void checkpoint_touch() @@ -378,11 +374,13 @@ void race_PreSpawn() self.race_completed = 1; MAKE_INDEPENDENT_PLAYER(self); bprint(self.netname, "^7 has abandoned the race.\n"); + // TODO support spectators with this (e.g. set a flag for forced sbar) if(clienttype(self) == CLIENTTYPE_REAL) { msg_entity = self; WriteByte(MSG_ONE, SVC_TEMPENTITY); WriteByte(MSG_ONE, TE_CSQC_FORCESCOREBOARD); + WriteByte(MSG_ONE, 1); // he can still move, but will see the scoreboard now } } diff --git a/data/qcsrc/server/teamplay.qc b/data/qcsrc/server/teamplay.qc index 92ad96cb4..857e582eb 100644 --- a/data/qcsrc/server/teamplay.qc +++ b/data/qcsrc/server/teamplay.qc @@ -344,7 +344,7 @@ void InitGameplayMode() cvar_set("timelimit", ftos(timelimit_override)); } - if(g_race_qualifying) + if(g_race && g_race_qualifying) { race_fraglimit = cvar("fraglimit"); cvar_set("fraglimit", "0"); @@ -407,37 +407,16 @@ void PrintWelcomeMessage(entity pl) if(!self.BUTTON_INFO) { - if(self.classname == "observer") - { - if(g_lms) - { - p = PlayerScore_Add(self, SP_LMS_RANK, 0); - if(p >= 666) - return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "^1Match has already begun\nwait for next round\n\n\n^7press attack to spectate other players")); - else if(p > 0) - return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "^1You have no more lives left\nwait for next round\n\n\n^7press attack to spectate other players")); - } - } - else if(self.classname == "spectator") - { - if(g_lms) - { - p = PlayerScore_Add(self, SP_LMS_RANK, 0); - if(p) - return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode")); - } - if (g_arena) - return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode")); - - local string specString; - specString = strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press jump to play\n^7press attack for next player\npress attack2 for free fly mode"); - - if(time < restart_countdown) //also show the countdown when being a spectator - specString = strcat(specString, "\n\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7"); - else if (timeoutStatus != 0) - specString = strcat(specString, "\n\n", getTimeoutText(1)); - return centerprint_atprio(self, CENTERPRIO_SPAM, specString); - } + // TODO get rid of this too + local string specString; + specString = NEWLINES; + if(time < restart_countdown) //also show the countdown when being a spectator + specString = strcat(specString, "\n\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7"); + else if (timeoutStatus != 0) + specString = strcat(specString, "\n\n", getTimeoutText(1)); + else + return; + return centerprint_atprio(self, CENTERPRIO_SPAM, specString); } if(g_minstagib) -- 2.39.2