From e3fd082ea120b2cd953fc769988544357f03e654 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 10 Mar 2014 06:53:29 +1100 Subject: [PATCH] Another attempt to move race and cts to the mutator system --- qcsrc/server/anticheat.qc | 49 +---- qcsrc/server/bot/havocbot/roles.qc | 7 - qcsrc/server/cl_client.qc | 58 +---- qcsrc/server/cl_physics.qc | 18 +- qcsrc/server/cl_player.qc | 3 +- qcsrc/server/cl_weapons.qc | 2 - qcsrc/server/command/getreplies.qc | 5 +- qcsrc/server/command/vote.qc | 4 +- qcsrc/server/defs.qh | 1 - qcsrc/server/g_damage.qc | 7 +- qcsrc/server/miscfunctions.qc | 78 ------- qcsrc/server/mutators/gamemode_cts.qc | 282 +++++++++++++++++++++++++ qcsrc/server/mutators/gamemode_cts.qh | 7 + qcsrc/server/mutators/gamemode_race.qc | 272 ++++++++++++++++++++++++ qcsrc/server/mutators/gamemode_race.qh | 8 + qcsrc/server/mutators/mutators.qh | 2 + qcsrc/server/progs.src | 4 + qcsrc/server/race.qc | 237 +++++++++++---------- qcsrc/server/race.qh | 21 +- qcsrc/server/scores.qc | 3 - qcsrc/server/scores_rules.qc | 28 --- qcsrc/server/sv_main.qc | 2 +- qcsrc/server/teamplay.qc | 17 +- 23 files changed, 725 insertions(+), 390 deletions(-) create mode 100644 qcsrc/server/mutators/gamemode_cts.qc create mode 100644 qcsrc/server/mutators/gamemode_cts.qh create mode 100644 qcsrc/server/mutators/gamemode_race.qc create mode 100644 qcsrc/server/mutators/gamemode_race.qh diff --git a/qcsrc/server/anticheat.qc b/qcsrc/server/anticheat.qc index d00c60b09..11c758ef4 100644 --- a/qcsrc/server/anticheat.qc +++ b/qcsrc/server/anticheat.qc @@ -52,8 +52,7 @@ float movement_oddity(vector m0, vector m1) void anticheat_physics() { - float f, wishspeed; - vector wishvel; + float f; // div0_evade -> SPECTATORS makevectors(self.v_angle); @@ -93,52 +92,6 @@ void anticheat_physics() MEAN_ACCUMULATE(anticheat_speedhack, max(0, f - self.anticheat_speedhack_offset), 1); self.anticheat_speedhack_offset += (f - self.anticheat_speedhack_offset) * frametime * 0.1; } - - // race/CTS: force kbd movement for fairness - if(g_race || g_cts) - { - // if record times matter - // ensure nothing EVIL is being done (i.e. div0_evade) - // this hinders joystick users though - // but it still gives SOME analog control - wishvel_x = fabs(self.movement_x); - wishvel_y = fabs(self.movement_y); - if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y) - { - wishvel_z = 0; - wishspeed = vlen(wishvel); - if(wishvel_x >= 2 * wishvel_y) - { - // pure X motion - if(self.movement_x > 0) - self.movement_x = wishspeed; - else - self.movement_x = -wishspeed; - self.movement_y = 0; - } - else if(wishvel_y >= 2 * wishvel_x) - { - // pure Y motion - self.movement_x = 0; - if(self.movement_y > 0) - self.movement_y = wishspeed; - else - self.movement_y = -wishspeed; - } - else - { - // diagonal - if(self.movement_x > 0) - self.movement_x = M_SQRT1_2 * wishspeed; - else - self.movement_x = -M_SQRT1_2 * wishspeed; - if(self.movement_y > 0) - self.movement_y = M_SQRT1_2 * wishspeed; - else - self.movement_y = -M_SQRT1_2 * wishspeed; - } - } - } } void anticheat_spectatecopy(entity spectatee) diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc index 3e0e60fcc..39d6b23b7 100644 --- a/qcsrc/server/bot/havocbot/roles.qc +++ b/qcsrc/server/bot/havocbot/roles.qc @@ -260,11 +260,6 @@ void havocbot_chooserole_dm() self.havocbot_role = havocbot_role_dm; } -void havocbot_chooserole_race() -{ - self.havocbot_role = havocbot_role_race; -} - void havocbot_chooserole() { dprint("choosing a role...\n"); @@ -273,8 +268,6 @@ void havocbot_chooserole() return; else if (g_keyhunt) havocbot_chooserole_kh(); - else if (g_race || g_cts) - havocbot_chooserole_race(); else if (g_onslaught) havocbot_chooserole_ons(); else // assume anything else is deathmatch diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 691c4930e..f8d3a3da7 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -140,7 +140,6 @@ void PutObserverInServer (void) { entity spot; self.hud = HUD_NORMAL; - race_PreSpawnObserver(); spot = SelectSpawnPoint (TRUE); if(!spot) @@ -154,15 +153,7 @@ void PutObserverInServer (void) WriteEntity(MSG_ONE, self); } - if((g_race && g_race_qualifying) || g_cts) - { - if(PlayerScore_Add(self, SP_RACE_FASTEST, 0)) - self.frags = FRAGS_LMS_LOSER; - else - self.frags = FRAGS_SPECTATOR; - } - else - self.frags = FRAGS_SPECTATOR; + self.frags = FRAGS_SPECTATOR; MUTATOR_CALLHOOK(MakePlayerObserver); @@ -391,8 +382,6 @@ void PutClientInServer (void) if(self.team < 0) JoinBestTeam(self, FALSE, TRUE); - race_PreSpawn(); - spot = SelectSpawnPoint (FALSE); if(!spot) { @@ -562,8 +551,6 @@ void PutClientInServer (void) self.speedrunning = FALSE; - race_PostSpawn(spot); - //stuffcmd(self, "chase_active 0"); //stuffcmd(self, "set viewsize $tmpviewsize \n"); @@ -1057,8 +1044,6 @@ void ClientConnect (void) anticheat_init(); - race_PreSpawnObserver(); - // identify the right forced team if(autocvar_g_campaign) { @@ -1209,27 +1194,7 @@ void ClientConnect (void) else self.hitplotfh = -1; - if(g_race || g_cts) { - string rr; - if(g_cts) - rr = CTS_RECORD; - else - rr = RACE_RECORD; - - msg_entity = self; - race_send_recordtime(MSG_ONE); - race_send_speedaward(MSG_ONE); - - speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"))); - speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"))); - race_send_speedaward_alltimebest(MSG_ONE); - - float i; - for (i = 1; i <= RANKINGS_CNT; ++i) { - race_SendRankings(i, 0, 0, MSG_ONE); - } - } - else if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca) // teamnagger is currently bad for ca + if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts send_CSQC_teamnagger(); CheatInitClient(); @@ -2455,8 +2420,6 @@ void PlayerPreThink (void) if(self.spectatee_status != oldspectatee_status) { ClientData_Touch(self); - if(g_race || g_cts) - race_InitSpectator(); } if(self.teamkill_soundtime) @@ -2615,22 +2578,5 @@ void PlayerPostThink (void) playerdemo_write(); - if((g_cts || g_race) && self.cvar_cl_allow_uidtracking == 1 && self.cvar_cl_allow_uid2name == 1) - { - if (!self.stored_netname) - self.stored_netname = strzone(uid2name(self.crypto_idfp)); - if(self.stored_netname != self.netname) - { - db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname); - strunzone(self.stored_netname); - self.stored_netname = strzone(self.netname); - } - } - - /* - if(g_race) - dprintf("%f %.6f\n", time, race_GetFractionalLapCount(self)); - */ - CSQCMODEL_AUTOUPDATE(); } diff --git a/qcsrc/server/cl_physics.qc b/qcsrc/server/cl_physics.qc index 009856137..af868f6e6 100644 --- a/qcsrc/server/cl_physics.qc +++ b/qcsrc/server/cl_physics.qc @@ -1249,22 +1249,22 @@ void SV_PlayerPhysics() } } - if((g_cts || g_race) && !IS_OBSERVER(self)) { - if(vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed) { + if((g_cts || g_race) && !IS_OBSERVER(self)) + { + if(vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed) + { speedaward_speed = vlen(self.velocity - self.velocity_z * '0 0 1'); speedaward_holder = self.netname; speedaward_uid = self.crypto_idfp; speedaward_lastupdate = time; } - if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) { - string rr; - if(g_cts) - rr = CTS_RECORD; - else - rr = RACE_RECORD; + if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) + { + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; race_send_speedaward(MSG_ALL); speedaward_lastsent = speedaward_speed; - if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") { + if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") + { speedaward_alltimebest = speedaward_speed; speedaward_alltimebest_holder = speedaward_holder; speedaward_alltimebest_uid = speedaward_uid; diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 67738c4c6..bc93ef263 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -414,7 +414,7 @@ void calculate_player_respawn_time() else self.respawn_countdown = -1; // do not count down - if(g_cts || autocvar_g_forced_respawn) + if(autocvar_g_forced_respawn) self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; } @@ -652,7 +652,6 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht // print an obituary message Obituary (attacker, inflictor, self, deathtype); - race_PreDie(); // increment frag counter for used weapon type float w; diff --git a/qcsrc/server/cl_weapons.qc b/qcsrc/server/cl_weapons.qc index 2885e6dc5..1c05aca78 100644 --- a/qcsrc/server/cl_weapons.qc +++ b/qcsrc/server/cl_weapons.qc @@ -301,8 +301,6 @@ float W_IsWeaponThrowable(float w) return 0; if (g_weaponarena) return 0; - if (g_cts) - return 0; if (g_nexball && w == WEP_GRENADE_LAUNCHER) return 0; if(w == 0) diff --git a/qcsrc/server/command/getreplies.qc b/qcsrc/server/command/getreplies.qc index b8979155d..ec6adadee 100644 --- a/qcsrc/server/command/getreplies.qc +++ b/qcsrc/server/command/getreplies.qc @@ -113,10 +113,7 @@ string getladder() float i, j, k, uidcnt = 0, thiscnt; string s, temp_s, rr, myuid, thisuid; - if(g_cts) - rr = CTS_RECORD; - else - rr = RACE_RECORD; + rr = (g_cts) ? CTS_RECORD : RACE_RECORD; for(k = 0; k < MapInfo_count; ++k) { diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index f92a0a40c..fdb53ace9 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -326,9 +326,7 @@ void reset_map(float dorespawn) if(time <= game_starttime && round_handler_IsActive()) round_handler_Reset(game_starttime); - if(g_race || g_cts) - race_ReadyRestart(); - else MUTATOR_CALLHOOK(reset_map_global); + MUTATOR_CALLHOOK(reset_map_global); for(self = world; (self = nextent(self)); ) if(IS_NOT_A_CLIENT(self)) diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index c32ef7716..a972bbeac 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -20,7 +20,6 @@ float g_cloaked, g_footsteps, g_grappling_hook, g_minstagib; float g_warmup_limit; float g_warmup_allguns; float g_warmup_allow_timeout; -float g_race_qualifying; float warmup_stage; float g_pickup_respawntime_weapon; float g_pickup_respawntime_superweapon; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 45319ca07..e11cc5eed 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -713,12 +713,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float } if (targ == attacker) - { - if(g_cts && !autocvar_g_cts_selfdamage) - damage = 0; - else - damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself - } + damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself // count the damage if(attacker) diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 5dc6f2e39..1dd98c384 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -957,8 +957,6 @@ void readlevelcvars(void) sv_clones = cvar("sv_clones"); sv_foginterval = cvar("sv_foginterval"); g_cloaked = cvar("g_cloaked"); - if(g_cts) - g_cloaked = 1; // always enable cloak in CTS g_footsteps = cvar("g_footsteps"); g_grappling_hook = cvar("g_grappling_hook"); g_jetpack = cvar("g_jetpack"); @@ -1826,82 +1824,6 @@ string uid2name(string myuid) { return s; } -float race_readTime(string map, float pos) -{ - string rr; - if(g_cts) - rr = CTS_RECORD; - else - rr = RACE_RECORD; - - return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos)))); -} - -string race_readUID(string map, float pos) -{ - string rr; - if(g_cts) - rr = CTS_RECORD; - else - rr = RACE_RECORD; - - return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))); -} - -float race_readPos(string map, float t) { - float i; - for (i = 1; i <= RANKINGS_CNT; ++i) - if (race_readTime(map, i) == 0 || race_readTime(map, i) > t) - return i; - - return 0; // pos is zero if unranked -} - -void race_writeTime(string map, float t, string myuid) -{ - string rr; - if(g_cts) - rr = CTS_RECORD; - else - rr = RACE_RECORD; - - float newpos; - newpos = race_readPos(map, t); - - float i, prevpos = 0; - for(i = 1; i <= RANKINGS_CNT; ++i) - { - if(race_readUID(map, i) == myuid) - prevpos = i; - } - if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs - for (i = prevpos; i > newpos; --i) { - db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1))); - db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1)); - } - } else { // player has no ranked record yet - for (i = RANKINGS_CNT; i > newpos; --i) { - db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1))); - db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1)); - } - } - - // store new time itself - db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t)); - db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid); -} - -string race_readName(string map, float pos) -{ - string rr; - if(g_cts) - rr = CTS_RECORD; - else - rr = RACE_RECORD; - - return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)))); -} - float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance) { float m, i; diff --git a/qcsrc/server/mutators/gamemode_cts.qc b/qcsrc/server/mutators/gamemode_cts.qc new file mode 100644 index 000000000..33e656807 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_cts.qc @@ -0,0 +1,282 @@ +void cts_ScoreRules() +{ + ScoreRules_basics(0, 0, 0, FALSE); + if(g_race_qualifying) + { + ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); + } + else + { + ScoreInfo_SetLabel_PlayerScore(SP_CTS_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_CTS_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); + } + ScoreRules_basics_end(); +} + +MUTATOR_HOOKFUNCTION(cts_PlayerPhysics) +{ + // force kbd movement for fairness + float wishspeed; + vector wishvel; + + // if record times matter + // ensure nothing EVIL is being done (i.e. div0_evade) + // this hinders joystick users though + // but it still gives SOME analog control + wishvel_x = fabs(self.movement_x); + wishvel_y = fabs(self.movement_y); + if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y) + { + wishvel_z = 0; + wishspeed = vlen(wishvel); + if(wishvel_x >= 2 * wishvel_y) + { + // pure X motion + if(self.movement_x > 0) + self.movement_x = wishspeed; + else + self.movement_x = -wishspeed; + self.movement_y = 0; + } + else if(wishvel_y >= 2 * wishvel_x) + { + // pure Y motion + self.movement_x = 0; + if(self.movement_y > 0) + self.movement_y = wishspeed; + else + self.movement_y = -wishspeed; + } + else + { + // diagonal + if(self.movement_x > 0) + self.movement_x = M_SQRT1_2 * wishspeed; + else + self.movement_x = -M_SQRT1_2 * wishspeed; + if(self.movement_y > 0) + self.movement_y = M_SQRT1_2 * wishspeed; + else + self.movement_y = -M_SQRT1_2 * wishspeed; + } + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_ResetMap) +{ + float s; + + Score_NicePrint(world); + + race_ClearRecords(); + PlayerScore_Sort(race_place, 0, 1, 0); + + entity e; + FOR_EACH_CLIENT(e) + { + if(e.race_place) + { + s = PlayerScore_Add(e, SP_RACE_FASTEST, 0); + if(!s) + e.race_place = 0; + } + print(e.netname, " = ", ftos(e.race_place), "\n"); + } + + if(g_race_qualifying == 2) + { + g_race_qualifying = 0; + independent_players = 0; + cvar_set("fraglimit", ftos(race_fraglimit)); + cvar_set("leadlimit", ftos(race_leadlimit)); + cvar_set("timelimit", ftos(race_timelimit)); + cts_ScoreRules(); + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_PlayerPreThink) +{ + if(IS_SPEC(self) || IS_OBSERVER(self)) + if(g_race_qualifying) + if(msg_entity.enemy.race_laptime) + race_SendNextCheckpoint(msg_entity.enemy, 1); + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_ClientConnect) +{ + race_PreparePlayer(); + self.race_checkpoint = -1; + + string rr = CTS_RECORD; + + msg_entity = self; + race_send_recordtime(MSG_ONE); + race_send_speedaward(MSG_ONE); + + speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"))); + speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"))); + race_send_speedaward_alltimebest(MSG_ONE); + + float i; + for (i = 1; i <= RANKINGS_CNT; ++i) + { + race_SendRankings(i, 0, 0, MSG_ONE); + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_MakePlayerObserver) +{ + if(PlayerScore_Add(self, SP_RACE_FASTEST, 0)) + self.frags = FRAGS_LMS_LOSER; + else + self.frags = FRAGS_SPECTATOR; + + race_PreparePlayer(); + self.race_checkpoint = -1; + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_PlayerSpawn) +{ + if(spawn_spot.target == "") + // Emergency: this wasn't a real spawnpoint. Can this ever happen? + race_PreparePlayer(); + + // if we need to respawn, do it right + self.race_respawn_checkpoint = self.race_checkpoint; + self.race_respawn_spotref = spawn_spot; + + self.race_place = 0; + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_PutClientInServer) +{ + if(IS_PLAYER(self)) + if(!gameover) + { + if(self.killcount == -666 /* initial spawn */ || g_race_qualifying) // spawn + race_PreparePlayer(); + else // respawn + race_RetractPlayer(); + + race_AbandonRaceCheck(self); + } + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_PlayerDies) +{ + self.respawn_flags |= RESPAWN_FORCE; + race_AbandonRaceCheck(self); + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_HavocBot_ChooseRule) +{ + self.havocbot_role = havocbot_role_race; + return TRUE; +} + +MUTATOR_HOOKFUNCTION(cts_PlayerPostThink) +{ + if(self.cvar_cl_allow_uidtracking == 1 && self.cvar_cl_allow_uid2name == 1) + { + if (!self.stored_netname) + self.stored_netname = strzone(uid2name(self.crypto_idfp)); + if(self.stored_netname != self.netname) + { + db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname); + strunzone(self.stored_netname); + self.stored_netname = strzone(self.netname); + } + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_ForbidThrowing) +{ + // no weapon dropping in CTS + return TRUE; +} + +MUTATOR_HOOKFUNCTION(cts_FilterItem) +{ + if(self.classname == "droppedweapon") + return TRUE; + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_PlayerDamage) +{ + if(frag_target == frag_attacker || frag_deathtype == DEATH_FALL) + if(!autocvar_g_cts_selfdamage) + frag_damage = 0; + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(cts_ForbidClearPlayerScore) +{ + return TRUE; // in CTS, you don't lose score by observing +} + +void cts_Initialize() +{ + g_cloaked = 1; // always enable cloak in CTS + + cts_ScoreRules(); +} + +MUTATOR_DEFINITION(gamemode_cts) +{ + MUTATOR_HOOK(PlayerPhysics, cts_PlayerPhysics, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_global, cts_ResetMap, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, cts_PlayerPreThink, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientConnect, cts_ClientConnect, CBC_ORDER_ANY); + MUTATOR_HOOK(MakePlayerObserver, cts_MakePlayerObserver, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, cts_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(PutClientInServer, cts_PutClientInServer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, cts_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRule, cts_HavocBot_ChooseRule, CBC_ORDER_ANY); + MUTATOR_HOOK(GetPressedKeys, cts_PlayerPostThink, CBC_ORDER_ANY); + MUTATOR_HOOK(ForbidThrowCurrentWeapon, cts_ForbidThrowing, CBC_ORDER_ANY); + MUTATOR_HOOK(FilterItem, cts_FilterItem, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDamage_Calculate, cts_PlayerDamage, CBC_ORDER_ANY); + MUTATOR_HOOK(ForbidPlayerScore_Clear, cts_ForbidClearPlayerScore, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + cts_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back cts_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + print("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/mutators/gamemode_cts.qh b/qcsrc/server/mutators/gamemode_cts.qh new file mode 100644 index 000000000..f02caf01f --- /dev/null +++ b/qcsrc/server/mutators/gamemode_cts.qh @@ -0,0 +1,7 @@ +float g_race_qualifying; + +// scores +#define ST_CTS_LAPS 1 +#define SP_CTS_LAPS 4 +#define SP_CTS_TIME 5 +#define SP_CTS_FASTEST 6 diff --git a/qcsrc/server/mutators/gamemode_race.qc b/qcsrc/server/mutators/gamemode_race.qc new file mode 100644 index 000000000..e075a4e95 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_race.qc @@ -0,0 +1,272 @@ +void race_ScoreRules() +{ + ScoreRules_basics(race_teams, 0, 0, FALSE); + if(race_teams) + { + ScoreInfo_SetLabel_TeamScore( ST_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); + } + else if(g_race_qualifying) + { + ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); + } + else + { + ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); + } + ScoreRules_basics_end(); +} + +MUTATOR_HOOKFUNCTION(race_PlayerPhysics) +{ + // force kbd movement for fairness + float wishspeed; + vector wishvel; + + // if record times matter + // ensure nothing EVIL is being done (i.e. div0_evade) + // this hinders joystick users though + // but it still gives SOME analog control + wishvel_x = fabs(self.movement_x); + wishvel_y = fabs(self.movement_y); + if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y) + { + wishvel_z = 0; + wishspeed = vlen(wishvel); + if(wishvel_x >= 2 * wishvel_y) + { + // pure X motion + if(self.movement_x > 0) + self.movement_x = wishspeed; + else + self.movement_x = -wishspeed; + self.movement_y = 0; + } + else if(wishvel_y >= 2 * wishvel_x) + { + // pure Y motion + self.movement_x = 0; + if(self.movement_y > 0) + self.movement_y = wishspeed; + else + self.movement_y = -wishspeed; + } + else + { + // diagonal + if(self.movement_x > 0) + self.movement_x = M_SQRT1_2 * wishspeed; + else + self.movement_x = -M_SQRT1_2 * wishspeed; + if(self.movement_y > 0) + self.movement_y = M_SQRT1_2 * wishspeed; + else + self.movement_y = -M_SQRT1_2 * wishspeed; + } + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_ResetMap) +{ + float s; + + Score_NicePrint(world); + + race_ClearRecords(); + PlayerScore_Sort(race_place, 0, 1, 0); + + entity e; + FOR_EACH_CLIENT(e) + { + if(e.race_place) + { + s = PlayerScore_Add(e, SP_RACE_FASTEST, 0); + if(!s) + e.race_place = 0; + } + print(e.netname, " = ", ftos(e.race_place), "\n"); + } + + if(g_race_qualifying == 2) + { + g_race_qualifying = 0; + independent_players = 0; + cvar_set("fraglimit", ftos(race_fraglimit)); + cvar_set("leadlimit", ftos(race_leadlimit)); + cvar_set("timelimit", ftos(race_timelimit)); + race_ScoreRules(); + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_PlayerPreThink) +{ + if(IS_SPEC(self) || IS_OBSERVER(self)) + if(g_race_qualifying) + if(msg_entity.enemy.race_laptime) + race_SendNextCheckpoint(msg_entity.enemy, 1); + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_ClientConnect) +{ + race_PreparePlayer(); + self.race_checkpoint = -1; + + string rr = RACE_RECORD; + + msg_entity = self; + race_send_recordtime(MSG_ONE); + race_send_speedaward(MSG_ONE); + + speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"))); + speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"))); + race_send_speedaward_alltimebest(MSG_ONE); + + float i; + for (i = 1; i <= RANKINGS_CNT; ++i) + { + race_SendRankings(i, 0, 0, MSG_ONE); + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_MakePlayerObserver) +{ + if(g_race_qualifying) + if(PlayerScore_Add(self, SP_RACE_FASTEST, 0)) + self.frags = FRAGS_LMS_LOSER; + else + self.frags = FRAGS_SPECTATOR; + + race_PreparePlayer(); + self.race_checkpoint = -1; + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_PlayerSpawn) +{ + if(spawn_spot.target == "") + // Emergency: this wasn't a real spawnpoint. Can this ever happen? + race_PreparePlayer(); + + // if we need to respawn, do it right + self.race_respawn_checkpoint = self.race_checkpoint; + self.race_respawn_spotref = spawn_spot; + + self.race_place = 0; + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_PutClientInServer) +{ + if(IS_PLAYER(self)) + if(!gameover) + { + if(self.killcount == -666 /* initial spawn */ || g_race_qualifying) // spawn + race_PreparePlayer(); + else // respawn + race_RetractPlayer(); + + race_AbandonRaceCheck(self); + } + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_PlayerDies) +{ + self.respawn_flags |= RESPAWN_FORCE; + race_AbandonRaceCheck(self); + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_HavocBot_ChooseRule) +{ + self.havocbot_role = havocbot_role_race; + return TRUE; +} + +MUTATOR_HOOKFUNCTION(race_PlayerPostThink) +{ + if(self.cvar_cl_allow_uidtracking == 1 && self.cvar_cl_allow_uid2name == 1) + { + if (!self.stored_netname) + self.stored_netname = strzone(uid2name(self.crypto_idfp)); + if(self.stored_netname != self.netname) + { + db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname); + strunzone(self.stored_netname); + self.stored_netname = strzone(self.netname); + } + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_ForbidClearPlayerScore) +{ + if(g_race_qualifying) + return TRUE; // in qualifying, you don't lose score by observing + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(race_GetTeamCount) +{ + ret_float = race_teams; + return FALSE; +} + +void race_Initialize() +{ + race_ScoreRules(); +} + +MUTATOR_DEFINITION(gamemode_race) +{ + MUTATOR_HOOK(PlayerPhysics, race_PlayerPhysics, CBC_ORDER_ANY); + MUTATOR_HOOK(reset_map_global, race_ResetMap, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, race_PlayerPreThink, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientConnect, race_ClientConnect, CBC_ORDER_ANY); + MUTATOR_HOOK(MakePlayerObserver, race_MakePlayerObserver, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, race_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(PutClientInServer, race_PutClientInServer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, race_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRule, race_HavocBot_ChooseRule, CBC_ORDER_ANY); + MUTATOR_HOOK(GetPressedKeys, race_PlayerPostThink, CBC_ORDER_ANY); + MUTATOR_HOOK(ForbidPlayerScore_Clear, race_ForbidClearPlayerScore, CBC_ORDER_ANY); + MUTATOR_HOOK(GetTeamCount, race_GetTeamCount, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + race_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back race_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + print("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/mutators/gamemode_race.qh b/qcsrc/server/mutators/gamemode_race.qh new file mode 100644 index 000000000..32829a25c --- /dev/null +++ b/qcsrc/server/mutators/gamemode_race.qh @@ -0,0 +1,8 @@ +float g_race_qualifying; +float race_teams; + +// scores +#define ST_RACE_LAPS 1 +#define SP_RACE_LAPS 4 +#define SP_RACE_TIME 5 +#define SP_RACE_FASTEST 6 diff --git a/qcsrc/server/mutators/mutators.qh b/qcsrc/server/mutators/mutators.qh index ecab77c88..2ab196b88 100644 --- a/qcsrc/server/mutators/mutators.qh +++ b/qcsrc/server/mutators/mutators.qh @@ -9,6 +9,8 @@ MUTATOR_DECLARATION(gamemode_onslaught); MUTATOR_DECLARATION(gamemode_domination); MUTATOR_DECLARATION(gamemode_lms); MUTATOR_DECLARATION(gamemode_invasion); +MUTATOR_DECLARATION(gamemode_cts); +MUTATOR_DECLARATION(gamemode_race); MUTATOR_DECLARATION(mutator_dodging); MUTATOR_DECLARATION(mutator_invincibleprojectiles); diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 1ae22e202..9426d2d74 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -48,6 +48,8 @@ mutators/gamemode_keepaway.qh mutators/gamemode_nexball.qh mutators/gamemode_lms.qh mutators/gamemode_invasion.qh +mutators/gamemode_cts.qh +mutators/gamemode_race.qh mutators/mutator_dodging.qh mutators/mutator_nades.qh @@ -246,6 +248,8 @@ mutators/gamemode_nexball.qc mutators/gamemode_onslaught.qc mutators/gamemode_lms.qc mutators/gamemode_invasion.qc +mutators/gamemode_cts.qc +mutators/gamemode_race.qc mutators/mutator_invincibleproj.qc mutators/mutator_new_toys.qc mutators/mutator_nix.qc diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index e6d7a43ac..0dbe7d118 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -1,3 +1,72 @@ +float race_readTime(string map, float pos) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos)))); +} + +string race_readUID(string map, float pos) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))); +} + +float race_readPos(string map, float t) +{ + float i; + for (i = 1; i <= RANKINGS_CNT; ++i) + if (race_readTime(map, i) == 0 || race_readTime(map, i) > t) + return i; + + return 0; // pos is zero if unranked +} + +void race_writeTime(string map, float t, string myuid) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + float newpos; + newpos = race_readPos(map, t); + + float i, prevpos = 0; + for(i = 1; i <= RANKINGS_CNT; ++i) + { + if(race_readUID(map, i) == myuid) + prevpos = i; + } + if (prevpos) + { + // player improved his existing record, only have to iterate on ranks between new and old recs + for (i = prevpos; i > newpos; --i) + { + db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1))); + db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1)); + } + } + else + { + // player has no ranked record yet + for (i = RANKINGS_CNT; i > newpos; --i) + { + db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1))); + db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1)); + } + } + + // store new time itself + db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t)); + db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid); +} + +string race_readName(string map, float pos) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)))); +} + + #define MAX_CHECKPOINTS 255 void spawnfunc_target_checkpoint(); @@ -91,13 +160,6 @@ void race_SendNextCheckpoint(entity e, float spec) // qualifying only }); } -void race_InitSpectator() -{ - if(g_race_qualifying) - if(msg_entity.enemy.race_laptime) - race_SendNextCheckpoint(msg_entity.enemy, 1); -} - void race_send_recordtime(float msg) { // send the server best time @@ -136,7 +198,9 @@ void race_SendStatus(float id, entity e) }); } -void race_setTime(string map, float t, string myuid, string mynetname, entity e) { // netname only used TEMPORARILY for printing +void race_setTime(string map, float t, string myuid, string mynetname, entity e) +{ + // netname only used TEMPORARILY for printing float newpos, player_prevpos; newpos = race_readPos(map, t); @@ -156,7 +220,10 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e) race_SendStatus(0, e); // "fail" Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_RACE_FAIL_RANKED, mynetname, player_prevpos, t, oldrec); return; - } else if (!newpos) { // no ranking, time worse than the worst ranked + } + else if (!newpos) + { + // no ranking, time worse than the worst ranked oldrec = race_readTime(GetMapname(), RANKINGS_CNT); race_SendStatus(0, e); // "fail" Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_RACE_FAIL_UNRANKED, mynetname, RANKINGS_CNT, t, oldrec); @@ -178,7 +245,8 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e) // store new ranking race_writeTime(GetMapname(), t, myuid); - if (newpos == 1) { + if (newpos == 1) + { write_recordmarker(e, time - TIME_DECODE(t), TIME_DECODE(t)); race_send_recordtime(MSG_ALL); } @@ -208,7 +276,8 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e) } } -void race_deleteTime(string map, float pos) { +void race_deleteTime(string map, float pos) +{ string rr; if(g_cts) rr = CTS_RECORD; @@ -216,12 +285,15 @@ void race_deleteTime(string map, float pos) { rr = RACE_RECORD; float i; - for (i = pos; i <= RANKINGS_CNT; ++i) { - if (i == RANKINGS_CNT) { + for (i = pos; i <= RANKINGS_CNT; ++i) + { + if (i == RANKINGS_CNT) + { db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), string_null); db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), string_null); } - else { + else + { db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(GetMapname(), i+1))); db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(GetMapname(), i+1)); } @@ -294,7 +366,8 @@ void race_SendTime(entity e, float cp, float t, float tvalid) if(recordholder == e.netname) recordholder = ""; - if(t != 0) { + if(t != 0) + { if(cp == race_timed_checkpoint) { race_setTime(GetMapname(), t, e.crypto_idfp, e.netname, e); @@ -476,7 +549,8 @@ void checkpoint_passed() other.porto_forbidden = 2; // decreased by 1 each StartFrame - if(defrag_ents) { + if(defrag_ents) + { if(self.race_checkpoint == -2) { self.race_checkpoint = other.race_checkpoint; @@ -484,7 +558,8 @@ void checkpoint_passed() float largest_cp_id = 0; float cp_amount = 0; - for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) { + for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) + { cp_amount += 1; if(cp.race_checkpoint > largest_cp_id) // update the finish id if someone hit a new checkpoint { @@ -494,13 +569,15 @@ void checkpoint_passed() race_highest_checkpoint = largest_cp_id + 1; race_timed_checkpoint = largest_cp_id + 1; - for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) { + for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) + { if(cp.race_checkpoint == -2) // set defragcpexists to -1 so that the cp id file will be rewritten when someone finishes defragcpexists = -1; } } } - if(cp_amount == 0) { + if(cp_amount == 0) + { for(cp = world; (cp = find(cp, classname, "target_stopTimer"));) cp.race_checkpoint = 1; race_highest_checkpoint = 1; @@ -675,7 +752,8 @@ void trigger_race_checkpoint_verify() while((l = fgets(fh))) { len = tokenize_console(l); - if(len != 2) { + if(len != 2) + { defragcpexists = -1; // something's wrong in the defrag cp file, set defragcpexists to -1 so that it will be rewritten when someone finishes continue; } @@ -689,19 +767,23 @@ void trigger_race_checkpoint_verify() g_race_qualifying = qual; - if(race_timed_checkpoint) { - if(defrag_ents) { + if(race_timed_checkpoint) + { + if(defrag_ents) + { for(cp = world; (cp = find(cp, classname, "target_startTimer"));) WaypointSprite_UpdateSprites(cp.sprite, "race-start", "", ""); for(cp = world; (cp = find(cp, classname, "target_stopTimer"));) WaypointSprite_UpdateSprites(cp.sprite, "race-finish", "", ""); - for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) { + for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) + { if(cp.race_checkpoint == -2) // something's wrong with the defrag cp file or it has not been written yet, set defragcpexists to -1 so that it will be rewritten when someone finishes defragcpexists = -1; } - if(defragcpexists != -1){ + if(defragcpexists != -1) + { float largest_cp_id = 0; for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) if(cp.race_checkpoint > largest_cp_id) @@ -710,14 +792,17 @@ void trigger_race_checkpoint_verify() cp.race_checkpoint = largest_cp_id + 1; // finish line race_highest_checkpoint = largest_cp_id + 1; race_timed_checkpoint = largest_cp_id + 1; - } else { + } + else + { for(cp = world; (cp = find(cp, classname, "target_stopTimer"));) cp.race_checkpoint = 255; // finish line race_highest_checkpoint = 255; race_timed_checkpoint = 255; } } - else { + else + { for(cp = world; (cp = find(cp, classname, "trigger_race_checkpoint")); ) if(cp.sprite) { @@ -729,11 +814,13 @@ void trigger_race_checkpoint_verify() } } - if(defrag_ents) { + if(defrag_ents) + { entity trigger, targ; for(trigger = world; (trigger = find(trigger, classname, "trigger_multiple")); ) for(targ = world; (targ = find(targ, targetname, trigger.target)); ) - if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") { + if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") + { trigger.wait = 0; trigger.delay = 0; targ.wait = 0; @@ -791,11 +878,7 @@ vector trigger_race_checkpoint_spawn_evalfunc(entity player, entity spot, vector void spawnfunc_trigger_race_checkpoint() { vector o; - if(!g_race && !g_cts) - { - remove(self); - return; - } + if(!g_race && !g_cts) { remove(self); return; } EXACTTRIGGER_INIT; @@ -843,11 +926,7 @@ void spawnfunc_trigger_race_checkpoint() void spawnfunc_target_checkpoint() // defrag entity { vector o; - if(!g_race && !g_cts) - { - remove(self); - return; - } + if(!g_race && !g_cts) { remove(self); return; } defrag_ents = 1; EXACTTRIGGER_INIT; @@ -926,57 +1005,9 @@ void race_RetractPlayer() self.race_checkpoint = self.race_respawn_checkpoint; } -void race_PreDie() -{ - if(!g_race && !g_cts) - return; - - race_AbandonRaceCheck(self); -} - -void race_PreSpawn() -{ - if(!g_race && !g_cts) - return; - if(self.killcount == -666 /* initial spawn */ || g_race_qualifying) // spawn - race_PreparePlayer(); - else // respawn - race_RetractPlayer(); - - race_AbandonRaceCheck(self); -} - -void race_PostSpawn(entity spot) -{ - if(!g_race && !g_cts) - return; - - if(spot.target == "") - // Emergency: this wasn't a real spawnpoint. Can this ever happen? - race_PreparePlayer(); - - // if we need to respawn, do it right - self.race_respawn_checkpoint = self.race_checkpoint; - self.race_respawn_spotref = spot; - - self.race_place = 0; -} - -void race_PreSpawnObserver() -{ - if(!g_race && !g_cts) - return; - race_PreparePlayer(); - self.race_checkpoint = -1; -} - void spawnfunc_info_player_race (void) { - if(!g_race && !g_cts) - { - remove(self); - return; - } + if(!g_race && !g_cts) { remove(self); return; } ++race_spawns; spawnfunc_info_player_deathmatch(); @@ -1010,38 +1041,6 @@ void race_ClearRecords() self = e; } -void race_ReadyRestart() -{ - float s; - - Score_NicePrint(world); - - race_ClearRecords(); - PlayerScore_Sort(race_place, 0, 1, 0); - - entity e; - FOR_EACH_CLIENT(e) - { - if(e.race_place) - { - s = PlayerScore_Add(e, SP_RACE_FASTEST, 0); - if(!s) - e.race_place = 0; - } - print(e.netname, " = ", ftos(e.race_place), "\n"); - } - - if(g_race_qualifying == 2) - { - g_race_qualifying = 0; - independent_players = 0; - cvar_set("fraglimit", ftos(race_fraglimit)); - cvar_set("leadlimit", ftos(race_leadlimit)); - cvar_set("timelimit", ftos(race_timelimit)); - ScoreRules_race(); - } -} - void race_ImposePenaltyTime(entity pl, float penalty, string reason) { if(g_race_qualifying) diff --git a/qcsrc/server/race.qh b/qcsrc/server/race.qh index f6cc54a15..09b4b36ce 100644 --- a/qcsrc/server/race.qh +++ b/qcsrc/server/race.qh @@ -1,14 +1,4 @@ -void race_InitSpectator(); -void race_PreSpawnObserver(); -void race_PreSpawn(); -void race_PostSpawn(entity spot); -void race_PreDie(); -void race_ReadyRestart(); -float race_teams; float race_spawns; -float race_PreviousCheckpoint(float f); -float race_NextCheckpoint(float f); -void race_AbandonRaceCheck(entity p); float race_highest_place_spawn; // number of places; a place higher gets spawned at 0 float race_lowest_place_spawn; // where to spawn in qualifying float race_fraglimit; @@ -18,8 +8,6 @@ float race_timelimit; .float race_started; .float race_completed; float race_completing; -void race_ImposePenaltyTime(entity pl, float penalty, string reason); -void race_StartCompleting(); .float race_movetime; // for reading .float race_movetime_frac; // fractional accumulator for higher accuracy (helper for writing) @@ -28,4 +16,13 @@ void race_StartCompleting(); .float race_respawn_checkpoint; .entity race_respawn_spotref; // try THIS spawn in case you respawn +// definitions for functions used outside race.qc +float race_PreviousCheckpoint(float f); +float race_NextCheckpoint(float f); +void race_AbandonRaceCheck(entity p); +void race_ImposePenaltyTime(entity pl, float penalty, string reason); +void race_StartCompleting(); float race_GetFractionalLapCount(entity e); +float race_readTime(string map, float pos); +string race_readUID(string map, float pos); +string race_readName(string map, float pos); diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index 3a8b830d7..7b3742f01 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -263,9 +263,6 @@ float PlayerScore_Clear(entity player) if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0; - if(g_cts) return 0; // in CTS, you don't lose score by observing - if(g_race && g_race_qualifying) return 0; // in qualifying, you don't lose score by observing - sk = player.scorekeeper; for(i = 0; i < MAX_SCORE; ++i) { diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index c55195c83..fbbf93803 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -65,34 +65,6 @@ void ScoreRules_kh(float teams) ScoreRules_basics_end(); } -// Race stuff -#define ST_RACE_LAPS 1 -#define SP_RACE_LAPS 4 -#define SP_RACE_TIME 5 -#define SP_RACE_FASTEST 6 -void ScoreRules_race() -{ - ScoreRules_basics(race_teams, 0, 0, FALSE); - if(race_teams) - { - ScoreInfo_SetLabel_TeamScore( ST_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); - ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); - } - else if(g_race_qualifying) - { - ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); - } - else - { - ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); - ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); - } - ScoreRules_basics_end(); -} - // Nexball stuff #define ST_NEXBALL_GOALS 1 #define SP_NEXBALL_GOALS 4 diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 920f738ae..33721b066 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -94,7 +94,7 @@ void CreatureFrame (void) { // check for falling damage float velocity_len = vlen(self.velocity); - if(!self.hook.state && !(g_cts && !autocvar_g_cts_selfdamage)) + if(!self.hook.state) { dm = vlen(self.oldvelocity) - velocity_len; // dm is now the velocity DECREASE. Velocity INCREASE should never cause a sound or any damage. if (self.deadflag) diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index e332e55ed..23f4c1963 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -159,7 +159,6 @@ void InitGameplayMode() if(g_race) { - if(autocvar_g_race_teams) { ActivateTeamplay(); @@ -172,6 +171,8 @@ void InitGameplayMode() qualifying_override = autocvar_g_race_qualifying_timelimit_override; fraglimit_override = autocvar_g_race_laps_limit; leadlimit_override = 0; // currently not supported by race + + MUTATOR_ADD(gamemode_race); } if(g_cts) @@ -179,6 +180,7 @@ void InitGameplayMode() g_race_qualifying = 1; fraglimit_override = 0; leadlimit_override = 0; + MUTATOR_ADD(gamemode_cts); } if(g_nexball) @@ -249,12 +251,8 @@ void InitGameplayMode() } if(g_race || g_cts) - { - if(g_race_qualifying) - independent_players = 1; - - ScoreRules_race(); - } + if(g_race_qualifying) + independent_players = 1; InitializeEntity(world, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK); } @@ -421,10 +419,7 @@ void CheckAllowedTeams (entity for_whom) else { // cover anything else by treating it like tdm with no teams spawned - if(g_race) - dm = race_teams; - else - dm = 2; + dm = 2; ret_float = dm; MUTATOR_CALLHOOK(GetTeamCount); -- 2.39.2