]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_nexball.qc
Revert "Merge branch 'TimePath/bot_api' into 'master'\r"
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_nexball.qc
index 4c081ab445f0c3abab6bc944c12c3a73edafc54e..f65e058433ec146ec9799b5d77d672c1bb68c49b 100644 (file)
@@ -3,7 +3,20 @@
 
 #include "gamemode.qh"
 
-#include "../t_jumppads.qh"
+float autocvar_g_nexball_basketball_bouncefactor;
+float autocvar_g_nexball_basketball_bouncestop;
+float autocvar_g_nexball_basketball_carrier_highspeed;
+bool autocvar_g_nexball_basketball_meter;
+float autocvar_g_nexball_basketball_meter_maxpower;
+float autocvar_g_nexball_basketball_meter_minpower;
+float autocvar_g_nexball_delay_collect;
+float autocvar_g_nexball_delay_goal;
+float autocvar_g_nexball_delay_start;
+float autocvar_g_nexball_football_bouncefactor;
+float autocvar_g_nexball_football_bouncestop;
+bool autocvar_g_nexball_radar_showallplayers;
+bool autocvar_g_nexball_sound_bounce;
+int autocvar_g_nexball_trail_color;
 
 float autocvar_g_nexball_safepass_turnrate;
 float autocvar_g_nexball_safepass_maxdist;
@@ -53,26 +66,24 @@ void LogNB(string mode, entity actor)
 }
 
 void ball_restart(void)
-{
+{SELFPARAM();
        if(self.owner)
                DropBall(self, self.owner.origin, '0 0 0');
        ResetBall();
 }
 
 void nexball_setstatus(void)
-{
-       entity oldself;
+{SELFPARAM();
        self.items &= ~IT_KEY1;
        if(self.ballcarried)
        {
                if(self.ballcarried.teamtime && (self.ballcarried.teamtime < time))
                {
                        bprint("The ", Team_ColoredFullName(self.team), " held the ball for too long.\n");
-                       oldself = self;
-                       self = self.ballcarried;
+                       setself(self.ballcarried);
                        DropBall(self, self.owner.origin, '0 0 0');
                        ResetBall();
-                       self = oldself;
+                       setself(this);
                }
                else
                        self.items |= IT_KEY1;
@@ -80,7 +91,7 @@ void nexball_setstatus(void)
 }
 
 void relocate_nexball(void)
-{
+{SELFPARAM();
        tracebox(self.origin, BALL_MINS, BALL_MAXS, self.origin, true, self);
        if(trace_startsolid)
        {
@@ -88,16 +99,16 @@ void relocate_nexball(void)
                o = self.origin;
                if(!move_out_of_solid(self))
                        objerror("could not get out of solid at all!");
-               print("^1NOTE: this map needs FIXING. ", self.classname, " at ", vtos(o - '0 0 1'));
-               print(" needs to be moved out of solid, e.g. by '", ftos(self.origin.x - o.x));
-               print(" ", ftos(self.origin.y - o.y));
-               print(" ", ftos(self.origin.z - o.z), "'\n");
+               LOG_INFO("^1NOTE: this map needs FIXING. ", self.classname, " at ", vtos(o - '0 0 1'));
+               LOG_INFO(" needs to be moved out of solid, e.g. by '", ftos(self.origin.x - o.x));
+               LOG_INFO(" ", ftos(self.origin.y - o.y));
+               LOG_INFO(" ", ftos(self.origin.z - o.z), "'\n");
                self.origin = o;
        }
 }
 
 void DropOwner(void)
-{
+{SELFPARAM();
        entity ownr;
        ownr = self.owner;
        DropBall(self, ownr.origin, ownr.velocity);
@@ -107,7 +118,7 @@ void DropOwner(void)
 }
 
 void GiveBall(entity plyr, entity ball)
-{
+{SELFPARAM();
        entity ownr;
 
        if((ownr = ball.owner))
@@ -146,7 +157,7 @@ void GiveBall(entity plyr, entity ball)
        ball.effects |= EF_NOSHADOW;
        ball.scale = 1; // scale down.
 
-       WaypointSprite_AttachCarrier("nb-ball", plyr, RADARICON_FLAGCARRIER, BALL_SPRITECOLOR);
+       WaypointSprite_AttachCarrier(WP_NbBall, plyr, RADARICON_FLAGCARRIER);
        WaypointSprite_UpdateRule(plyr.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
 
        if(autocvar_g_nexball_basketball_delay_hold)
@@ -155,15 +166,15 @@ void GiveBall(entity plyr, entity ball)
                ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold;
        }
 
-       ownr = self;
-       self = plyr;
-       self.weaponentity.weapons = self.weapons;
-       self.weaponentity.switchweapon = self.weapon;
-       self.weapons = WEPSET_PORTO;
-       WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
-       self.switchweapon = WEP_PORTO;
-       W_SwitchWeapon(WEP_PORTO);
-       self = ownr;
+       plyr.weaponentity.weapons = plyr.weapons;
+       plyr.weaponentity.switchweapon = plyr.weapon;
+       plyr.weapons = WEPSET(NEXBALL);
+       setself(plyr);
+       Weapon w = WEP_NEXBALL;
+       w.wr_resetplayer(w);
+       plyr.switchweapon = WEP_NEXBALL.m_id;
+       W_SwitchWeapon(WEP_NEXBALL.m_id);
+       setself(this);
 }
 
 void DropBall(entity ball, vector org, vector vel)
@@ -190,7 +201,7 @@ void DropBall(entity ball, vector org, vector vel)
        }
 
        WaypointSprite_Kill(ball.owner.waypointsprite_attachedforcarrier);
-       WaypointSprite_Spawn("nb-ball", 0, 0, ball, '0 0 64', world, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER, BALL_SPRITECOLOR); // no health bar please
+       WaypointSprite_Spawn(WP_NbBall, 0, 0, ball, '0 0 64', world, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); // no health bar please
        WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
 
        ball.owner.ballcarried = world;
@@ -198,7 +209,7 @@ void DropBall(entity ball, vector org, vector vel)
 }
 
 void InitBall(void)
-{
+{SELFPARAM();
        if(gameover) return;
        self.flags &= ~FL_ONGROUND;
        self.movetype = MOVETYPE_BOUNCE;
@@ -212,13 +223,13 @@ void InitBall(void)
        self.teamtime = 0;
        self.pusher = world;
        self.team = false;
-       sound(self, CH_TRIGGER, self.noise1, VOL_BASE, ATTEN_NORM);
+       _sound(self, CH_TRIGGER, self.noise1, VOL_BASE, ATTEN_NORM);
        WaypointSprite_Ping(self.waypointsprite_attachedforcarrier);
        LogNB("init", world);
 }
 
 void ResetBall(void)
-{
+{SELFPARAM();
        if(self.cnt < 2)        // step 1
        {
                if(time == self.teamtime)
@@ -243,7 +254,7 @@ void ResetBall(void)
        {
 //             dprint("Step 4: time: ", ftos(time), "\n");
                if(vlen(self.origin - self.spawnorigin) > 10)  // should not happen anymore
-                       dprint("The ball moved too far away from its spawn origin.\nOffset: ",
+                       LOG_TRACE("The ball moved too far away from its spawn origin.\nOffset: ",
                                   vtos(self.origin - self.spawnorigin), " Velocity: ", vtos(self.velocity), "\n");
                self.velocity = '0 0 0';
                setorigin(self, self.spawnorigin); // make sure it's positioned correctly anyway
@@ -254,12 +265,12 @@ void ResetBall(void)
 }
 
 void football_touch(void)
-{
+{SELFPARAM();
        if(other.solid == SOLID_BSP)
        {
                if(time > self.lastground + 0.1)
                {
-                       sound(self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+                       _sound(self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
                        self.lastground = time;
                }
                if(vlen(self.velocity) && !self.cnt)
@@ -300,7 +311,7 @@ void football_touch(void)
 }
 
 void basketball_touch(void)
-{
+{SELFPARAM();
        if(other.ballcarried)
        {
                football_touch();
@@ -315,14 +326,14 @@ void basketball_touch(void)
        }
        else if(other.solid == SOLID_BSP)
        {
-               sound(self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+               _sound(self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
                if(vlen(self.velocity) && !self.cnt)
                        self.nextthink = min(time + autocvar_g_nexball_delay_idle, self.teamtime);
        }
 }
 
 void GoalTouch(void)
-{
+{SELFPARAM();
        entity ball;
        float isclient, pscore, otherteam;
        string pname;
@@ -381,7 +392,7 @@ void GoalTouch(void)
                pscore = 1;
        }
 
-       sound(ball, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NONE);
+       _sound(ball, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NONE);
 
        if(ball.team && pscore)
        {
@@ -413,7 +424,7 @@ void GoalTouch(void)
 //=======================//
 //        team ents       //
 //=======================//
-void spawnfunc_nexball_team(void)
+spawnfunc(nexball_team)
 {
        if(!g_nexball)
        {
@@ -425,7 +436,7 @@ void spawnfunc_nexball_team(void)
 
 void nb_spawnteam(string teamname, float teamcolor)
 {
-       dprint("^2spawned team ", teamname, "\n");
+       LOG_TRACE("^2spawned team ", teamname, "\n");
        entity e;
        e = spawn();
        e.classname = "nexball_team";
@@ -488,7 +499,7 @@ void nb_delayedinit(void)
 //=======================//
 
 void SpawnBall(void)
-{
+{SELFPARAM();
        if(!g_nexball) { remove(self); return; }
 
 //     balls += 4; // using the remaining bits to count balls will leave more than the max edict count, so it's fine
@@ -500,7 +511,7 @@ void SpawnBall(void)
        }
 
        precache_model(self.model);
-       setmodel(self, self.model);
+       _setmodel(self, self.model);
        setsize(self, BALL_MINS, BALL_MAXS);
        ball_scale = self.scale;
 
@@ -520,26 +531,26 @@ void SpawnBall(void)
        if(!autocvar_g_nexball_sound_bounce)
                self.noise = "";
        else if(self.noise == "")
-               self.noise = "sound/nexball/bounce.wav";
+               self.noise = SND(NB_BOUNCE);
        //bounce sound placeholder (FIXME)
        if(self.noise1 == "")
-               self.noise1 = "sound/nexball/drop.wav";
+               self.noise1 = SND(NB_DROP);
        //ball drop sound placeholder (FIXME)
        if(self.noise2 == "")
-               self.noise2 = "sound/nexball/steal.wav";
+               self.noise2 = SND(NB_STEAL);
        //stealing sound placeholder (FIXME)
        if(self.noise) precache_sound(self.noise);
        precache_sound(self.noise1);
        precache_sound(self.noise2);
 
-       WaypointSprite_AttachCarrier("nb-ball", self, RADARICON_FLAGCARRIER, BALL_SPRITECOLOR); // the ball's team is not set yet, no rule update needed
+       WaypointSprite_AttachCarrier(WP_NbBall, self, RADARICON_FLAGCARRIER); // the ball's team is not set yet, no rule update needed
 
        self.reset = ball_restart;
        self.think = InitBall;
        self.nextthink = game_starttime + autocvar_g_nexball_delay_start;
 }
 
-void spawnfunc_nexball_basketball(void)
+spawnfunc(nexball_basketball)
 {
        nexball_mode |= NBM_BASKETBALL;
        self.classname = "nexball_basketball";
@@ -562,7 +573,7 @@ void spawnfunc_nexball_basketball(void)
        SpawnBall();
 }
 
-void spawnfunc_nexball_football(void)
+spawnfunc(nexball_football)
 {
        nexball_mode |= NBM_FOOTBALL;
        self.classname = "nexball_football";
@@ -574,7 +585,7 @@ void spawnfunc_nexball_football(void)
 }
 
 float nb_Goal_Customize()
-{
+{SELFPARAM();
        entity e, wp_owner;
        e = WaypointSprite_getviewentity(other);
        wp_owner = self.owner;
@@ -584,14 +595,15 @@ float nb_Goal_Customize()
 }
 
 void SpawnGoal(void)
-{
+{SELFPARAM();
        if(!g_nexball) { remove(self); return; }
 
        EXACTTRIGGER_INIT;
 
        if(self.team != GOAL_OUT && Team_TeamToNumber(self.team) != -1)
        {
-               WaypointSprite_SpawnFixed("goal", (self.absmin + self.absmax) * 0.5, self, sprite, RADARICON_NONE, ((self.team) ? Team_ColorRGB(self.team) : '1 0.5 0'));
+               entity wp = WaypointSprite_SpawnFixed(WP_NbGoal, (self.absmin + self.absmax) * 0.5, self, sprite, RADARICON_NONE);
+               wp.colormod = ((self.team) ? Team_ColorRGB(self.team) : '1 0.5 0');
                self.sprite.customizeentityforclient = nb_Goal_Customize;
        }
 
@@ -602,40 +614,40 @@ void SpawnGoal(void)
        self.touch = GoalTouch;
 }
 
-void spawnfunc_nexball_redgoal(void)
+spawnfunc(nexball_redgoal)
 {
        self.team = NUM_TEAM_1;
        SpawnGoal();
 }
-void spawnfunc_nexball_bluegoal(void)
+spawnfunc(nexball_bluegoal)
 {
        self.team = NUM_TEAM_2;
        SpawnGoal();
 }
-void spawnfunc_nexball_yellowgoal(void)
+spawnfunc(nexball_yellowgoal)
 {
        self.team = NUM_TEAM_3;
        SpawnGoal();
 }
-void spawnfunc_nexball_pinkgoal(void)
+spawnfunc(nexball_pinkgoal)
 {
        self.team = NUM_TEAM_4;
        SpawnGoal();
 }
 
-void spawnfunc_nexball_fault(void)
+spawnfunc(nexball_fault)
 {
        self.team = GOAL_FAULT;
        if(self.noise == "")
-               self.noise = "misc/typehit.wav";
+               self.noise = SND(TYPEHIT);
        SpawnGoal();
 }
 
-void spawnfunc_nexball_out(void)
+spawnfunc(nexball_out)
 {
        self.team = GOAL_OUT;
        if(self.noise == "")
-               self.noise = "misc/typehit.wav";
+               self.noise = SND(TYPEHIT);
        SpawnGoal();
 }
 
@@ -643,34 +655,34 @@ void spawnfunc_nexball_out(void)
 //Spawnfuncs preserved for compatibility
 //
 
-void spawnfunc_ball(void)
+spawnfunc(ball)
 {
-       spawnfunc_nexball_football();
+       spawnfunc_nexball_football(this);
 }
-void spawnfunc_ball_football(void)
+spawnfunc(ball_football)
 {
-       spawnfunc_nexball_football();
+       spawnfunc_nexball_football(this);
 }
-void spawnfunc_ball_basketball(void)
+spawnfunc(ball_basketball)
 {
-       spawnfunc_nexball_basketball();
+       spawnfunc_nexball_basketball(this);
 }
 // The "red goal" is defended by blue team. A ball in there counts as a point for red.
-void spawnfunc_ball_redgoal(void)
+spawnfunc(ball_redgoal)
 {
-       spawnfunc_nexball_bluegoal();   // I blame Revenant
+       spawnfunc_nexball_bluegoal(this);       // I blame Revenant
 }
-void spawnfunc_ball_bluegoal(void)
+spawnfunc(ball_bluegoal)
 {
-       spawnfunc_nexball_redgoal();    // but he didn't mean to cause trouble :p
+       spawnfunc_nexball_redgoal(this);        // but he didn't mean to cause trouble :p
 }
-void spawnfunc_ball_fault(void)
+spawnfunc(ball_fault)
 {
-       spawnfunc_nexball_fault();
+       spawnfunc_nexball_fault(this);
 }
-void spawnfunc_ball_bound(void)
+spawnfunc(ball_bound)
 {
-       spawnfunc_nexball_out();
+       spawnfunc_nexball_out(this);
 }
 
 //=======================//
@@ -679,7 +691,7 @@ void spawnfunc_ball_bound(void)
 
 
 void W_Nexball_Think()
-{
+{SELFPARAM();
        //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);
@@ -694,7 +706,7 @@ void W_Nexball_Think()
 }
 
 void W_Nexball_Touch(void)
-{
+{SELFPARAM();
        entity ball, attacker;
        attacker = self.owner;
        //self.think = func_null;
@@ -709,7 +721,7 @@ void W_Nexball_Touch(void)
                        if(!attacker.ballcarried)
                        {
                                LogNB("stole", attacker);
-                               sound(other, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM);
+                               _sound(other, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM);
 
                                if(SAME_TEAM(attacker, other) && time > attacker.teamkill_complain)
                                {
@@ -725,13 +737,13 @@ void W_Nexball_Touch(void)
 }
 
 void W_Nexball_Attack(float t)
-{
+{SELFPARAM();
        entity ball;
        float mul, mi, ma;
        if(!(ball = self.ballcarried))
                return;
 
-       W_SetupShot(self, false, 4, "nexball/shoot1.wav", CH_WEAPON_A, 0);
+       W_SetupShot(self, false, 4, SND(NB_SHOOT1), CH_WEAPON_A, 0);
        tracebox(w_shotorg, BALL_MINS, BALL_MAXS, w_shotorg, MOVE_WORLDONLY, world);
        if(trace_startsolid)
        {
@@ -761,11 +773,11 @@ void W_Nexball_Attack(float t)
 }
 
 void W_Nexball_Attack2(void)
-{
+{SELFPARAM();
        if(self.ballcarried.enemy)
        {
                entity _ball = self.ballcarried;
-               W_SetupShot(self, false, 4, "nexball/shoot1.wav", CH_WEAPON_A, 0);
+               W_SetupShot(self, false, 4, SND(NB_SHOOT1), CH_WEAPON_A, 0);
                DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
                _ball.think = W_Nexball_Think;
                _ball.nextthink = time;
@@ -775,12 +787,8 @@ void W_Nexball_Attack2(void)
        if(!autocvar_g_nexball_tackling)
                return;
 
-       entity missile;
-       if(!(balls & BALL_BASKET))
-               return;
-       W_SetupShot(self, false, 2, "nexball/shoot2.wav", CH_WEAPON_A, 0);
-//     pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-       missile = spawn();
+       W_SetupShot(self, false, 2, SND(NB_SHOOT2), CH_WEAPON_A, 0);
+       entity missile = spawn();
 
        missile.owner = self;
        missile.classname = "ballstealer";
@@ -805,7 +813,7 @@ void W_Nexball_Attack2(void)
 }
 
 float ball_customize()
-{
+{SELFPARAM();
        if(!self.owner)
        {
                self.effects &= ~EF_FLAME;
@@ -831,58 +839,51 @@ float ball_customize()
        return true;
 }
 
-float w_nexball_weapon(float req)
-{
-       if(req == WR_THINK)
+       METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, bool fire1, bool fire2))
        {
-               if(self.BUTTON_ATCK)
-                       if(weapon_prepareattack(0, autocvar_g_balance_nexball_primary_refire))
+               if(fire1)
+                       if(weapon_prepareattack(thiswep, actor, false, autocvar_g_balance_nexball_primary_refire))
                                if(autocvar_g_nexball_basketball_meter)
                                {
                                        if(self.ballcarried && !self.metertime)
                                                self.metertime = time;
                                        else
-                                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
+                                               weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                                }
                                else
                                {
                                        W_Nexball_Attack(-1);
-                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
+                                       weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                                }
-               if(self.BUTTON_ATCK2)
-                       if(weapon_prepareattack(1, autocvar_g_balance_nexball_secondary_refire))
+               if(fire2)
+                       if(weapon_prepareattack(thiswep, actor, true, autocvar_g_balance_nexball_secondary_refire))
                        {
                                W_Nexball_Attack2();
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
+                               weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
                        }
 
-               if(!self.BUTTON_ATCK && self.metertime && self.ballcarried)
+               if(!fire1 && self.metertime && self.ballcarried)
                {
                        W_Nexball_Attack(time - self.metertime);
                        // DropBall or stealing will set metertime back to 0
-                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
+                       weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                }
        }
-       else if(req == WR_INIT)
+       METHOD(BallStealer, wr_setup, void(BallStealer thiswep))
        {
-               precache_model("models/weapons/g_porto.md3");
-               precache_model("models/weapons/v_porto.md3");
-               precache_model("models/weapons/h_porto.iqm");
-               precache_model("models/elaser.mdl");
-               precache_sound("nexball/shoot1.wav");
-               precache_sound("nexball/shoot2.wav");
-               precache_sound("misc/typehit.wav");
+               //weapon_setup(WEP_PORTO.m_id);
        }
-       else if(req == WR_SETUP)
+       METHOD(BallStealer, wr_checkammo1, bool(BallStealer thiswep))
        {
-               //weapon_setup(WEP_PORTO);
+               return true;
+       }
+       METHOD(BallStealer, wr_checkammo2, bool(BallStealer thiswep))
+       {
+               return true;
        }
-       // No need to check WR_CHECKAMMO* or WR_AIM, it should always return true
-       return true;
-}
 
 MUTATOR_HOOKFUNCTION(nexball_BallDrop)
-{
+{SELFPARAM();
        if(self.ballcarried && g_nexball)
                DropBall(self.ballcarried, self.origin, self.velocity);
 
@@ -890,7 +891,7 @@ MUTATOR_HOOKFUNCTION(nexball_BallDrop)
 }
 
 MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink)
-{
+{SELFPARAM();
        makevectors(self.v_angle);
        if(nexball_mode & NBM_BASKETBALL)
        {
@@ -938,7 +939,8 @@ MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink)
                        if(self.weaponentity.weapons)
                        {
                                self.weapons = self.weaponentity.weapons;
-                               WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
+                               Weapon w = WEP_NEXBALL;
+                               w.wr_resetplayer(w);
                                self.switchweapon = self.weaponentity.switchweapon;
                                W_SwitchWeapon(self.switchweapon);
 
@@ -954,11 +956,11 @@ MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink)
 }
 
 MUTATOR_HOOKFUNCTION(nexball_PlayerSpawn)
-{
+{SELFPARAM();
        self.weaponentity.weapons = '0 0 0';
 
        if(nexball_mode & NBM_BASKETBALL)
-               self.weapons |= WEPSET_PORTO;
+               self.weapons |= WEPSET(NEXBALL);
        else
                self.weapons = '0 0 0';
 
@@ -966,7 +968,7 @@ MUTATOR_HOOKFUNCTION(nexball_PlayerSpawn)
 }
 
 MUTATOR_HOOKFUNCTION(nexball_PlayerPhysics)
-{
+{SELFPARAM();
        if(self.ballcarried)
        {
                self.stat_sv_airspeedlimit_nonqw *= autocvar_g_nexball_basketball_carrier_highspeed;
@@ -975,25 +977,18 @@ MUTATOR_HOOKFUNCTION(nexball_PlayerPhysics)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(nexball_SetStartItems)
-{
-       start_items |= IT_UNLIMITED_SUPERWEAPONS; // FIXME BAD BAD BAD BAD HACK, NEXBALL SHOULDN'T ABUSE PORTO'S WEAPON SLOT
-
-       return false;
-}
-
 MUTATOR_HOOKFUNCTION(nexball_ForbidThrowing)
-{
-       if(self.weapon == WEP_MORTAR)
+{SELFPARAM();
+       if(self.weapon == WEP_NEXBALL.m_id)
                return true;
 
        return false;
 }
 
 MUTATOR_HOOKFUNCTION(nexball_FilterItem)
-{
+{SELFPARAM();
        if(self.classname == "droppedweapon")
-       if(self.weapon == WEP_MORTAR)
+       if(self.weapon == WEP_NEXBALL.m_id)
                return true;
 
        return false;
@@ -1007,7 +1002,6 @@ MUTATOR_DEFINITION(gamemode_nexball)
        MUTATOR_HOOK(PlayerSpawn, nexball_PlayerSpawn, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPreThink, nexball_PlayerPreThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPhysics, nexball_PlayerPhysics, CBC_ORDER_ANY);
-       MUTATOR_HOOK(SetStartItems, nexball_SetStartItems, CBC_ORDER_ANY);
        MUTATOR_HOOK(ForbidThrowCurrentWeapon, nexball_ForbidThrowing, CBC_ORDER_ANY);
        MUTATOR_HOOK(FilterItem, nexball_FilterItem, CBC_ORDER_ANY);
 
@@ -1029,10 +1023,12 @@ MUTATOR_DEFINITION(gamemode_nexball)
                radar_showennemies = autocvar_g_nexball_radar_showallplayers;
 
                InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE);
+               WEP_NEXBALL.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
        }
 
        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.
@@ -1040,7 +1036,7 @@ MUTATOR_DEFINITION(gamemode_nexball)
 
        MUTATOR_ONREMOVE
        {
-               print("This is a game type and it cannot be removed at runtime.");
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
                return -1;
        }