]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_nexball.qc
Merge remote-tracking branch 'origin/master' into divVerent/csad
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_nexball.qc
index 4f889291444103c65d779f6d9aaa2c95830ecf16..62cfd01ad867250673f406bfc68ab7ea99841671 100644 (file)
@@ -1,3 +1,10 @@
+float autocvar_g_nexball_safepass_turnrate;
+float autocvar_g_nexball_safepass_maxdist;
+float autocvar_g_nexball_safepass_holdtime;
+float autocvar_g_nexball_viewmodel_scale;
+float autocvar_g_nexball_tackling;
+vector autocvar_g_nexball_viewmodel_offset;
+
 void basketball_touch();
 void football_touch();
 void ResetBall();
@@ -100,7 +107,7 @@ void GiveBall(entity plyr, entity ball)
        {
                WaypointSprite_Kill(ball.waypointsprite_attachedforcarrier);
        }
-
+       
        //setattachment(ball, plyr, "");
        setorigin(ball, plyr.origin + plyr.view_ofs);
 
@@ -110,14 +117,14 @@ void GiveBall(entity plyr, entity ball)
        ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it
        ball.team = plyr.team;
        plyr.ballcarried = ball;
-       ball.dropperid = plyr.playerid;
+       ball.nb_dropper = plyr;
 
        plyr.effects |= autocvar_g_nexball_basketball_effects_default;
        ball.effects &~= autocvar_g_nexball_basketball_effects_default;
 
        ball.velocity = '0 0 0';
        ball.movetype = MOVETYPE_NONE;
-       ball.touch = SUB_Null;
+       ball.touch = func_null;
        ball.effects |= EF_NOSHADOW;
        ball.scale = 1; // scale down.
 
@@ -129,6 +136,16 @@ void GiveBall(entity plyr, entity ball)
                ball.think = DropOwner;
                ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold;
        }
+       
+       ownr = self;
+       self = plyr;    
+       WEPSET_COPY_EE(self.weaponentity, self);
+       self.weaponentity.switchweapon = self.weapon;
+       WEPSET_COPY_EW(self, WEP_PORTO);
+       weapon_action(WEP_PORTO, WR_RESETPLAYER);
+       self.switchweapon = WEP_PORTO;
+       W_SwitchWeapon(WEP_PORTO);
+       self = ownr;
 }
 
 void DropBall(entity ball, vector org, vector vel)
@@ -143,7 +160,7 @@ void DropBall(entity ball, vector org, vector vel)
        ball.flags &~= FL_ONGROUND;
        ball.scale = ball_scale;
        ball.velocity = vel;
-       ball.ctf_droptime = time;
+       ball.nb_droptime = time;
        ball.touch = basketball_touch;
        ball.think = ResetBall;
        ball.nextthink = min(time + autocvar_g_nexball_delay_idle, ball.teamtime);
@@ -184,11 +201,11 @@ void InitBall(void)
 
 void ResetBall(void)
 {
-       if(self.cnt < 2)    // step 1
+       if(self.cnt < 2)        // step 1
        {
                if(time == self.teamtime)
                        bprint("The ", ColoredTeamName(self.team), " held the ball for too long.\n");
-               self.touch = SUB_Null;
+               self.touch = func_null;
                self.movetype = MOVETYPE_NOCLIP;
                self.velocity = '0 0 0'; // just in case?
                if(!self.cnt)
@@ -196,14 +213,14 @@ void ResetBall(void)
                self.cnt = 2;
                self.nextthink = time;
        }
-       else if(self.cnt < 4)      // step 2 and 3
+       else if(self.cnt < 4)     // step 2 and 3
        {
 //             dprint("Step ", ftos(self.cnt), ": Calculated velocity: ", vtos(self.spawnorigin - self.origin), ", time: ", ftos(time), "\n");
                self.velocity = (self.spawnorigin - self.origin) * (self.cnt - 1); // 1 or 0.5 second movement
                self.nextthink = time + 0.5;
                self.cnt += 1;
        }
-       else     // step 4
+       else     // step 4
        {
 //             dprint("Step 4: time: ", ftos(time), "\n");
                if(vlen(self.origin - self.spawnorigin) > 10)  // should not happen anymore
@@ -240,22 +257,22 @@ void football_touch(void)
        self.pusher = other;
        self.team = other.team;
 
-       if(autocvar_g_nexball_football_physics == -1)    // MrBougo try 1, before decompiling Rev's original
+       if(autocvar_g_nexball_football_physics == -1)   // MrBougo try 1, before decompiling Rev's original
        {
                if(vlen(other.velocity))
                        self.velocity = other.velocity * 1.5 + '0 0 1' * autocvar_g_nexball_football_boost_up;
        }
-       else if(autocvar_g_nexball_football_physics == 1)      // MrBougo's modded Rev style: partially independant of the height of the aiming point
+       else if(autocvar_g_nexball_football_physics == 1)         // MrBougo's modded Rev style: partially independant of the height of the aiming point
        {
                makevectors(other.v_angle);
                self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + '0 0 1' * autocvar_g_nexball_football_boost_up;
        }
-       else if(autocvar_g_nexball_football_physics == 2)      // 2nd mod try: totally independant. Really playable!
+       else if(autocvar_g_nexball_football_physics == 2)         // 2nd mod try: totally independant. Really playable!
        {
                makevectors(other.v_angle_y * '0 1 0');
                self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
        }
-       else     // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant)
+       else     // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant)
        {
                makevectors(other.v_angle);
                self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
@@ -270,7 +287,7 @@ void basketball_touch(void)
                football_touch();
                return;
        }
-       if(!self.cnt && other.classname == "player" && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_nexball_delay_collect))
+       if(!self.cnt && other.classname == "player" && (other != self.nb_dropper || time > self.nb_droptime + autocvar_g_nexball_delay_collect))
        {
                if(other.health <= 0)
                        return;
@@ -306,13 +323,15 @@ void GoalTouch(void)
 
        if(nb_teams == 2)
                otherteam = OtherTeam(ball.team);
+       else
+               otherteam = 0;
 
        if((isclient = ball.pusher.flags & FL_CLIENT))
                pname = ball.pusher.netname;
        else
                pname = "Someone (?)";
 
-       if(ball.team == self.team)         //owngoal (regular goals)
+       if(ball.team == self.team)               //owngoal (regular goals)
        {
                LogNB("owngoal", ball.pusher);
                bprint("Boo! ", pname, "^7 scored a goal against their own team!\n");
@@ -336,7 +355,7 @@ void GoalTouch(void)
                        bprint("The ball was returned.\n");
                pscore = 0;
        }
-       else                               //score
+       else                                                       //score
        {
                LogNB(strcat("goal:", ftos(self.team)), ball.pusher);
                bprint("Goaaaaal! ", pname, "^7 scored a point for the ", ColoredTeamName(ball.team), ".\n");
@@ -368,12 +387,12 @@ void GoalTouch(void)
        ball.cnt = 1;
        ball.think = ResetBall;
        if(ball.classname == "nexball_basketball")
-               ball.touch = football_touch; // better than SUB_Null: football control until the ball gets reset
+               ball.touch = football_touch; // better than func_null: football control until the ball gets reset
        ball.nextthink = time + autocvar_g_nexball_delay_goal * (self.team != GOAL_OUT);
 }
 
 //=======================//
-//       team ents       //
+//        team ents       //
 //=======================//
 void spawnfunc_nexball_team(void)
 {
@@ -399,7 +418,7 @@ void nb_spawnteam(string teamname, float teamcolor)
 
 void nb_spawnteams(void)
 {
-       float t_r, t_b, t_y, t_p;
+       float t_r = 0, t_b = 0, t_y = 0, t_p = 0;
        entity e;
        for(e = world; (e = find(e, classname, "nexball_goal"));)
        {
@@ -446,7 +465,7 @@ void nb_delayedinit(void)
 
 
 //=======================//
-//      spawnfuncs       //
+//       spawnfuncs       //
 //=======================//
 
 void SpawnBall(void)
@@ -507,7 +526,7 @@ void SpawnBall(void)
 
 void spawnfunc_nexball_basketball(void)
 {
-    nexball_mode |= NBM_BASKETBALL;
+       nexball_mode |= NBM_BASKETBALL;
        self.classname = "nexball_basketball";
        if not(balls & BALL_BASKET)
        {
@@ -530,7 +549,7 @@ void spawnfunc_nexball_basketball(void)
 
 void spawnfunc_nexball_football(void)
 {
-    nexball_mode |= NBM_FOOTBALL;
+       nexball_mode |= NBM_FOOTBALL;
        self.classname = "nexball_football";
        self.solid = SOLID_TRIGGER;
        balls |= BALL_FOOT;
@@ -610,11 +629,11 @@ void spawnfunc_ball_basketball(void)
 // The "red goal" is defended by blue team. A ball in there counts as a point for red.
 void spawnfunc_ball_redgoal(void)
 {
-       spawnfunc_nexball_bluegoal();    // I blame Revenant
+       spawnfunc_nexball_bluegoal();   // I blame Revenant
 }
 void spawnfunc_ball_bluegoal(void)
 {
-       spawnfunc_nexball_redgoal();    // but he didn't mean to cause trouble :p
+       spawnfunc_nexball_redgoal();    // but he didn't mean to cause trouble :p
 }
 void spawnfunc_ball_fault(void)
 {
@@ -626,14 +645,32 @@ void spawnfunc_ball_bound(void)
 }
 
 //=======================//
-//      Weapon code      //
+//       Weapon code     //
 //=======================//
 
+
+void W_Nexball_Think()
+{
+       //dprint("W_Nexball_Think\n");
+       //vector new_dir = steerlib_arrive(self.enemy.origin, 2500);
+       vector new_dir = normalize(self.enemy.origin + '0 0 50' - self.origin);
+       vector old_dir = normalize(self.velocity);       
+       float _speed = vlen(self.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
+       
+       self.velocity = new_vel;
+       
+       self.nextthink = time;
+}
+
 void W_Nexball_Touch(void)
 {
        entity ball, attacker;
        attacker = self.owner;
-
+       //self.think = func_null;
+       //self.enemy = world;
+       
        PROJECTILE_TOUCH;
        if(attacker.team != other.team || autocvar_g_nexball_basketball_teamsteal)
                if((ball = other.ballcarried) && (attacker.classname == "player"))
@@ -687,12 +724,28 @@ void W_Nexball_Attack(float t)
                        mul = 2 - mul;
                mul = mi + (ma - mi) * mul; // range from the minimal power to the maximal power
        }
+       
        DropBall(ball, w_shotorg, W_CalculateProjectileVelocity(self.velocity, w_shotdir * autocvar_g_balance_nexball_primary_speed * mul, FALSE));
+       
+
        //TODO: use the speed_up cvar too ??
 }
 
 void W_Nexball_Attack2(void)
 {
+       if(self.ballcarried.enemy)
+       {
+               entity _ball = self.ballcarried;
+               W_SetupShot(self, FALSE, 4, "nexball/shoot1.wav", CH_WEAPON_A, 0);
+               DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
+               _ball.think = W_Nexball_Think;
+               _ball.nextthink = time;
+               return;
+       }
+       
+       if(!autocvar_g_nexball_tackling)
+               return;
+       
        entity missile;
        if(!(balls & BALL_BASKET))
                return;
@@ -720,34 +773,37 @@ void W_Nexball_Attack2(void)
        missile.flags = FL_PROJECTILE;
 }
 
-var const float() nullfunc;
 float ball_customize()
 {
-    if(!self.owner)
-        self.customizeentityforclient = nullfunc;
-    
-    if(other == self.owner)
-        self.scale = 0.5;
-    else
-        self.scale = 1;
-    return TRUE;
+       if(!self.owner)
+       {
+               self.effects &~= EF_FLAME;
+               self.scale = 1;
+               self.customizeentityforclient = func_null;
+               return TRUE;
+       }               
+       
+       if(other == self.owner)
+       {
+               self.scale = autocvar_g_nexball_viewmodel_scale;
+               if(self.enemy)
+                       self.effects |= EF_FLAME;
+               else
+                       self.effects &~= EF_FLAME;
+       }       
+       else
+       {
+               self.effects &~= EF_FLAME;
+               self.scale = 1;
+       }
+               
+       return TRUE;
 }
 
 float w_nexball_weapon(float req)
 {
        if(req == WR_THINK)
        {
-           
-               
-               if(nexball_mode & NBM_BASKETBALL)
-               if(!self.BUTTON_ATCK &&  self.ballcarried)
-               {
-                   makevectors(self.v_angle);
-            self.ballcarried.velocity = self.velocity;            
-            self.ballcarried.customizeentityforclient = ball_customize;
-            setorigin(self.ballcarried, self.origin + self.view_ofs + v_forward * 23 + v_right * 4);
-               }
-
                if(self.BUTTON_ATCK)
                        if(weapon_prepareattack(0, autocvar_g_balance_nexball_primary_refire))
                                if(autocvar_g_nexball_basketball_meter)
@@ -820,17 +876,77 @@ MUTATOR_HOOKFUNCTION(nexball_BuildMutatorsPrettyString)
        return 0;
 }
 
+MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink)
+{
+       makevectors(self.v_angle);
+       if(nexball_mode & NBM_BASKETBALL)
+       {               
+               if(self.ballcarried)
+               {
+                       // 'view ball'
+                       self.ballcarried.velocity = self.velocity;                      
+                       self.ballcarried.customizeentityforclient = ball_customize;
+                       
+                       setorigin(self.ballcarried, self.origin + self.view_ofs + 
+                                         v_forward * autocvar_g_nexball_viewmodel_offset_x + 
+                                         v_right * autocvar_g_nexball_viewmodel_offset_y + 
+                                         v_up * autocvar_g_nexball_viewmodel_offset_z);        
+                                         
+                       // 'safe passing'
+                       if(autocvar_g_nexball_safepass_maxdist)
+                       {
+                               if(self.ballcarried.wait < time && self.ballcarried.enemy)
+                               {
+                                       //centerprint(self, sprintf("Lost lock on %s", self.ballcarried.enemy.netname));
+                                       self.ballcarried.enemy = world;
+                               }
+                                       
+                               
+                               //tracebox(self.origin + self.view_ofs, '-2 -2 -2', '2 2 2', self.origin + self.view_ofs + v_forward * autocvar_g_nexball_safepass_maxdist);
+                               crosshair_trace(self);
+                               if( trace_ent && 
+                                       trace_ent.flags & FL_CLIENT &&
+                                       trace_ent.deadflag == DEAD_NO &&
+                                       trace_ent.team == self.team &&
+                                       vlen(trace_ent.origin - self.origin) <= autocvar_g_nexball_safepass_maxdist )
+                               {
+                                       
+                                       //if(self.ballcarried.enemy != trace_ent)
+                                       //      centerprint(self, sprintf("Locked to %s", trace_ent.netname));
+                                       self.ballcarried.enemy = trace_ent;
+                                       self.ballcarried.wait = time + autocvar_g_nexball_safepass_holdtime;
+                                       
+                                       
+                               }
+                       }
+               }
+               else
+               {                       
+                       if(!WEPSET_EMPTY_E(self.weaponentity))
+                       {
+                               WEPSET_COPY_EE(self, self.weaponentity);
+                               weapon_action(WEP_PORTO, WR_RESETPLAYER);
+                               self.switchweapon = self.weaponentity.switchweapon;
+                               W_SwitchWeapon(self.switchweapon);
+                               
+               WEPSET_CLEAR_E(self.weaponentity);
+                       }
+               }
+               
+       }
+       return FALSE;
+}
 
 MUTATOR_HOOKFUNCTION(nexball_PlayerSpawn)
-{    
-    if(nexball_mode & NBM_BASKETBALL)
-        return FALSE;
-    
-    if(autocvar_g_weapon_stay)
-        return FALSE;
+{      
+       WEPSET_CLEAR_E(self.weaponentity);
+       
+       if(nexball_mode & NBM_BASKETBALL)
+               WEPSET_OR_EW(self, WEP_PORTO);
+       else
+               WEPSET_CLEAR_E(self);
 
-    self.weapons = 0;
-    return FALSE;
+       return FALSE;
 }
 
 MUTATOR_DEFINITION(gamemode_nexball)
@@ -841,10 +957,10 @@ MUTATOR_DEFINITION(gamemode_nexball)
        MUTATOR_HOOK(BuildMutatorsPrettyString, nexball_BuildMutatorsPrettyString, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsString, nexball_BuildMutatorsString, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, nexball_PlayerSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPreThink, nexball_PlayerPreThink, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {
-           g_nexball = 1;
                g_nexball_meter_period = autocvar_g_nexball_meter_period;
                if(g_nexball_meter_period <= 0)
                        g_nexball_meter_period = 2; // avoid division by zero etc. due to silly users
@@ -854,14 +970,27 @@ MUTATOR_DEFINITION(gamemode_nexball)
                // General settings
                /*
                CVTOV(g_nexball_football_boost_forward);   //100
-               CVTOV(g_nexball_football_boost_up);        //200
-               CVTOV(g_nexball_delay_idle);               //10
-               CVTOV(g_nexball_football_physics);         //0
+               CVTOV(g_nexball_football_boost_up);             //200
+               CVTOV(g_nexball_delay_idle);                       //10
+               CVTOV(g_nexball_football_physics);               //0
                */
                radar_showennemies = autocvar_g_nexball_radar_showallplayers;
 
                InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE);
        }
 
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back nb_delayedinit 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;
 }