]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc
Merge branch 'terencehill/menu_registries' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / gamemodes / gamemode / keepaway / sv_keepaway.qc
index 6952f31389b6e46901c41270c5952906e651c47b..4ba23b197955ca4efdecb6d9ad06a23a178486d1 100644 (file)
@@ -46,20 +46,14 @@ void ka_EventLog(string mode, entity actor) // use an alias for easy changing an
                GameLogEcho(strcat(":ka:", mode, ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : "")));
 }
 
-void ka_TouchEvent(entity this, entity toucher);
 void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated
 {
        if(game_stopped) return;
        vector oldballorigin = this.origin;
 
        if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
-       {
-               entity spot = SelectSpawnPoint(this, true);
-               setorigin(this, spot.origin);
-               this.angles = spot.angles;
-       }
+               setorigin(this, SelectSpawnPoint(this, true).origin);
 
-       makevectors(this.angles);
        set_movetype(this, MOVETYPE_BOUNCE);
        this.velocity = '0 0 200';
        this.angles = '0 0 0';
@@ -78,18 +72,38 @@ void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated
        sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
 }
 
+.float timepoints_counter;
+MUTATOR_HOOKFUNCTION(ka, reset_map_global)
+{
+       FOREACH_CLIENT(true,
+       {
+               it.timepoints_counter = 0;
+       });
+       return true;
+}
+
 void ka_TimeScoring(entity this)
 {
        if(this.owner.ballcarried)
        { // add points for holding the ball after a certain amount of time
+               float timescore = 0;
                if(autocvar_g_keepaway_score_timepoints)
-                       GameRules_scoring_add(this.owner, SCORE, autocvar_g_keepaway_score_timepoints);
+                       timescore = autocvar_g_keepaway_score_timepoints / max(0.001, autocvar_g_keepaway_score_timeinterval);
 
-               GameRules_scoring_add(this.owner, KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds"
-               this.nextthink = time + autocvar_g_keepaway_score_timeinterval;
+               if (timescore)
+                       GameRules_scoring_add_float2int(this.owner, SCORE, timescore, timepoints_counter, 1);
+
+               GameRules_scoring_add(this.owner, KEEPAWAY_BCTIME, 1);
+               this.nextthink = time + 1;
        }
 }
 
+void ka_DamageEvent(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
+{
+       if(ITEM_DAMAGE_NEEDKILL(deathtype))
+               ka_RespawnBall(this);
+}
+
 void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something
 {
        if (!this || game_stopped)
@@ -124,8 +138,11 @@ void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball c
        this.effects |= EF_NODRAW;
        settouch(this, func_null);
        setthink(this, ka_TimeScoring);
-       this.nextthink = time + autocvar_g_keepaway_score_timeinterval;
+       this.nextthink = time + 1;
        this.takedamage = DAMAGE_NO;
+       this.event_damage = func_null;
+       this.damagedbycontents = false;
+       IL_REMOVE(g_damagedbycontents, this);
        navigation_dynamicgoal_unset(this);
 
        // apply effects to player
@@ -176,8 +193,12 @@ void ka_DropEvent(entity player) // runs any time that a player is supposed to l
        setthink(ball, ka_RespawnBall);
        ball.nextthink = time + autocvar_g_keepawayball_respawntime;
        ball.takedamage = DAMAGE_YES;
+       ball.event_damage = ka_DamageEvent;
+       ball.damagedbycontents = true;
+       IL_PUSH(g_damagedbycontents, ball);
        ball.effects &= ~EF_NODRAW;
        setorigin(ball, player.origin + '0 0 10');
+       nudgeoutofsolid(ball); // a ball has a horizontally bigger bbox than a player
        ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
        ball.owner = NULL;
        navigation_dynamicgoal_set(ball, player);
@@ -200,53 +221,51 @@ void ka_DropEvent(entity player) // runs any time that a player is supposed to l
 
 MODEL(KA_BALL, "models/orbs/orbblue.md3");
 
-void ka_RemoveBall(entity ball)
-{
-       entity player = ball.owner;
-       if (player) // it was attached
-               ka_PlayerReset(player);
-       else
-               WaypointSprite_DetachCarrier(ball);
-       delete(ball);
-}
-
 void ka_RemoveBalls()
 {
        IL_EACH(g_kaballs, true,
        {
-               ka_RemoveBall(it);
+               if (it.owner) // it was attached
+                       ka_PlayerReset(it.owner);
+               else
+                       WaypointSprite_DetachCarrier(it);
+               delete(it);
        });
 }
 
-void ka_SpawnBall()
+void ka_SpawnBalls()
 {
-       entity e = new(keepawayball);
-       setmodel(e, MDL_KA_BALL);
-       setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
-       e.damageforcescale = autocvar_g_keepawayball_damageforcescale;
-       e.takedamage = DAMAGE_YES;
-       e.solid = SOLID_TRIGGER;
-       set_movetype(e, MOVETYPE_BOUNCE);
-       e.glow_color = autocvar_g_keepawayball_trail_color;
-       e.glow_trail = true;
-       e.flags = FL_ITEM;
-       IL_PUSH(g_items, e);
-       e.pushable = true;
-       settouch(e, ka_TouchEvent);
-       e.owner = NULL;
-       IL_PUSH(g_kaballs, e);
-       navigation_dynamicgoal_init(e, false);
-
-       InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So.
-}
-
-void ka_SpawnBalls(int ballcount)
-{
-       int realballcount = max(1, ballcount); // never allow less than 1 ball to spawn
-       for(int j = 0; j < realballcount; ++j)
+       int i = 0;
+       do // never allow less than 1 ball to spawn
        {
-               ka_SpawnBall();
+               entity e = new(keepawayball);
+               setmodel(e, MDL_KA_BALL);
+               e.solid = SOLID_TRIGGER; // before setsize to ensure area grid linking
+               // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
+               // bones_was_here: that was WITH sv_legacy_bbox_expand 1 and FL_ITEM (mins -= '15 15 1'; maxs += '15 15 1')
+               // it's round so should have a symmetrical bbox, same height as pickup items so it can't be jumped over in any physics
+               setsize(e, '-24 -24 -24', '24 24 24');
+               e.damageforcescale = autocvar_g_keepawayball_damageforcescale;
+               e.takedamage = DAMAGE_YES;
+               e.event_damage = ka_DamageEvent;
+               e.damagedbycontents = true;
+               IL_PUSH(g_damagedbycontents, e);
+               set_movetype(e, MOVETYPE_BOUNCE);
+               e.glow_color = autocvar_g_keepawayball_trail_color;
+               e.glow_trail = true;
+               e.flags = FL_ITEM;
+               IL_PUSH(g_items, e);
+               e.pushable = true;
+               settouch(e, ka_TouchEvent);
+               e.owner = NULL;
+               IL_PUSH(g_kaballs, e);
+               navigation_dynamicgoal_init(e, false);
+
+               ka_RespawnBall(e);
+
+               ++i;
        }
+       while (i < KA_BALL_COUNT);
 }
 
 void ka_Handler_CheckBall(entity this)
@@ -259,20 +278,12 @@ void ka_Handler_CheckBall(entity this)
        else
        {
                if (IL_EMPTY(g_kaballs))
-                       ka_SpawnBalls(KA_BALL_COUNT); // ;)
+                       ka_SpawnBalls(); // ;)
        }
 
        this.nextthink = time;
 }
 
-void ka_Initialize() // run at the start of a match, initiates game mode
-{
-       g_kaballs = IL_NEW();
-       ka_Handler = new_pure(ka_Handler);
-       setthink(ka_Handler, ka_Handler_CheckBall);
-       ka_Handler.nextthink = time;
-}
-
 
 // ================
 // Bot player logic
@@ -387,12 +398,8 @@ MUTATOR_HOOKFUNCTION(ka, PlayerPreThink)
 {
        entity player = M_ARGV(0, entity);
 
-       // clear the item used for the ball in keepaway
-       player.items &= ~IT_KEY1;
-
        // if the player has the ball, make sure they have the item for it (Used for HUD primarily)
-       if(player.ballcarried)
-               player.items |= IT_KEY1;
+       STAT(OBJECTIVE_STATUS, player) = BITSET(STAT(OBJECTIVE_STATUS, player), KA_CARRYING, player.ballcarried != NULL);
 }
 
 MUTATOR_HOOKFUNCTION(ka, PlayerUseKey)
@@ -407,42 +414,41 @@ MUTATOR_HOOKFUNCTION(ka, PlayerUseKey)
        }
 }
 
-MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players in damage.qc
+MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
-       float frag_damage = M_ARGV(4, float);
-       vector frag_force = M_ARGV(6, vector);
+
+       // as a gamemode rule, only apply scaling to player versus player combat
+       if(!IS_PLAYER(frag_attacker) || !IS_PLAYER(frag_target))
+               return;
 
        if(frag_attacker.ballcarried) // if the attacker is a ballcarrier
        {
                if(frag_target == frag_attacker) // damage done to yourself
                {
-                       frag_damage *= autocvar_g_keepaway_ballcarrier_selfdamage;
-                       frag_force *= autocvar_g_keepaway_ballcarrier_selfforce;
+                       M_ARGV(4, float) *= autocvar_g_keepaway_ballcarrier_selfdamage;
+                       M_ARGV(6, vector) *= autocvar_g_keepaway_ballcarrier_selfforce;
                }
-               else // damage done to noncarriers
+               else // damage done to other ballcarriers
                {
-                       frag_damage *= autocvar_g_keepaway_ballcarrier_damage;
-                       frag_force *= autocvar_g_keepaway_ballcarrier_force;
+                       M_ARGV(4, float) *= autocvar_g_keepaway_ballcarrier_damage;
+                       M_ARGV(6, vector) *= autocvar_g_keepaway_ballcarrier_force;
                }
        }
-       else if (IS_PLAYER(frag_attacker) && !frag_target.ballcarried) // if the target is a noncarrier
+       else // if the attacker is a noncarrier
        {
                if(frag_target == frag_attacker) // damage done to yourself
                {
-                       frag_damage *= autocvar_g_keepaway_noncarrier_selfdamage;
-                       frag_force *= autocvar_g_keepaway_noncarrier_selfforce;
+                       M_ARGV(4, float) *= autocvar_g_keepaway_noncarrier_selfdamage;
+                       M_ARGV(6, vector) *= autocvar_g_keepaway_noncarrier_selfforce;
                }
                else // damage done to other noncarriers
                {
-                       frag_damage *= autocvar_g_keepaway_noncarrier_damage;
-                       frag_force *= autocvar_g_keepaway_noncarrier_force;
+                       M_ARGV(4, float) *= autocvar_g_keepaway_noncarrier_damage;
+                       M_ARGV(6, vector) *= autocvar_g_keepaway_noncarrier_force;
                }
        }
-
-       M_ARGV(4, float) = frag_damage;
-       M_ARGV(6, vector) = frag_force;
 }
 
 MUTATOR_HOOKFUNCTION(ka, ClientDisconnect)