]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_freezetag.qc
Merge branch 'master' into mirceakitsune/damage_effects
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_freezetag.qc
index d57a011259ff1210762c19903445ae60577159fc..f56dc59f18f15979d5cdd8e5defb06826babdaf9 100644 (file)
@@ -1,12 +1,16 @@
 void freezetag_Initialize()
 {
        precache_model("models/ice/ice.md3");
-       warmup = time + cvar("g_freezetag_warmup");
+       warmup = time + autocvar_g_start_delay + autocvar_g_freezetag_warmup;
+       ScoreRules_freezetag();
 }
 
 void freezetag_CheckWinner()
 {
-       if(next_round || (time > warmup - cvar("g_freezetag_warmup") && time < warmup))
+       if(time <= game_starttime) // game didn't even start yet! nobody can win in that case.
+               return;
+
+       if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup))
                return; // already waiting for next round to start
 
        if((redalive >= 1 && bluealive >= 1) // counted in arena.qc
@@ -15,52 +19,56 @@ void freezetag_CheckWinner()
                || (bluealive >= 1 && yellowalive >= 1)
                || (bluealive >= 1 && pinkalive >= 1)
                || (yellowalive >= 1 && pinkalive >= 1))
-               return; // we still have active players on two or more teams
-
-       if(redalive + bluealive + yellowalive + pinkalive <= 0)
-       {
-               next_round = time + 5;
-               return;
-       }
+               return; // we still have active players on two or more teams, nobody won yet
 
        entity e, winner;
        string teamname;
+       winner = world;
 
        FOR_EACH_PLAYER(e)
        {
                if(e.freezetag_frozen == 0 && e.classname == "player" && e.health >= 1) // here's one player from the winning team... good
                {
                        winner = e;
-                       TeamScore_AddToTeam(winner.team, ST_SCORE, +1); // just in case a winner isn't found, we do this already here (causes crashes otherwise...)
                        break; // break, we found the winner
                }
        }
 
-       if(winner.team == COLOR_TEAM1)
-               teamname = "^1Red Team";
-       else if(winner.team == COLOR_TEAM2)
-               teamname = "^4Blue Team";
-       else if(winner.team == COLOR_TEAM3)
-               teamname = "^3Yellow Team";
-       else
-               teamname = "^6Pink Team";
-       FOR_EACH_PLAYER(e) {
-               centerprint(e, strcat(teamname, "^5 wins the round, all other teams were frozen.\n"));
+       if(winner != world) // just in case a winner wasn't found
+       {
+               if(winner.team == COLOR_TEAM1)
+                       teamname = "^1Red Team";
+               else if(winner.team == COLOR_TEAM2)
+                       teamname = "^4Blue Team";
+               else if(winner.team == COLOR_TEAM3)
+                       teamname = "^3Yellow Team";
+               else
+                       teamname = "^6Pink Team";
+               FOR_EACH_PLAYER(e) {
+                       centerprint(e, strcat(teamname, "^5 wins the round, all other teams were frozen.\n"));
+               }
+               bprint(teamname, "^5 wins the round since all the other teams were frozen.\n");
+               TeamScore_AddToTeam(winner.team, ST_SCORE, +1);
        }
-       bprint(teamname, "^5 wins the round since all the other teams were frozen.\n");
 
        next_round = time + 5;
 }
 
+// this is needed to allow the player to turn his view around (fixangle can't
+// be used to freeze his view, as that also changes the angles), while not
+// turning that ice object with the player
 void freezetag_Ice_Think()
 {
        setorigin(self, self.owner.origin - '0 0 16');
        self.nextthink = time;
 }
 
-void freezetag_Freeze()
+void freezetag_Freeze(entity attacker)
 {
+       if(self.freezetag_frozen)
+               return;
        self.freezetag_frozen = 1;
+       self.freezetag_revive_progress = 0;
 
        entity ice;
        ice = spawn();
@@ -71,7 +79,13 @@ void freezetag_Freeze()
        ice.frame = floor(random() * 21); // ice model has 20 different looking frames
        setmodel(ice, "models/ice/ice.md3");
 
-       self.movement = '0 0 0';
+       entity oldself;
+       oldself = self;
+       self = ice;
+       freezetag_Ice_Think();
+       self = oldself;
+
+       RemoveGrapplingHook(self);
 
        // add waypoint
        WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE);
@@ -79,11 +93,30 @@ void freezetag_Freeze()
        {
                WaypointSprite_UpdateTeamRadar(self.waypointsprite_attached, RADARICON_WAYPOINT, '0.25 0.90 1');
        }
+
+       if(attacker == self)
+       {
+               // you froze your own dumb self
+               // counted as "suicide" already
+               PlayerScore_Add(self, SP_SCORE, -1);
+       }
+       else if(attacker.classname == "player")
+       {
+               // got frozen by an enemy
+               // counted as "kill" and "death" already
+               PlayerScore_Add(self, SP_SCORE, -1);
+               PlayerScore_Add(attacker, SP_SCORE, +1);
+       }
+       else
+       {
+               // nothing - got frozen by the game type rules themselves
+       }
 }
 
-void freezetag_Unfreeze()
+void freezetag_Unfreeze(entity attacker)
 {
        self.freezetag_frozen = 0;
+       self.freezetag_revive_progress = 0;
 
        // remove the ice block
        entity ice;
@@ -100,38 +133,61 @@ void freezetag_Unfreeze()
 
 MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
 {
-       self.freezetag_frozen = 1;
-       count_alive_players();
+       if(self.freezetag_frozen == 0)
+       {
+               if(self.team == COLOR_TEAM1)
+                       --redalive;
+               else if(self.team == COLOR_TEAM2)
+                       --bluealive;
+               else if(self.team == COLOR_TEAM3)
+                       --yellowalive;
+               else if(self.team == COLOR_TEAM4)
+                       --pinkalive;
+               --totalalive;
+       }
 
-       freezetag_CheckWinner();
-       freezetag_Unfreeze();
+       if(totalspawned > 2) // only check for winners if we had more than two players (one of them left, don't let the other player win just because of that)
+               freezetag_CheckWinner();
+
+       freezetag_Unfreeze(world);
 
        return 1;
 }
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 {
-       // if the player was previously not frozen (should be the case anyway), decrement alive playercount before running CheckWinner
-       if (self.team == COLOR_TEAM1 && self.freezetag_frozen == 0) redalive -= 1;
-       else if (self.team == COLOR_TEAM2 && self.freezetag_frozen == 0) bluealive -= 1;
-       else if (self.team == COLOR_TEAM3 && self.freezetag_frozen == 0) yellowalive -= 1;
-       else if (self.team == COLOR_TEAM4 && self.freezetag_frozen == 0) pinkalive -= 1;
-       
-       freezetag_Freeze();
-
-       centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n"));
+       if(self.freezetag_frozen == 0)
+       {
+               if(self.team == COLOR_TEAM1)
+                       --redalive;
+               else if(self.team == COLOR_TEAM2)
+                       --bluealive;
+               else if(self.team == COLOR_TEAM3)
+                       --yellowalive;
+               else if(self.team == COLOR_TEAM4)
+                       --pinkalive;
+               --totalalive;
+
+        freezetag_Freeze(frag_attacker);
+       }
+
+    if(frag_attacker.classname == STR_PLAYER)
+        centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n"));
+
        if(frag_attacker == frag_target || frag_attacker == world)
        {
-               centerprint(frag_target, "^1You froze yourself.\n");
+        if(frag_target.classname == STR_PLAYER)
+            centerprint(frag_target, "^1You froze yourself.\n");
                bprint("^7", frag_target.netname, "^1 froze himself.\n");
        }
        else
        {
-               centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n"));
+        if(frag_target.classname == STR_PLAYER)
+            centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n"));
                bprint("^7", frag_target.netname, "^1 was frozen by ^7", frag_attacker.netname, ".\n");
        }
 
-       frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P
+       frag_target.health = autocvar_g_balance_health_start; // "respawn" the player :P
 
        freezetag_CheckWinner();
 
@@ -140,14 +196,18 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
 {
-       if(time > warmup) // spawn too late, freeze player
+    freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed
+
+       if(totalspawned == 1 && time > game_starttime) // only one player active on server, start a new match immediately
+       if(!next_round && warmup && (time < warmup - autocvar_g_freezetag_warmup || time > warmup)) // not awaiting next round
        {
-               centerprint(self, "^1You spawned after the round started, you'll spawn as frozen.\n");
-               freezetag_Freeze();
+               next_round = time;
+               return 1;
        }
-       else // we are still in the delay period before the round starts
+       if(warmup && time > warmup) // spawn too late, freeze player
        {
-               freezetag_Unfreeze();
+               centerprint(self, "^1You spawned after the round started, you'll spawn as frozen.\n");
+               freezetag_Freeze(world);
        }
 
        return 1;
@@ -161,53 +221,85 @@ MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill)
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
 {
+       float n;
        vector revive_extra_size;
-       revive_extra_size = '1 1 1' * cvar("g_freezetag_revive_extra_size");
 
-       float teammate_nearby;
+       revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+
+       entity o;
+       o = world;
+       n = 0;
        FOR_EACH_PLAYER(other) if(self != other)
        {
                if(other.freezetag_frozen == 0)
                {
                        if(other.team == self.team)
                        {
-                               teammate_nearby = boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax);
-                               if(teammate_nearby)
-                                       break;
+                               if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+                               {
+                                       if(!o)
+                                               o = other;
+                                       ++n;
+                               }
                        }
                }
        }
 
-       if(teammate_nearby && self.freezetag_frozen == 1)
+       if(n && self.freezetag_frozen) // OK, there is at least one teammate reviving us
        {
-               if(self.freezetag_beginrevive_time == -9999)
-               {
-                       self.freezetag_beginrevive_time = time;
-                       self.freezetag_revive_progress = 0;
-                       other.freezetag_revive_progress = 0;
-               }
-               else
+               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * autocvar_g_freezetag_revive_speed, 1);
+
+               if(self.freezetag_revive_progress >= 1)
                {
-                       self.freezetag_revive_progress = (time - self.freezetag_beginrevive_time) / cvar("g_freezetag_revive_time");
-                       other.freezetag_revive_progress = (time - self.freezetag_beginrevive_time) / cvar("g_freezetag_revive_time");
-                       if(time - self.freezetag_beginrevive_time >= cvar("g_freezetag_revive_time"))
+                       freezetag_Unfreeze(self);
+
+                       // EVERY team mate nearby gets a point (even if multiple!)
+                       FOR_EACH_PLAYER(other) if(self != other)
                        {
-                               freezetag_Unfreeze();
+                               if(other.freezetag_frozen == 0)
+                               {
+                                       if(other.team == self.team)
+                                       {
+                                               if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+                                               {
+                                                       PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
+                                                       PlayerScore_Add(other, SP_SCORE, +1);
+                                               }
+                                       }
+                               }
+                       }
 
-                               centerprint(self, strcat("^5You were revived by ^7", other.netname, ".\n"));
-                               centerprint(other, strcat("^5You revived ^7", self.netname, ".\n"));
-                               bprint("^7", other.netname, "^5 revived ^7", self.netname, ".\n");
+                       if(n > 1)
+                               centerprint(self, strcat("^5You were revived by ^7", o.netname, "^5 et al.\n"));
+                       else
+                               centerprint(self, strcat("^5You were revived by ^7", o.netname, "^5.\n"));
+                       centerprint(o, strcat("^5You revived ^7", self.netname, "^5.\n"));
+                       if(n > 1)
+                               bprint("^7", o.netname, "^5 et al revived ^7", self.netname, "^5.\n");
+                       else
+                               bprint("^7", o.netname, "^5 revived ^7", self.netname, "^5.\n");
+               }
 
-                               self.freezetag_beginrevive_time = -9999;
-                               self.freezetag_revive_progress = 0;
-                               other.freezetag_revive_progress = 0;
+               // now find EVERY teammate within reviving radius, set their revive_progress values correct
+               FOR_EACH_PLAYER(other) if(self != other)
+               {
+                       if(other.freezetag_frozen == 0)
+                       {
+                               if(other.team == self.team)
+                               {
+                                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+                                               other.freezetag_revive_progress = self.freezetag_revive_progress;
+                               }
                        }
                }
        }
-       else if(!teammate_nearby) // only if no teammate is nearby will we reset
+       else if(!n && self.freezetag_frozen) // only if no teammate is nearby will we reset
+       {
+               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
+       }
+       else if(!n)
        {
-               self.freezetag_beginrevive_time = -9999;
-               self.freezetag_revive_progress = 0;
+               self.freezetag_revive_progress = 0; // thawing nobody
        }
 
        return 1;
@@ -216,10 +308,33 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
 MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
 {
        if(self.freezetag_frozen)
+       {
                self.movement = '0 0 0';
+               self.disableclientprediction = 1;
+       }
        return 1;
 }
 
+MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
+{
+    if(g_freezetag)
+    {
+        if(frag_target.freezetag_frozen == 1 && frag_deathtype != DEATH_HURTTRIGGER)
+        {
+            frag_damage = 0;
+            frag_force = frag_force * autocvar_g_freezetag_frozen_force;
+        }
+    }
+    return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
+{
+       if (self.freezetag_frozen)
+               return 1;
+       return 0;
+}
+
 MUTATOR_DEFINITION(gamemode_freezetag)
 {
        MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY);
@@ -229,6 +344,8 @@ MUTATOR_DEFINITION(gamemode_freezetag)
        MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
+       MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_FIRST); //first, last or any? dunno.
 
        MUTATOR_ONADD
        {