Merge branch 'master' into Mario/race_cts_mutators
authorMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 14:02:30 +0000 (01:02 +1100)
committerMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 14:02:30 +0000 (01:02 +1100)
24 files changed:
qcsrc/client/shownames.qc
qcsrc/server/anticheat.qc
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/command/getreplies.qc
qcsrc/server/command/vote.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_cts.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_cts.qh [new file with mode: 0644]
qcsrc/server/mutators/gamemode_race.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_race.qh [new file with mode: 0644]
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src
qcsrc/server/race.qc
qcsrc/server/race.qh
qcsrc/server/scores.qc
qcsrc/server/scores_rules.qc
qcsrc/server/sv_main.qc
qcsrc/server/teamplay.qc

index 413eaaf..5deba45 100644 (file)
@@ -104,7 +104,7 @@ void Draw_ShowNames(entity ent)
                if(!ent.sameteam || (ent.sv_entnum == player_localentnum))
                        ent.alpha *= getplayeralpha(ent.sv_entnum-1);
 
-               if(ent.alpha < ALPHA_MIN_VISIBLE)
+               if(ent.alpha < ALPHA_MIN_VISIBLE && gametype != MAPINFO_TYPE_CTS)
                        return;
 
                float dist;
index 958504b..33c8291 100644 (file)
@@ -76,8 +76,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);
@@ -160,52 +159,6 @@ void anticheat_physics()
                self.anticheat_speedhack_accu = 1;
                self.anticheat_speedhack_lasttime = servertime;
        }
-
-       // 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)
index 45f5a6e..2104c34 100644 (file)
@@ -221,50 +221,11 @@ void havocbot_role_dm()
        }
 }
 
-//Race:
-//go to next checkpoint, and annoy enemies
-.float race_checkpoint;
-void havocbot_role_race()
-{
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       entity e;
-       if (self.bot_strategytime < time)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-               /*
-               havocbot_goalrating_items(100, self.origin, 10000);
-               havocbot_goalrating_enemyplayers(500, self.origin, 20000);
-               */
-
-               for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
-               {
-                       if(e.cnt == self.race_checkpoint)
-                       {
-                               navigation_routerating(e, 1000000, 5000);
-                       }
-                       else if(self.race_checkpoint == -1)
-                       {
-                               navigation_routerating(e, 1000000, 5000);
-                       }
-               }
-
-               navigation_goalrating_end();
-       }
-}
-
 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 +234,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
index bb24fd6..4f6e29c 100644 (file)
@@ -1,6 +1,3 @@
-void race_send_recordtime(float msg);
-void race_SendRankings(float pos, float prevpos, float del, float msg);
-
 void send_CSQC_teamnagger() {
        WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
        WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
@@ -140,7 +137,6 @@ void PutObserverInServer (void)
 {
        entity  spot;
     self.hud = HUD_NORMAL;
-       race_PreSpawnObserver();
 
        spot = SelectSpawnPoint (TRUE);
        if(!spot)
@@ -154,15 +150,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 +379,6 @@ void PutClientInServer (void)
                if(self.team < 0)
                        JoinBestTeam(self, FALSE, TRUE);
 
-               race_PreSpawn();
-
                spot = SelectSpawnPoint (FALSE);
                if(!spot)
                {
@@ -565,8 +551,6 @@ void PutClientInServer (void)
 
                self.speedrunning = FALSE;
 
-               race_PostSpawn(spot);
-
                //stuffcmd(self, "chase_active 0");
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
 
@@ -1060,8 +1044,6 @@ void ClientConnect (void)
 
        anticheat_init();
 
-       race_PreSpawnObserver();
-
        // identify the right forced team
        if(autocvar_g_campaign)
        {
@@ -1212,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();
@@ -2467,8 +2429,6 @@ void PlayerPreThink (void)
        if(self.spectatee_status != oldspectatee_status)
        {
                ClientData_Touch(self);
-               if(g_race || g_cts)
-                       race_InitSpectator();
        }
 
        if(self.teamkill_soundtime)
@@ -2627,22 +2587,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();
 }
index 7563cf5..5051804 100644 (file)
@@ -1250,22 +1250,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;
index 0fe71f1..3a687ec 100644 (file)
@@ -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;
index 2885e6d..1c05aca 100644 (file)
@@ -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)
index 29e817e..aadc629 100644 (file)
@@ -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)
        {
index f92a0a4..fdb53ac 100644 (file)
@@ -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))
index c32ef77..a972bbe 100644 (file)
@@ -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;
index 42dee6a..7cc48be 100644 (file)
@@ -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)
index 5defa9b..a5311d5 100644 (file)
@@ -958,8 +958,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");
@@ -1817,82 +1815,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 (file)
index 0000000..ef4d53d
--- /dev/null
@@ -0,0 +1,314 @@
+// legacy bot roles
+.float race_checkpoint;
+void havocbot_role_cts()
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       entity e;
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+
+               for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
+               {
+                       if(e.cnt == self.race_checkpoint)
+                       {
+                               navigation_routerating(e, 1000000, 5000);
+                       }
+                       else if(self.race_checkpoint == -1)
+                       {
+                               navigation_routerating(e, 1000000, 5000);
+                       }
+               }
+
+               navigation_goalrating_end();
+       }
+}
+
+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;
+       
+       if(IS_REAL_CLIENT(self))
+       {
+               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_BotRoles)
+{
+       self.havocbot_role = havocbot_role_cts;
+       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_ChooseRole, cts_BotRoles, 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 (file)
index 0000000..f02caf0
--- /dev/null
@@ -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 (file)
index 0000000..fd438d8
--- /dev/null
@@ -0,0 +1,306 @@
+// legacy bot roles
+.float race_checkpoint;
+void havocbot_role_race()
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       entity e;
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+
+               for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
+               {
+                       if(e.cnt == self.race_checkpoint)
+                       {
+                               navigation_routerating(e, 1000000, 5000);
+                       }
+                       else if(self.race_checkpoint == -1)
+                       {
+                               navigation_routerating(e, 1000000, 5000);
+                       }
+               }
+
+               navigation_goalrating_end();
+       }
+}
+
+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;
+
+       if(IS_REAL_CLIENT(self))
+       {
+               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_BotRoles)
+{
+       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();
+       if(g_race_qualifying == 2)
+               warmup_stage = 0;
+}
+
+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_ChooseRole, race_BotRoles, 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 (file)
index 0000000..32829a2
--- /dev/null
@@ -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
index ab519e9..a28a459 100644 (file)
@@ -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);
index 812a1d3..07683fc 100644 (file)
@@ -50,6 +50,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
 mutators/mutator_buffs.qh
@@ -255,6 +257,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
index e6d7a43..8cc7d32 100644 (file)
@@ -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();
@@ -72,6 +141,9 @@ void race_SendNextCheckpoint(entity e, float spec) // qualifying only
        if(recordholder == e.netname)
                recordholder = "";
 
+       if(!IS_REAL_CLIENT(e))
+               return;
+
        if(!spec)
                msg_entity = e;
        WRITESPECTATABLE_MSG_ONE({
@@ -91,13 +163,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
@@ -121,6 +186,9 @@ void race_SendRankings(float pos, float prevpos, float del, float msg)
 
 void race_SendStatus(float id, entity e)
 {
+       if(!IS_REAL_CLIENT(e))
+               return;
+
        float msg;
        if (id == 0)
                msg = MSG_ONE;
@@ -136,7 +204,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 +226,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 +251,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 +282,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 +291,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 +372,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);
@@ -326,18 +405,21 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
                        recordholder = "";
                }
 
-               msg_entity = e;
-               if(g_race_qualifying)
+               if(IS_REAL_CLIENT(e))
                {
-                       WRITESPECTATABLE_MSG_ONE_VARNAME(dummy1, {
-                               WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                               WriteByte(MSG_ONE, TE_CSQC_RACE);
-                               WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING);
-                               WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
-                               WriteInt24_t(MSG_ONE, t); // time to that intermediate
-                               WriteInt24_t(MSG_ONE, recordtime); // previously best time
-                               WriteString(MSG_ONE, recordholder); // record holder
-                       });
+                       msg_entity = e;
+                       if(g_race_qualifying)
+                       {
+                               WRITESPECTATABLE_MSG_ONE_VARNAME(dummy1, {
+                                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                                       WriteByte(MSG_ONE, TE_CSQC_RACE);
+                                       WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING);
+                                       WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
+                                       WriteInt24_t(MSG_ONE, t); // time to that intermediate
+                                       WriteInt24_t(MSG_ONE, recordtime); // previously best time
+                                       WriteString(MSG_ONE, recordholder); // record holder
+                               });
+                       }
                }
        }
        else // RACE! Not Qualifying
@@ -354,49 +436,55 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
                else
                        lself = lother = othtime = 0;
 
-               msg_entity = e;
-               WRITESPECTATABLE_MSG_ONE_VARNAME(dummy2, {
-                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_RACE);
-                       WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE);
-                       WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
-                       if(e == oth)
-                       {
-                               WriteInt24_t(MSG_ONE, 0);
-                               WriteByte(MSG_ONE, 0);
-                               WriteString(MSG_ONE, "");
-                       }
-                       else
-                       {
-                               WriteInt24_t(MSG_ONE, TIME_ENCODE(time - race_checkpoint_lasttimes[cp]));
-                               WriteByte(MSG_ONE, lself - lother);
-                               WriteString(MSG_ONE, oth.netname); // record holder
-                       }
-               });
+               if(IS_REAL_CLIENT(e))
+               {
+                       msg_entity = e;
+                       WRITESPECTATABLE_MSG_ONE_VARNAME(dummy2, {
+                               WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                               WriteByte(MSG_ONE, TE_CSQC_RACE);
+                               WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE);
+                               WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
+                               if(e == oth)
+                               {
+                                       WriteInt24_t(MSG_ONE, 0);
+                                       WriteByte(MSG_ONE, 0);
+                                       WriteString(MSG_ONE, "");
+                               }
+                               else
+                               {
+                                       WriteInt24_t(MSG_ONE, TIME_ENCODE(time - race_checkpoint_lasttimes[cp]));
+                                       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;
 
-               msg_entity = oth;
-               WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, {
-                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_RACE);
-                       WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT);
-                       WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
-                       if(e == oth)
-                       {
-                               WriteInt24_t(MSG_ONE, 0);
-                               WriteByte(MSG_ONE, 0);
-                               WriteString(MSG_ONE, "");
-                       }
-                       else
-                       {
-                               WriteInt24_t(MSG_ONE, TIME_ENCODE(time - othtime));
-                               WriteByte(MSG_ONE, lother - lself);
-                               WriteString(MSG_ONE, e.netname); // record holder
-                       }
-               });
+               if(IS_REAL_CLIENT(oth))
+               {
+                       msg_entity = oth;
+                       WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, {
+                               WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                               WriteByte(MSG_ONE, TE_CSQC_RACE);
+                               WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT);
+                               WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
+                               if(e == oth)
+                               {
+                                       WriteInt24_t(MSG_ONE, 0);
+                                       WriteByte(MSG_ONE, 0);
+                                       WriteString(MSG_ONE, "");
+                               }
+                               else
+                               {
+                                       WriteInt24_t(MSG_ONE, TIME_ENCODE(time - othtime));
+                                       WriteByte(MSG_ONE, lother - lself);
+                                       WriteString(MSG_ONE, e.netname); // record holder
+                               }
+                       });
+               }
        }
 }
 
@@ -408,6 +496,9 @@ void race_ClearTime(entity e)
        e.race_penalty_accumulator = 0;
        e.race_lastpenalty = world;
 
+       if(!IS_REAL_CLIENT(e))
+               return;
+
        msg_entity = e;
        WRITESPECTATABLE_MSG_ONE({
                WriteByte(MSG_ONE, SVC_TEMPENTITY);
@@ -476,7 +567,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 +576,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 +587,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 +770,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 +785,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 +810,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 +832,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 +896,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 +944,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 +1023,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,63 +1059,37 @@ 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)
        {
                pl.race_penalty_accumulator += penalty;
-               msg_entity = pl;
-               WRITESPECTATABLE_MSG_ONE({
-                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_RACE);
-                       WriteByte(MSG_ONE, RACE_NET_PENALTY_QUALIFYING);
-                       WriteShort(MSG_ONE, TIME_ENCODE(penalty));
-                       WriteString(MSG_ONE, reason);
-               });
+               if(IS_REAL_CLIENT(pl))
+               {
+                       msg_entity = pl;
+                       WRITESPECTATABLE_MSG_ONE({
+                               WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                               WriteByte(MSG_ONE, TE_CSQC_RACE);
+                               WriteByte(MSG_ONE, RACE_NET_PENALTY_QUALIFYING);
+                               WriteShort(MSG_ONE, TIME_ENCODE(penalty));
+                               WriteString(MSG_ONE, reason);
+                       });
+               }
        }
        else
        {
                pl.race_penalty = time + penalty;
-               msg_entity = pl;
-               WRITESPECTATABLE_MSG_ONE_VARNAME(dummy, {
-                       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-                       WriteByte(MSG_ONE, TE_CSQC_RACE);
-                       WriteByte(MSG_ONE, RACE_NET_PENALTY_RACE);
-                       WriteShort(MSG_ONE, TIME_ENCODE(penalty));
-                       WriteString(MSG_ONE, reason);
-               });
+               if(IS_REAL_CLIENT(pl))
+               {
+                       msg_entity = pl;
+                       WRITESPECTATABLE_MSG_ONE_VARNAME(dummy, {
+                               WriteByte(MSG_ONE, SVC_TEMPENTITY);
+                               WriteByte(MSG_ONE, TE_CSQC_RACE);
+                               WriteByte(MSG_ONE, RACE_NET_PENALTY_RACE);
+                               WriteShort(MSG_ONE, TIME_ENCODE(penalty));
+                               WriteString(MSG_ONE, reason);
+                       });
+               }
        }
 }
 
index f6cc54a..09b4b36 100644 (file)
@@ -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);
index 3a8b830..7b3742f 100644 (file)
@@ -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)
        {
index c55195c..fbbf938 100644 (file)
@@ -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
index 3a14529..c8a7503 100644 (file)
@@ -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)
index a0c80db..87ea68d 100644 (file)
@@ -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)
@@ -248,12 +250,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);
 }
@@ -420,10 +418,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);