-/*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).
-- Add protection so a mine doesn't explode if it would harm the player or a team mate, in case both an enemy and a friend are its range. This already exists for alt-fire detonation, but not for proximity detonation. Should probably be a cvared but default option.
-- 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
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
+
+ local 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_number = self.mine_number;
+ newmine.mine_time = self.mine_time;
+
+ newmine.touch = SUB_Null;
+ newmine.think = self.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))
void W_Mine_DoRemoteExplode ()
{
- W_Mine_Unregister();
-
self.event_damage = SUB_Null;
self.takedamage = DAMAGE_NO;
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
{
W_Mine_DoRemoteExplode();
}
+}
+
+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"))
+ {
+ entity head;
+ head = findradius(self.origin, cvar("g_balance_minelayer_radius"));
+ while(head)
+ {
+ if(head == self.owner || !IsDifferentTeam(head, self.owner))
+ return;
+ head = head.chain;
+ }
}
+
+ self.mine_time = 0;
+ W_Mine_Explode();
}
void W_Mine_Think (void)
{
+ entity head;
+
self.nextthink = time;
if (time > self.cnt)
{
return;
}
- // remote detonation
- if (self.owner.weapon == WEP_MINE_LAYER)
- if (self.owner.deadflag == DEAD_NO)
- if (self.minelayer_detonate)
- W_Mine_RemoteExplode();
-
- entity head;
-
- // detect players who are close to the mine and explode if anyone should detonate it
- head = findradius(self.origin, cvar("g_balance_minelayer_detectionradius"));
+ // set the mine for detonation when a foe gets too close
+ head = findradius(self.origin, cvar("g_balance_minelayer_proximityradius"));
while(head)
{
if(head.classname == "player" && head.deadflag == DEAD_NO)
- if(head != self.owner)
- if(IsDifferentTeam(head, self.owner)) // don't detonate for team mates
+ if(head != self.owner && IsDifferentTeam(head, self.owner)) // don't trigger for team mates
if(!self.mine_time)
{
spamsound (self, CHAN_PROJECTILE, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
self.mine_time = time + cvar("g_balance_minelayer_time");
}
-
head = head.chain;
}
- // if it's time for the mine to explode, make sure no friend is in its radius
- // if an ally is detected, the explosion is delayed until he's at a safe distance
+ // explode if it's time to
if(self.mine_time && time >= self.mine_time)
- {
- head = findradius(self.origin, cvar("g_balance_minelayer_radius"));
- while(head)
- {
- if(head == self.owner || !IsDifferentTeam(head, self.owner))
- return;
- head = head.chain;
- }
- self.mine_time = 0;
- W_Mine_Explode();
- }
+ W_Mine_ProximityExplode();
+
+ // remote detonation
+ if (self.owner.weapon == WEP_MINE_LAYER)
+ if (self.owner.deadflag == DEAD_NO)
+ if (self.minelayer_detonate)
+ W_Mine_RemoteExplode();
if(self.csqcprojectile_clientanimate == 0)
UpdateCSQCProjectile(self);
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)
// 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;
for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
self.mine_number += 1;
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
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;
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");