]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/gamemodes/gamemode/nexball/nexball.qc
Merge branch 'master' into Lyberta/TeamplayOverhaul
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / gamemodes / gamemode / nexball / nexball.qc
index ba42962692441141079d57526061e381fa17322a..82e5ed8cf16f683a219912910d873f86426a57f2 100644 (file)
@@ -13,7 +13,7 @@ MUTATOR_HOOKFUNCTION(cl_nb, WantEventchase)
 }
 #endif
 #ifdef SVQC
-.float metertime = _STAT(NB_METERSTART);
+.entity ballcarried;
 
 int autocvar_g_nexball_goalleadlimit;
 #define autocvar_g_nexball_goallimit cvar("g_nexball_goallimit")
@@ -69,14 +69,14 @@ float OtherTeam(float t)  //works only if there are two teams on the map!
        return e.team;
 }
 
-const float ST_NEXBALL_GOALS = 1;
+const int ST_NEXBALL_GOALS = 1;
 void nb_ScoreRules(int teams)
 {
-       ScoreRules_basics(teams, 0, 0, true);
-       ScoreInfo_SetLabel_TeamScore(   ST_NEXBALL_GOALS,  "goals", SFL_SORT_PRIO_PRIMARY);
-       ScoreInfo_SetLabel_PlayerScore( SP_NEXBALL_GOALS,  "goals", SFL_SORT_PRIO_PRIMARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER);
-       ScoreRules_basics_end();
+    GameRules_scoring(teams, 0, 0, {
+        field_team(ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY);
+        field(SP_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY);
+        field(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER);
+    });
 }
 
 void LogNB(string mode, entity actor)
@@ -119,14 +119,16 @@ void relocate_nexball(entity this)
        tracebox(this.origin, BALL_MINS, BALL_MAXS, this.origin, true, this);
        if(trace_startsolid)
        {
-               vector o;
-               o = this.origin;
-               if(!move_out_of_solid(this))
+               vector o = this.origin;
+               if (!move_out_of_solid(this)) {
                        objerror(this, "could not get out of solid at all!");
-               LOG_INFO("^1NOTE: this map needs FIXING. ", this.classname, " at ", vtos(o - '0 0 1'));
-               LOG_INFO(" needs to be moved out of solid, e.g. by '", ftos(this.origin.x - o.x));
-               LOG_INFO(" ", ftos(this.origin.y - o.y));
-               LOG_INFO(" ", ftos(this.origin.z - o.z), "'\n");
+        }
+        LOG_INFOF(
+            "^1NOTE: this map needs FIXING. %s at %s needs to be moved out of solid, e.g. by %s",
+            this.classname,
+            vtos(o - '0 0 1'),
+            vtos(this.origin - o)
+        );
                this.origin = o;
        }
 }
@@ -149,9 +151,10 @@ void GiveBall(entity plyr, entity ball)
        {
                ownr.effects &= ~autocvar_g_nexball_basketball_effects_default;
                ownr.ballcarried = NULL;
-               if(ownr.metertime)
+               GameRules_scoring_vip(ownr, false);
+               if(STAT(NB_METERSTART, ownr))
                {
-                       ownr.metertime = 0;
+                       STAT(NB_METERSTART, ownr) = 0;
                        ownr.(weaponentity).state = WS_READY;
                }
                WaypointSprite_Kill(ownr.waypointsprite_attachedforcarrier);
@@ -171,6 +174,7 @@ void GiveBall(entity plyr, entity ball)
        ball.weaponentity_fld = weaponentity;
        ball.team = plyr.team;
        plyr.ballcarried = ball;
+       GameRules_scoring_vip(plyr, true);
        ball.nb_dropper = plyr;
 
        plyr.effects |= autocvar_g_nexball_basketball_effects_default;
@@ -191,9 +195,9 @@ void GiveBall(entity plyr, entity ball)
                ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold;
        }
 
-       plyr.(weaponentity).weapons = plyr.weapons;
+       STAT(WEAPONS, plyr.(weaponentity)) = STAT(WEAPONS, plyr);
        plyr.m_switchweapon = plyr.(weaponentity).m_weapon;
-       plyr.weapons = WEPSET(NEXBALL);
+       STAT(WEAPONS, plyr) = WEPSET(NEXBALL);
        Weapon w = WEP_NEXBALL;
        w.wr_resetplayer(w, plyr);
        plyr.(weaponentity).m_switchweapon = WEP_NEXBALL;
@@ -217,9 +221,9 @@ void DropBall(entity ball, vector org, vector vel)
        setthink(ball, ResetBall);
        ball.nextthink = min(time + autocvar_g_nexball_delay_idle, ball.teamtime);
 
-       if(ball.owner.metertime)
+       if(STAT(NB_METERSTART, ball.owner))
        {
-               ball.owner.metertime = 0;
+               STAT(NB_METERSTART, ball.owner) = 0;
                .entity weaponentity = ball.weaponentity_fld;
                ball.owner.(weaponentity).state = WS_READY;
        }
@@ -228,8 +232,9 @@ void DropBall(entity ball, vector org, vector vel)
        WaypointSprite_Spawn(WP_NbBall, 0, 0, ball, '0 0 64', NULL, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); // no health bar please
        WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
 
-       ball.owner.ballcarried = NULL;
-       ball.owner = NULL;
+       entity e = ball.owner; ball.owner = NULL;
+       e.ballcarried = NULL;
+       GameRules_scoring_vip(e, false);
 }
 
 void InitBall(entity this)
@@ -428,9 +433,9 @@ void GoalTouch(entity this, entity toucher)
        if(isclient)
        {
                if(pscore > 0)
-                       PlayerScore_Add(ball.pusher, SP_NEXBALL_GOALS, pscore);
+                       GameRules_scoring_add(ball.pusher, NEXBALL_GOALS, pscore);
                else if(pscore < 0)
-                       PlayerScore_Add(ball.pusher, SP_NEXBALL_FAULTS, -pscore);
+                       GameRules_scoring_add(ball.pusher, NEXBALL_FAULTS, -pscore);
        }
 
        if(ball.owner)  // Happens on spawnflag GOAL_TOUCHPLAYER
@@ -630,7 +635,7 @@ void SpawnGoal(entity this)
 
        EXACTTRIGGER_INIT;
 
-       if(this.team != GOAL_OUT && Team_TeamToNumber(this.team) != -1)
+       if(this.team != GOAL_OUT && Team_IsValidTeam(this.team))
        {
                entity wp = WaypointSprite_SpawnFixed(WP_NbGoal, (this.absmin + this.absmax) * 0.5, this, sprite, RADARICON_NONE);
                wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 0.5 0');
@@ -715,136 +720,6 @@ spawnfunc(ball_bound)
        spawnfunc_nexball_out(this);
 }
 
-//=======================//
-//       Weapon code     //
-//=======================//
-
-
-void W_Nexball_Think(entity this)
-{
-       //dprint("W_Nexball_Think\n");
-       //vector new_dir = steerlib_arrive(this.enemy.origin, 2500);
-       vector new_dir = normalize(this.enemy.origin + '0 0 50' - this.origin);
-       vector old_dir = normalize(this.velocity);
-       float _speed = vlen(this.velocity);
-       vector new_vel = normalize(old_dir + (new_dir * autocvar_g_nexball_safepass_turnrate)) * _speed;
-       //vector new_vel = (new_dir * autocvar_g_nexball_safepass_turnrate
-
-       this.velocity = new_vel;
-
-       this.nextthink = time;
-}
-
-void W_Nexball_Touch(entity this, entity toucher)
-{
-       entity ball, attacker;
-       attacker = this.owner;
-       //this.think = func_null;
-       //this.enemy = NULL;
-
-       PROJECTILE_TOUCH(this, toucher);
-       if(attacker.team != toucher.team || autocvar_g_nexball_basketball_teamsteal)
-               if((ball = toucher.ballcarried) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (IS_PLAYER(attacker)))
-               {
-                       toucher.velocity = toucher.velocity + normalize(this.velocity) * toucher.damageforcescale * autocvar_g_balance_nexball_secondary_force;
-                       UNSET_ONGROUND(toucher);
-                       if(!attacker.ballcarried)
-                       {
-                               LogNB("stole", attacker);
-                               _sound(toucher, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM);
-
-                               if(SAME_TEAM(attacker, toucher) && time > CS(attacker).teamkill_complain)
-                               {
-                                       CS(attacker).teamkill_complain = time + 5;
-                                       CS(attacker).teamkill_soundtime = time + 0.4;
-                                       CS(attacker).teamkill_soundsource = toucher;
-                               }
-
-                               GiveBall(attacker, toucher.ballcarried);
-                       }
-               }
-       delete(this);
-}
-
-void W_Nexball_Attack(entity actor, .entity weaponentity, float t)
-{
-       entity ball;
-       float mul, mi, ma;
-       if(!(ball = actor.ballcarried))
-               return;
-
-       W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
-       tracebox(w_shotorg, BALL_MINS, BALL_MAXS, w_shotorg, MOVE_WORLDONLY, NULL);
-       if(trace_startsolid)
-       {
-               if(actor.metertime)
-                       actor.metertime = 0; // Shot failed, hide the power meter
-               return;
-       }
-
-       //Calculate multiplier
-       if(t < 0)
-               mul = 1;
-       else
-       {
-               mi = autocvar_g_nexball_basketball_meter_minpower;
-               ma = max(mi, autocvar_g_nexball_basketball_meter_maxpower); // avoid confusion
-               //One triangle wave period with 1 as max
-               mul = 2 * (t % g_nexball_meter_period) / g_nexball_meter_period;
-               if(mul > 1)
-                       mul = 2 - mul;
-               mul = mi + (ma - mi) * mul; // range from the minimal power to the maximal power
-       }
-
-       DropBall(ball, w_shotorg, W_CalculateProjectileVelocity(actor, actor.velocity, w_shotdir * autocvar_g_balance_nexball_primary_speed * mul, false));
-
-
-       //TODO: use the speed_up cvar too ??
-}
-
-vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
-
-void W_Nexball_Attack2(entity actor, .entity weaponentity)
-{
-       if(actor.ballcarried.enemy)
-       {
-               entity _ball = actor.ballcarried;
-               W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
-               DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
-               setthink(_ball, W_Nexball_Think);
-               _ball.nextthink = time;
-               return;
-       }
-
-       if(!autocvar_g_nexball_tackling)
-               return;
-
-       W_SetupShot(actor, weaponentity, false, 2, SND_NB_SHOOT2, CH_WEAPON_A, 0);
-       entity missile = new(ballstealer);
-
-       missile.owner = actor;
-
-       set_movetype(missile, MOVETYPE_FLY);
-       PROJECTILE_MAKETRIGGER(missile);
-
-       //setmodel(missile, "models/elaser.mdl");  // precision set below
-       setsize(missile, '0 0 0', '0 0 0');
-       setorigin(missile, w_shotorg);
-
-       W_SetupProjVelocity_Basic(missile, autocvar_g_balance_nexball_secondary_speed, 0);
-       missile.angles = vectoangles(missile.velocity);
-       settouch(missile, W_Nexball_Touch);
-       setthink(missile, SUB_Remove);
-       missile.nextthink = time + autocvar_g_balance_nexball_secondary_lifetime; //FIXME: use a distance instead?
-
-       missile.effects = EF_BRIGHTFIELD | EF_LOWPRECISION;
-       missile.flags = FL_PROJECTILE;
-       IL_PUSH(g_projectiles, missile);
-       IL_PUSH(g_bot_dodge, missile);
-
-       CSQCProjectile(missile, true, PROJECTILE_ELECTRO, true);
-}
-
 bool ball_customize(entity this, entity client)
 {
        if(!this.owner)
@@ -872,61 +747,6 @@ bool ball_customize(entity this, entity client)
        return true;
 }
 
-METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity weaponentity, int fire))
-{
-    TC(BallStealer, thiswep);
-    if(fire & 1)
-        if(weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_balance_nexball_primary_refire))
-            if(autocvar_g_nexball_basketball_meter)
-            {
-                if(actor.ballcarried && !actor.metertime)
-                    actor.metertime = time;
-                else
-                    weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
-            }
-            else
-            {
-                W_Nexball_Attack(actor, weaponentity, -1);
-                weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
-            }
-    if(fire & 2)
-        if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_nexball_secondary_refire))
-        {
-            W_Nexball_Attack2(actor, weaponentity);
-            weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
-        }
-
-    if(!(fire & 1) && actor.metertime && actor.ballcarried)
-    {
-        W_Nexball_Attack(actor, weaponentity, time - actor.metertime);
-        // DropBall or stealing will set metertime back to 0
-        weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
-    }
-}
-
-METHOD(BallStealer, wr_setup, void(BallStealer this, entity actor, .entity weaponentity))
-{
-    TC(BallStealer, this);
-    //weapon_setup(WEP_PORTO.m_id);
-}
-
-METHOD(BallStealer, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
-{
-    TC(BallStealer, this);
-}
-
-METHOD(BallStealer, wr_checkammo1, bool(BallStealer this, entity actor, .entity weaponentity))
-{
-    TC(BallStealer, this);
-    return true;
-}
-
-METHOD(BallStealer, wr_checkammo2, bool(BallStealer this, entity actor, .entity weaponentity))
-{
-    TC(BallStealer, this);
-    return true;
-}
-
 void nb_DropBall(entity player)
 {
        if(player.ballcarried && g_nexball)
@@ -1008,15 +828,15 @@ MUTATOR_HOOKFUNCTION(nb, PlayerPreThink)
                        {
                                .entity weaponentity = weaponentities[slot];
 
-                               if(player.(weaponentity).weapons)
+                               if(STAT(WEAPONS, player.(weaponentity)))
                                {
-                                       player.weapons = player.(weaponentity).weapons;
+                                       STAT(WEAPONS, player) = STAT(WEAPONS, player.(weaponentity));
                                        Weapon w = WEP_NEXBALL;
                                        w.wr_resetplayer(w, player);
                                        player.(weaponentity).m_switchweapon = player.m_switchweapon;
                                        W_SwitchWeapon(player, player.(weaponentity).m_switchweapon, weaponentity);
 
-                                       player.(weaponentity).weapons = '0 0 0';
+                                       STAT(WEAPONS, player.(weaponentity)) = '0 0 0';
                                }
                        }
                }
@@ -1031,40 +851,35 @@ MUTATOR_HOOKFUNCTION(nb, SpectateCopy)
        entity spectatee = M_ARGV(0, entity);
        entity client = M_ARGV(1, entity);
 
-       client.metertime = spectatee.metertime;
+       STAT(NB_METERSTART, client) = STAT(NB_METERSTART, spectatee);
 }
 
 MUTATOR_HOOKFUNCTION(nb, PlayerSpawn)
 {
        entity player = M_ARGV(0, entity);
 
-       player.metertime = 0;
+       STAT(NB_METERSTART, player) = 0;
        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
                .entity weaponentity = weaponentities[slot];
-               player.(weaponentity).weapons = '0 0 0';
+               STAT(WEAPONS, player.(weaponentity)) = '0 0 0';
        }
 
        if (nexball_mode & NBM_BASKETBALL)
-               player.weapons |= WEPSET(NEXBALL);
+               STAT(WEAPONS, player) |= WEPSET(NEXBALL);
        else
-               player.weapons = '0 0 0';
+               STAT(WEAPONS, player) = '0 0 0';
 
        return false;
 }
 
-.float stat_sv_airspeedlimit_nonqw;
-.float stat_sv_maxspeed;
-
-MUTATOR_HOOKFUNCTION(nb, PlayerPhysics)
+MUTATOR_HOOKFUNCTION(nb, PlayerPhysics_UpdateStats)
 {
        entity player = M_ARGV(0, entity);
+       // these automatically reset, no need to worry
 
        if(player.ballcarried)
-       {
-               player.stat_sv_airspeedlimit_nonqw *= autocvar_g_nexball_basketball_carrier_highspeed;
-               player.stat_sv_maxspeed *= autocvar_g_nexball_basketball_carrier_highspeed;
-       }
+               STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_nexball_basketball_carrier_highspeed;
 }
 
 MUTATOR_HOOKFUNCTION(nb, ForbidThrowCurrentWeapon)
@@ -1087,7 +902,7 @@ MUTATOR_HOOKFUNCTION(nb, FilterItem)
 {
        entity item = M_ARGV(0, entity);
 
-       if(item.classname == "droppedweapon")
+       if(Item_IsLoot(item))
        if(item.weapon == WEP_NEXBALL.m_id)
                return true;
 
@@ -1105,7 +920,7 @@ MUTATOR_HOOKFUNCTION(nb, ItemTouch)
        return MUT_ITEMTOUCH_CONTINUE;
 }
 
-MUTATOR_HOOKFUNCTION(nb, CheckAllowedTeams)
+MUTATOR_HOOKFUNCTION(nb, TeamBalance_CheckAllowedTeams)
 {
        M_ARGV(1, string) = "nexball_team";
        return true;
@@ -1134,6 +949,7 @@ MUTATOR_HOOKFUNCTION(nb, SendWaypoint)
 
 REGISTER_MUTATOR(nb, g_nexball)
 {
+    MUTATOR_STATIC();
        MUTATOR_ONADD
        {
                g_nexball_meter_period = autocvar_g_nexball_meter_period;
@@ -1153,25 +969,15 @@ REGISTER_MUTATOR(nb, g_nexball)
                InitializeEntity(NULL, nb_delayedinit, INITPRIO_GAMETYPE);
                WEP_NEXBALL.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
 
-               ActivateTeamplay();
-               SetLimits(autocvar_g_nexball_goallimit, autocvar_g_nexball_goalleadlimit, autocvar_timelimit_override, -1);
-               have_team_spawns = -1; // request team spawns
+               GameRules_teams(true);
+               GameRules_limit_score(autocvar_g_nexball_goallimit);
+               GameRules_limit_lead(autocvar_g_nexball_goalleadlimit);
        }
 
        MUTATOR_ONROLLBACK_OR_REMOVE
        {
                WEP_NEXBALL.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
-               // we actually cannot roll back nb_delayedinit here
-               // BUT: we don't need to! If this gets called, adding always
-               // succeeds.
        }
-
-       MUTATOR_ONREMOVE
-       {
-               LOG_INFO("This is a game type and it cannot be removed at runtime.");
-               return -1;
-       }
-
        return 0;
 }