]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/w_minelayer.qc
Merge remote branch 'refs/remotes/origin/fruitiex/racefixes'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_minelayer.qc
index 20442e86fe03208748a381e0b5e25c42acf3d3d8..dc4f48152e4ebb6d6dc07df6a0af8d49695cb77c 100644 (file)
@@ -1,32 +1,57 @@
-/*TODO list (things left to do before this weapon should be ready, delete once it's all done):
-- The weapon currently uses sounds and models from other weapons. We need a modeler and sound artist to make this weapon its own (the gun model should probably be something between the porto and rocket launcher design-wise).
-- Mines remain stuck in the air if they hit a moving entity (like an elevator or players). They should normally stick to them... perhaps set them as an attachment?
-- Bot code for the weapon may be needed. The bot AI may not have any info about this gun yet.
-- The mine model needs to face properly when it sticks to a surface. Once we'll have a correct mine model, we can't afford the model facing any way it falls to the ground. Should probably look at the porto code to see how portals face in the right direction when sticking to walls. 
-*/
-
 #ifdef REGISTER_WEAPON
 REGISTER_WEAPON(MINE_LAYER, w_minelayer, IT_ROCKETS, 4, WEP_FLAG_NORMAL | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_HIGH, "minelayer", "minelayer", "Mine Layer");
 #else
 #ifdef SVQC
-.float minelayer_detonate;
-.float mine_number, mine_time;
+void W_Mine_Think (void);
+.float minelayer_detonate, minelayer_mines;
+.float mine_time;
 
 void spawnfunc_weapon_minelayer (void)
 {
        weapon_defaultspawnfunc(WEP_MINE_LAYER);
 }
 
-void W_Mine_Unregister()
+void W_Mine_Stick ()
 {
-       if(self.owner && self.owner.lastmine == self)
-               self.owner.lastmine = world;
+       spamsound (self, CHAN_PROJECTILE, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM);
+
+       // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile
+
+       entity newmine;
+       newmine = spawn();
+       newmine.classname = self.classname;
+
+       newmine.bot_dodge = self.bot_dodge;
+       newmine.bot_dodgerating = self.bot_dodgerating;
+
+       newmine.owner = self.owner;
+       setsize(newmine, '-4 -4 -4', '4 4 4');
+       setorigin(newmine, self.origin);
+       setmodel(newmine, "models/mine.md3");
+       newmine.angles = vectoangles(-trace_plane_normal); // face against the surface
+
+       newmine.takedamage = self.takedamage;
+       newmine.damageforcescale = self.damageforcescale;
+       newmine.health = self.health;
+       newmine.event_damage = self.event_damage;
+
+       newmine.movetype = MOVETYPE_NONE; // lock the mine in place
+       newmine.projectiledeathtype = self.projectiledeathtype;
+
+       newmine.mine_time = self.mine_time;
+
+       newmine.touch = SUB_Null;
+       newmine.think = W_Mine_Think;
+       newmine.nextthink = time;
+       newmine.cnt = self.cnt;
+       newmine.flags = self.flags;
+
+       remove(self);
+       self = newmine;
 }
 
 void W_Mine_Explode ()
 {
-       W_Mine_Unregister();
-
        if(other.takedamage == DAMAGE_AIM)
                if(other.classname == "player")
                        if(IsDifferentTeam(self.owner, other))
@@ -52,8 +77,6 @@ void W_Mine_Explode ()
 
 void W_Mine_DoRemoteExplode ()
 {
-       W_Mine_Unregister();
-
        self.event_damage = SUB_Null;
        self.takedamage = DAMAGE_NO;
 
@@ -71,11 +94,9 @@ void W_Mine_DoRemoteExplode ()
        remove (self);
 }
 
-void W_Mine_RemoteExplode()
+void W_Mine_RemoteExplode ()
 {
        if(self.owner.deadflag == DEAD_NO)
-       if(self.owner.lastmine)
-       {
                if((self.spawnshieldtime >= 0)
                        ? (time >= self.spawnshieldtime) // timer
                        : (vlen(NearestPointOnBox(self.owner, self.origin) - self.origin) > cvar("g_balance_minelayer_radius")) // safety device
@@ -83,10 +104,9 @@ void W_Mine_RemoteExplode()
                {
                        W_Mine_DoRemoteExplode();
                }
-       }
 }
 
-void W_Mine_ProximityExplode()
+void W_Mine_ProximityExplode ()
 {
        // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance
        if(cvar("g_balance_minelayer_protection"))
@@ -114,12 +134,22 @@ void W_Mine_Think (void)
        {
                other = world;
                self.projectiledeathtype |= HITTYPE_BOUNCE;
-               W_Mine_Explode ();
+               W_Mine_Explode();
+               return;
+       }
+
+       // a player's mines shall explode if he disconnects or dies
+       // TODO: Do this on team change too
+       if(self.owner.classname != "player" || self.owner.deadflag != DEAD_NO)
+       {
+               other = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               W_Mine_Explode();
                return;
        }
 
-       // set the mine for detonation when a foe gets too close
-       head = findradius(self.origin, cvar("g_balance_minelayer_detectionradius"));
+       // set the mine for detonation when a foe gets close enough
+       head = findradius(self.origin, cvar("g_balance_minelayer_proximityradius"));
        while(head)
        {
                if(head.classname == "player" && head.deadflag == DEAD_NO)
@@ -149,9 +179,10 @@ void W_Mine_Think (void)
 void W_Mine_Touch (void)
 {
        PROJECTILE_TOUCH;
-       spamsound (self, CHAN_PROJECTILE, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM);
-       self.movetype = MOVETYPE_NONE; // lock the mine in place
-       // TODO: make sure this doesn't cause the mine to get stuck in the air if it falls over a moving entity
+       if(!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))
+               W_Mine_Stick();
+       else if(self.movetype != MOVETYPE_NONE) // don't unstick a locked mine when someone touches it
+               self.velocity = '0 0 0';
 }
 
 void W_Mine_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
@@ -166,18 +197,17 @@ void W_Mine_Damage (entity inflictor, entity attacker, float damage, float death
 
 void W_Mine_Attack (void)
 {
-       local entity mine;
-       local entity flash;
+       entity mine;
+       entity flash;
 
        // scan how many mines we placed, and return if we reached our limit
        if(cvar("g_balance_minelayer_limit"))
        {
-               entity mine;
-               self.mine_number = 0;
+               self.minelayer_mines = 0;
                for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
-                       self.mine_number += 1;
+                       self.minelayer_mines += 1;
 
-               if(self.mine_number >= cvar("g_balance_minelayer_limit"))
+               if(self.minelayer_mines >= cvar("g_balance_minelayer_limit"))
                {
                        // the refire delay keeps this message from being spammed
                        sprint(self, strcat("You cannot place more than ^2", cvar_string("g_balance_minelayer_limit"), " ^7mines at a time\n") );
@@ -194,7 +224,6 @@ void W_Mine_Attack (void)
 
        mine = WarpZone_RefSys_SpawnSameRefSys(self);
        mine.owner = self;
-       self.lastmine = mine;
        if(cvar("g_balance_minelayer_detonatedelay") >= 0)
                mine.spawnshieldtime = time + cvar("g_balance_minelayer_detonatedelay");
        else
@@ -214,7 +243,7 @@ void W_Mine_Attack (void)
        setsize (mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot
 
        setorigin (mine, w_shotorg - v_forward * 4); // move it back so it hits the wall at the right point
-       W_SetupProjectileVelocity(mine, cvar("g_balance_minelayer_speedstart"), 0);
+       W_SetupProjectileVelocity(mine, cvar("g_balance_minelayer_speed"), 0);
        mine.angles = vectoangles (mine.velocity);
 
        mine.touch = W_Mine_Touch;
@@ -233,6 +262,8 @@ void W_Mine_Attack (void)
        W_AttachToShotorg(flash, '5 0 0');
 
        // common properties
+
+       other = mine; MUTATOR_CALLHOOK(EditProjectile);
 }
 
 void spawnfunc_weapon_minelayer (void); // defined in t_items.qc
@@ -248,9 +279,9 @@ float w_minelayer(float req)
                if(skill >= 2) // skill 0 and 1 bots won't detonate mines!
                {
                        // decide whether to detonate mines
-                       local entity mine, targetlist, targ;
-                       local float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
-                       local float selfdamage, teamdamage, enemydamage;
+                       entity targetlist, targ;
+                       float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+                       float selfdamage, teamdamage, enemydamage;
                        edgedamage = cvar("g_balance_minelayer_edgedamage");
                        coredamage = cvar("g_balance_minelayer_damage");
                        edgeradius = cvar("g_balance_minelayer_radius");
@@ -283,7 +314,7 @@ float w_minelayer(float req)
                                }
                                mine = find(mine, classname, "mine");
                        }
-                       local float desirabledamage;
+                       float desirabledamage;
                        desirabledamage = enemydamage;
                        if (teamplay != 1 && time > self.invincible_finished && time > self.spawnshieldtime)
                                desirabledamage = desirabledamage - selfdamage * cvar("g_balance_selfdamagepercent");
@@ -312,7 +343,7 @@ float w_minelayer(float req)
                                                targ = targ.chain;
                                        }
                                }else{
-                                       local float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
+                                       float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
                                        //As the distance gets larger, a correct detonation gets near imposible
                                        //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player
                                        if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1)
@@ -365,6 +396,7 @@ float w_minelayer(float req)
        else if (req == WR_PRECACHE)
        {
                precache_model ("models/flash.md3");
+               precache_model ("models/mine.md3");
                precache_model ("models/weapons/g_minelayer.md3");
                precache_model ("models/weapons/v_minelayer.md3");
                precache_model ("models/weapons/h_minelayer.iqm");