From ba9b0ea3a9bcc7eb1a6b16deb69427fb1f585da9 Mon Sep 17 00:00:00 2001 From: div0 Date: Fri, 8 Aug 2008 18:07:15 +0000 Subject: [PATCH] some missing files; g_race_qualifying is still broken (ignores fraglimit, no idea why) git-svn-id: svn://svn.icculus.org/nexuiz/trunk@4060 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/defaultNexuiz.cfg | 6 + data/qcsrc/client/Defs.qc | 6 + data/qcsrc/client/Main.qc | 39 +++++- data/qcsrc/client/sbar.qc | 84 ++++++++++-- data/qcsrc/common/constants.qh | 7 +- data/qcsrc/server/campaign.qc | 2 - data/qcsrc/server/g_world.qc | 6 + data/qcsrc/server/havocbot_roles.qc | 2 - data/qcsrc/server/miscfunctions.qc | 62 +-------- data/qcsrc/server/race.qc | 199 ++++++++++++++++++++++++++++ data/qcsrc/server/race.qh | 1 + data/qcsrc/server/scores.qc | 10 ++ data/qcsrc/server/scores.qh | 1 + data/qcsrc/server/scores_rules.qc | 12 +- 14 files changed, 352 insertions(+), 85 deletions(-) create mode 100644 data/qcsrc/server/race.qc create mode 100644 data/qcsrc/server/race.qh diff --git a/data/defaultNexuiz.cfg b/data/defaultNexuiz.cfg index df90f8abf..270f2b31e 100644 --- a/data/defaultNexuiz.cfg +++ b/data/defaultNexuiz.cfg @@ -483,6 +483,12 @@ set g_onslaught_cp_health 1000 // assault set g_assault 0 +// race +set g_race 0 +set g_race_qualifying 0 +// Qualifying uses timelimit, and the one with the best time wins. Fraglimit is nonfunctional then. +// Normal race uses fraglimit as a limit for the laps. + // server game balance settings set g_balance_armor_regen 0 set g_balance_armor_rot 0.1 diff --git a/data/qcsrc/client/Defs.qc b/data/qcsrc/client/Defs.qc index 742d98068..f51ec064e 100644 --- a/data/qcsrc/client/Defs.qc +++ b/data/qcsrc/client/Defs.qc @@ -179,3 +179,9 @@ float race_checkpoint; float race_time; float race_laptime; float race_checkpointtime; +float race_delta; +float race_previousbesttime; +string race_previousbestname; +float race_nextcheckpoint; +float race_nextbesttime; +string race_nextbestname; diff --git a/data/qcsrc/client/Main.qc b/data/qcsrc/client/Main.qc index 595f1ee49..a4f4291d2 100644 --- a/data/qcsrc/client/Main.qc +++ b/data/qcsrc/client/Main.qc @@ -568,16 +568,41 @@ void Net_Config() void Net_ReadRace() { float checkpoint, t; + float b; - race_checkpoint = ReadByte(); - race_time = ReadShort(); + b = ReadByte(); - race_checkpointtime = time; + switch(b) + { + case RACE_NET_CHECKPOINT_HIT: + race_checkpoint = ReadByte(); + race_time = ReadShort(); + race_previousbesttime = ReadShort(); + if(race_previousbestname) + strunzone(race_previousbestname); + race_previousbestname = strzone(ReadString()); + + race_checkpointtime = time; + + if(race_checkpoint == 0) + race_laptime = time; // valid + + break; - if(race_checkpoint == 0) - race_laptime = time; // valid - else if(race_checkpoint == 255) - race_laptime = 0; // invalid + case RACE_NET_CHECKPOINT_CLEAR: + race_laptime = 0; + race_checkpointtime = 0; + break; + + case RACE_NET_CHECKPOINT_NEXT: + race_nextcheckpoint = ReadByte(); + + race_nextbesttime = ReadShort(); + if(race_nextbestname) + strunzone(race_nextbestname); + race_nextbestname = strzone(ReadString()); + break; + } } // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer. diff --git a/data/qcsrc/client/sbar.qc b/data/qcsrc/client/sbar.qc index d0ad1312c..fd0636e1e 100644 --- a/data/qcsrc/client/sbar.qc +++ b/data/qcsrc/client/sbar.qc @@ -934,6 +934,53 @@ void Sbar_DrawScoreboard() sbar = sbar_save; } +string MakeRaceString(float cp, float mytime, float histime, string hisname) +{ + string col; + string timestr; + string cpname; + + if(histime == 0) // goal hit + { + if(mytime >= 0) + { + timestr = strcat("+", ftos_decimals(+mytime, 1)); + col = "^1"; + } + else + { + timestr = strcat("-", ftos_decimals(-mytime, 1)); + col = "^2"; + } + } + else if(histime > 0) // anticipation + { + if(mytime >= histime) + timestr = strcat("+", ftos_decimals(mytime - histime, 1)); + else + timestr = mmsss(histime * 10); + col = "^3"; + } + else + col = "^7"; + + if(cp) + cpname = strcat("Intermediate ", ftos(cp)); + else + cpname = "Finish line"; + + if(histime < 0) + return strcat(col, cpname); + else if(hisname == "") + return strcat(col, cpname, " (", timestr, ")"); + else + return strcat(col, cpname, " (", timestr, " ", hisname, col, ")"); +} + +void dummyfunction(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8) +{ +} + void Sbar_Score(float margin) { float timelimit, timeleft, minutes, seconds, distribution, myplace, score; @@ -1038,8 +1085,9 @@ void Sbar_Score(float margin) if(gametype == GAME_RACE) { + drawfont = sbar_bigfont; + if(race_checkpointtime) - if(race_checkpoint != 255) { float a; vector m; @@ -1048,27 +1096,41 @@ void Sbar_Score(float margin) m = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; a = bound(0, 2 - (time - race_checkpointtime), 1); - drawfont = sbar_bigfont; - if(a >= 0) + s = ""; + if(a > 0) // just hit a checkpoint? { - if(race_checkpoint) - s = strcat("Intermediate ", ftos(race_checkpoint)); + if(race_time && race_previousbesttime) + s = MakeRaceString(race_checkpoint, race_time / 10 - race_previousbesttime / 10, 0, race_previousbestname); else - s = strcat("Finish line"); - drawstring(m - '0 88 0' - '12 0 0' * stringwidth(s, FALSE), s, '24 24 0', '0 1 0', sbar_alpha_fg * a, 0); - if(race_time) + s = MakeRaceString(race_checkpoint, 0, -1, race_previousbestname); + } + else + { + if(race_laptime && race_nextbesttime) { - s = mmsss(race_time); - drawstring(m - '0 64 0' - '16 0 0' * stringwidth(s, FALSE), s, '32 32 0', '0 1 0', sbar_alpha_fg * a, 0); + a = bound(0, 2 - ((race_laptime + race_nextbesttime/10) - time), 1); + if(a > 0) // next one? + { + s = MakeRaceString(race_nextcheckpoint, time - race_laptime, race_nextbesttime / 10, race_nextbestname); + } } } + if(s != "") + if(a > 0) + { + dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); // work around DP bug (set OFS_PARAM5 to 0) + drawcolorcodedstring(m - '0 80 0' - '8 0 0' * stringwidth(s, TRUE), s, '16 16 0', sbar_alpha_fg * a, 0); + } + if(race_laptime) { s = mmsss(10*(time - race_laptime)); - drawstring(m - '0 64 0' - '16 0 0' * stringwidth(s, FALSE), s, '32 32 0', '1 1 1', sbar_alpha_fg * (1 - a), 0); + drawstring(m - '0 64 0' - '16 0 0' * stringwidth(s, FALSE), s, '32 32 0', '1 1 1', sbar_alpha_fg, 0); } } + + drawfont = sbar_font; } sbar = sbar_save; diff --git a/data/qcsrc/common/constants.qh b/data/qcsrc/common/constants.qh index a58215b3a..1fa0529e7 100644 --- a/data/qcsrc/common/constants.qh +++ b/data/qcsrc/common/constants.qh @@ -7,7 +7,8 @@ // Revision 6: more robust against packet loss/delays, also show not yet connected clients // Revision 7: packet loss column // Revision 8: race -#define CSQC_REVISION 8 +// Revision 9: race delta +#define CSQC_REVISION 9 // probably put these in common/ // so server/ and client/ can be synced better @@ -201,6 +202,10 @@ const float MAPVOTE_NET_UPDATE = 1; const float MAPVOTE_NET_PIC = 2; const float MAPVOTE_NET_OWNVOTE = 3; +const float RACE_NET_CHECKPOINT_HIT = 0; // byte checkpoint, short time, short recordtime, string recordholder +const float RACE_NET_CHECKPOINT_CLEAR = 1; +const float RACE_NET_CHECKPOINT_NEXT = 2; // byte nextcheckpoint, short recordtime, string recordholder + /** * Lower scores are better (e.g. suicides) */ diff --git a/data/qcsrc/server/campaign.qc b/data/qcsrc/server/campaign.qc index 1a8236f6f..1c7dc7317 100644 --- a/data/qcsrc/server/campaign.qc +++ b/data/qcsrc/server/campaign.qc @@ -81,8 +81,6 @@ void CampaignSaveCvar(string cvarname, float value) { while((l = fgets(fh))) { - cvarname = strcat1(cvarname); - contents = strcat1(contents); len = tokenize(l); if(len != 3) continue; diff --git a/data/qcsrc/server/g_world.qc b/data/qcsrc/server/g_world.qc index 23662ab02..90bd0b127 100644 --- a/data/qcsrc/server/g_world.qc +++ b/data/qcsrc/server/g_world.qc @@ -1488,6 +1488,12 @@ float WinningCondition_Scores(float limit) if(WinningConditionHelper_topscore == 0) WinningConditionHelper_equality = 0; + if(WinningConditionHelper_lowerisbetter) + { + WinningConditionHelper_topscore = -WinningConditionHelper_topscore; + limit = -limit; + } + return GetWinningCode(limit && (WinningConditionHelper_topscore >= limit), WinningConditionHelper_equality); } diff --git a/data/qcsrc/server/havocbot_roles.qc b/data/qcsrc/server/havocbot_roles.qc index 60d5dfbab..4d2d209c6 100644 --- a/data/qcsrc/server/havocbot_roles.qc +++ b/data/qcsrc/server/havocbot_roles.qc @@ -551,12 +551,10 @@ void havocbot_role_race() { if(e.cnt == self.race_checkpoint) { - print("found good checkpoint\n"); navigation_routerating(e, 1000000, 5000); } else if(self.race_checkpoint == -1) { - print("found good checkpoint -1\n"); navigation_routerating(e, 1000000, 5000); } } diff --git a/data/qcsrc/server/miscfunctions.qc b/data/qcsrc/server/miscfunctions.qc index 14d01392d..513f531ed 100644 --- a/data/qcsrc/server/miscfunctions.qc +++ b/data/qcsrc/server/miscfunctions.qc @@ -124,10 +124,8 @@ string STR_PLAYER = "player"; #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER) #endif -// change that to actually calling strcat when running on an engine without -// unlimited tempstrings: -// string strcat1(string s) = #115; // FRIK_FILE -#define strcat1(s) (s) +// copies a string to a tempstring (so one can strunzone it) +string strcat1(string s) = #115; // FRIK_FILE float logfile_open; float logfile; @@ -594,62 +592,6 @@ string Team_ColorNameLowerCase(float t) return "neutral"; } -/* -string decolorize(string s) -{ - string out; - out = ""; - while(s != "") - { - float n; - string ch1, ch2; - n = 1; - ch1 = substring(s, 0, 1); - ch2 = substring(s, 1, 1); - if(ch1 == "^") - { - n = 2; - if(ch2 == "^") - out = strcat(out, "^^"); - else if(ch2 == "0") - out = strcat1(out); - else if(ch2 == "1") - out = strcat1(out); - else if(ch2 == "2") - out = strcat1(out); - else if(ch2 == "3") - out = strcat1(out); - else if(ch2 == "4") - out = strcat1(out); - else if(ch2 == "5") - out = strcat1(out); - else if(ch2 == "6") - out = strcat1(out); - else if(ch2 == "7") - out = strcat1(out); - else if(ch2 == "8") - out = strcat1(out); - else if(ch2 == "9") - out = strcat1(out); - else - { - n = 1; - out = strcat(out, "^^"); - } - s = substring(s, n, strlen(s) - n); - } - else - { - s = substring(s, 1, strlen(s) - 1); - out = strcat(out, ch1); - } - } - return out; -} -#define strdecolorize(s) decolorize(s) -#define strlennocol(s) strlen(decolorize(s)) -*/ - #define CENTERPRIO_POINT 1 #define CENTERPRIO_SPAM 2 #define CENTERPRIO_REBALANCE 2 diff --git a/data/qcsrc/server/race.qc b/data/qcsrc/server/race.qc new file mode 100644 index 000000000..e87ae69bd --- /dev/null +++ b/data/qcsrc/server/race.qc @@ -0,0 +1,199 @@ +.float race_checkpoint; // player: next checkpoint that has to be reached +.float race_laptime; + +float race_checkpoint_records[256]; +string race_checkpoint_recordholders[256]; + +float race_highest_checkpoint; + +void race_SendNextCheckpoint(entity e) +{ + float recordtime; + string recordholder; + float cp; + + if(clienttype(e) != CLIENTTYPE_REAL) + return; + + if(!e.race_laptime) + return; + + cp = e.race_checkpoint; + recordtime = race_checkpoint_records[cp]; + recordholder = race_checkpoint_recordholders[cp]; + /* + recordtime = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/", ftos(cp), "/time"))); + recordholder = db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/", ftos(cp), "/netname")); + */ + if(recordholder == e.netname) + recordholder = ""; + + msg_entity = e; + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_NEXT); + 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) +{ + t = floor(0.5 + 10 * t); // make integer + + if(tvalid) + if(cp == 0) // finish line + { + float s; + s = PlayerScore_Add(e, SP_RACE_FASTEST, 0); + if(!s || t < s) + PlayerScore_Add(e, SP_RACE_FASTEST, t - s); + PlayerScore_Add(e, SP_RACE_LAPS, 1); + } + + float recordtime; + string recordholder; + + if(tvalid) + { + recordtime = race_checkpoint_records[cp]; + recordholder = strcat1(race_checkpoint_recordholders[cp]); // make a tempstring copy, as we'll possibly strunzone it! + if(recordholder == e.netname) + recordholder = ""; + + if(t < recordtime || recordtime == 0) + { + race_checkpoint_records[cp] = t; + if(race_checkpoint_recordholders[cp]) + strunzone(race_checkpoint_recordholders[cp]); + race_checkpoint_recordholders[cp] = strzone(e.netname); + if(cp == 0) + { + float grecordtime; + string grecordholder; + grecordtime = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/time"))); + grecordholder = db_get(ServerProgsDB, strcat(GetMapname(), "/racerecord/netname")); + if(grecordholder == e.netname) + grecordholder = ""; + if(grecordholder == "") + if(grecordtime == 0) + bprint(e.netname, "^7 set the all-time fastest lap record with ", mmsss(t), "\n"); + else + bprint(e.netname, "^7 broke his all-time fastest lap record with ", mmsss(t), "\n"); + else + bprint(e.netname, "^7 broke ", grecordholder, "^7's all-time fastest lap record with ", mmsss(t), "\n"); + db_put(ServerProgsDB, strcat(GetMapname(), "/racerecord/time"), ftos(t)); + db_put(ServerProgsDB, strcat(GetMapname(), "/racerecord/netname"), e.netname); + } + + entity p; + FOR_EACH_REALPLAYER(p) + if(p.race_checkpoint == cp) + race_SendNextCheckpoint(p); + } + } + else + { + // dummies + t = 0; + recordtime = 0; + recordholder = ""; + } + + 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_HIT); + WriteByte(MSG_ONE, cp); // checkpoint the player now is at + WriteShort(MSG_ONE, t); // time to that intermediate + WriteShort(MSG_ONE, recordtime); // previously best time + WriteString(MSG_ONE, recordholder); // record holder + print(race_checkpoint_recordholders[cp], "\n"); +} + +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 +} + +void checkpoint_touch() +{ + if(other.classname != "player") + return; + + if(other.race_checkpoint == -1 || other.race_checkpoint == self.cnt) + { + if(self.cnt == race_highest_checkpoint) + other.race_checkpoint = 0; + else + other.race_checkpoint = self.cnt + 1; + + race_SendTime(other, self.cnt, time - other.race_laptime, !!other.race_laptime); + + if(!self.cnt) // finish line + other.race_laptime = time; + + race_SendNextCheckpoint(other); + } + else if(other.race_checkpoint == self.cnt + 1) + { + // ignored + } + else if(other.race_checkpoint == 0 && self.cnt == race_highest_checkpoint) + { + // ignored + } + else + { + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } +} + +void checkpoint_use() +{ + other = activator; + checkpoint_touch(); +} + +void spawnfunc_trigger_race_checkpoint() +{ + vector o; + if(!g_race) + return; + InitTrigger(); + if(self.targetname) + self.use = checkpoint_use; + else + self.touch = checkpoint_touch; + + o = (self.absmin + self.absmax) * 0.5; + traceline(o, o - '0 0 1' * (o_z - self.absmin_z), MOVE_NORMAL, self); + o = trace_endpos - '0 0 1' * PL_MIN_z; + self.nearestwaypoint = waypoint_spawn(o, o, WAYPOINTFLAG_GENERATED); + self.nearestwaypointtimeout = time + 1000000000; + + if(!self.message) + self.message = "went backwards"; + + if(self.cnt > race_highest_checkpoint) + race_highest_checkpoint = self.cnt; +} + +void race_PreparePlayer() +{ + if(!g_race) + return; + race_ClearTime(self); +} diff --git a/data/qcsrc/server/race.qh b/data/qcsrc/server/race.qh new file mode 100644 index 000000000..3aad26efc --- /dev/null +++ b/data/qcsrc/server/race.qh @@ -0,0 +1 @@ +void race_PreparePlayer(); diff --git a/data/qcsrc/server/scores.qc b/data/qcsrc/server/scores.qc index 37efbb2c0..68bd0ef18 100644 --- a/data/qcsrc/server/scores.qc +++ b/data/qcsrc/server/scores.qc @@ -10,6 +10,8 @@ float teamscores_flags[MAX_TEAMSCORE]; float teamscores_entities_count; var .float scores_primary; var .float teamscores_primary; +float scores_flags_primary; +float teamscores_flags_primary; void Net_LinkEntity(entity e) { @@ -124,7 +126,10 @@ void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags) scores_label[i] = label; scores_flags[i] = scoreflags; if(scoreflags & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY) + { scores_primary = scores[i]; + scores_flags_primary = scoreflags; + } } void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags) @@ -132,7 +137,10 @@ void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags) teamscores_label[i] = label; teamscores_flags[i] = scoreflags; if(scoreflags & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY) + { teamscores_primary = teamscores[i]; + teamscores_flags_primary = scoreflags; + } } void ScoreInfo_Write(float targ) @@ -320,6 +328,7 @@ void WinningConditionHelper() } WinningConditionHelper_topscore = teamscorekeepers[WinningConditionHelper_winnerteam].teamscores_primary; + WinningConditionHelper_lowerisbetter = (teamscores_flags_primary & SFL_LOWER_IS_BETTER); WinningConditionHelper_winner = world; if(WinningConditionHelper_equality) @@ -344,6 +353,7 @@ void WinningConditionHelper() } WinningConditionHelper_topscore = WinningConditionHelper_winner.scorekeeper.scores_primary; + WinningConditionHelper_lowerisbetter = (scores_flags_primary & SFL_LOWER_IS_BETTER); if(WinningConditionHelper_equality) WinningConditionHelper_winner = world; diff --git a/data/qcsrc/server/scores.qh b/data/qcsrc/server/scores.qh index 5b8a7b197..a7bea9c4c 100644 --- a/data/qcsrc/server/scores.qh +++ b/data/qcsrc/server/scores.qh @@ -90,6 +90,7 @@ float WinningConditionHelper_topscore; ///< highest score float WinningConditionHelper_equality; ///< 1 if and only if the top two have equal scores float WinningConditionHelper_winnerteam; ///< the color of the winning team, or -1 if none entity WinningConditionHelper_winner; ///< the winning player, or world if none +float WinningConditionHelper_lowerisbetter; ///< lower is better, duh /** * Returns score strings for eventlog etc. diff --git a/data/qcsrc/server/scores_rules.qc b/data/qcsrc/server/scores_rules.qc index ea89433c9..b708838b0 100644 --- a/data/qcsrc/server/scores_rules.qc +++ b/data/qcsrc/server/scores_rules.qc @@ -126,7 +126,15 @@ void ScoreRules_kh(float teams) void ScoreRules_race() { ScoreRules_basics(0, 0); - ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + if(cvar("g_race_qualifying")) + { + ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + } + else + { + ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", 0); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); + } ScoreRules_basics_end(); } -- 2.39.2