Ricocheting spikes is nothing new and has been done for similar weapons... I can in no way take credit for the idea of ricocheting spikes... in fact, the credit goes to a fellow by the name of Charlie Zimmerman for his version ricocheting pikes. And Dr. Shadowborg for Rebovec (Very useful and very cool!)
All I really did was shrink the code down a bit.
You are welcome to use this to your heart's desire.
If you have any feedback please speak up! Particularly in ways of shrinking the code further or simplifying it... maybe Darkplaces has a better/simpler way of doing something?
Alright.... here is my finished version of ricocheting spikes. Also included is a "bleeding door" fix... nothing new and is not necessary, but allows some realism to the new spike code.
Spikes will now ricochet off of pretty much everything (if "Bleeding Door" fix is applied)... including doors, lifts, plats, and exploding boxes(which still explode as normal). Every now and again a spike will bounce away rather than ricocheting. Also, some spikes will stick into a solid object, including doors, lifts, plats, and exploding boxes. Spikes will travel/move with the door, lift, or plat it is stuck to.
Note: Ricocheting spikes requires the Darkplaces engine and it's dpextensions.qc file as the spike code uses DP's MOVETYPE_BOUNCEMISSILE for ricochets and MOVETYPE_FOLLOW for making a spike follow doors, lifts, and plats.
I do have a non Darkplaces version that works as well, but needs to be cleaned up!
Also note that I used custom sound files which can be downloaded here: http://download299.mediafire.com/txx144 ... Spikes.zip
If you aren't using the "Bleeding Door" fix then you'll need to uncomment two lines.
At the very top:
Code: Select all
//.float i_bleed;
Code: Select all
//other.i_bleed = TRUE;
Code: Select all
.float sticksoundtype
.float ricodrop;
.entity oldowner;
float crandom;
//.float i_bleed; // Uncomment this if you are NOT using my Bleading Door Fix.
vector(vector org, vector dir, float justnorm) ReboVec =
{
local vector spot1, spot2, olddir, result;
olddir = normalize(dir);
if(justnorm)
{
traceline(org, org + dir*10, FALSE, self);
result = trace_plane_normal;
return result;
}
spot1 = org - (16*olddir);
spot2 = org + (16*olddir);
traceline(spot1, spot2, FALSE, self);
self.origin = trace_endpos;
result = olddir-(trace_plane_normal*(trace_plane_normal*olddir)*2);
result = normalize(result);
return result;
};
void (entity ent, vector ang, vector org) FollowEntity =
{
self.movetype = MOVETYPE_FOLLOW; // Tell DP that this entity is going to follow something
self.solid = SOLID_NOT; // Make this entity a non-solid
self.aiment = other; // Make this entity follow the selected entity
self.punchangle = other.angles; // Set the original angles of the selected entity to this entity
self.view_ofs = self.origin - other.origin; // Set this entity's origin to the selected entity's relative origin
self.v_angle = self.angles - other.angles; // Set this entity's origin to the selected entity's relative angles
};
void() RicoThink =
{
if (self.attack_finished < time) // Remove spike when time is up
{
remove(self);
}
else if (self.ricodrop) // Spike falls from object that it is sticking to
{
self.movetype = MOVETYPE_BOUNCE; // Cuases the spike to fall from the wall and bounce when it hits a solid
self.solid = SOLID_BBOX; // Make solid so that spike makes a bounce sound
makevectors(self.v_angle); // Grab the x,y,z values form the vector v_angle and place them into v_right, v_forward, v_up... I think?
self.avelocity = v_forward * 600; // Causes the spike to flip over backwards at the speed of 600
}
self.nextthink = time + 0.1; // Finish the thinking so that the spikes will remove themselves
};
void() RicoSpikeTouch =
{
local float ran_rico;
if(self.owner != self) // Checking to see if spike is it's own owner
{
self.oldowner = self.owner; // Store origanal owner (you) away for later use
self.owner = self; // Make the spike the owner of itself so that it can damage it's origanal owner
}
if (other.solid == SOLID_TRIGGER) // If the spike touched a trigger field, do nothing, just pass through it
return;
if (pointcontents(self.origin) == CONTENT_SKY) // Touched the sky, remove the spike and do nothing
{
remove(self);
return;
}
if (other.takedamage && self.movetype != MOVETYPE_BOUNCE) // If something that can take damage and if the spike doesn't bounce
{
//other.i_bleed = TRUE; //Uncomment this line if you are NOT using my Bleading Door Fix
if (other.i_bleed) // Bleeding Door Fix - Only living things bleed
spawn_touchblood (9);
if(self.oldowner.weapon == IT_NAILGUN) // Regular Nailgun does 9 damage
T_Damage (other, self, self.oldowner, 9);
if(self.oldowner.weapon == IT_SUPER_NAILGUN) // Super Nailgun does 18 damage
T_Damage (other, self, self.oldowner, 18);
if(other.i_bleed) // If the spike hits a living thing then remove the spike.
remove(self);
}
if(self.movetype != MOVETYPE_BOUNCE) // If the spike can bounce then don't spark on solid objects
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
// Determines what type of sound (Quake's sounds) to play and what type of spark
if (self.classname == "wizspike")
WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
else if (self.classname == "knightspike")
WriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);
else
WriteByte (MSG_BROADCAST, TE_SPIKE);
WriteCoord (MSG_BROADCAST, self.origin_x);
WriteCoord (MSG_BROADCAST, self.origin_y);
WriteCoord (MSG_BROADCAST, self.origin_z);
}
ran_rico = random (); // USed to randomize spike sounds
if (self.movetype == MOVETYPE_BOUNCE) // If the spike can bounce then make a random sound when it hits a solid
{
if (self.sticksoundtype == 1)
sound (self, CHAN_VOICE, "weapons/ricodrop1.wav", 0.25, ATTN_IDLE);
else
sound (self, CHAN_VOICE, "weapons/ricodrop2.wav", 0.25, ATTN_IDLE);
}
else if (random() < .4) //Stick
{
self.ricodrop = TRUE; // Tells the think functiion that this spike will eventually become unstuck and fall.
self.solid = SOLID_NOT; // Spike is no longer solid, now it can't do damage if it is stuck into something.
self.origin = self.origin - 6 * normalize(self.velocity); // Pushes the spike's origin back 6 units so that just the tip is sticking into something.
self.avelocity = '0 0 0'; // Stop the spike from rotating in all directions.
self.velocity = '0 0 0'; // Stop the spike from moving in any direction.
if(other.classname == "door" || other.classname == "plat" || other.classname == "train")
{
FollowEntity(other, other.angles, other.origin); // If stuck in a door, plat, or train then allow the spike to move with the door, plat, or train. Note: Darkplaces dependant.
}
// Make a random sound when the spike sticks into something
if (ran_rico >= 0.5)
{
sound (self, CHAN_VOICE, "weapons/ricostick1.wav", 1, ATTN_IDLE);
self.sticksoundtype = 1; // Remember what sound it makes. Later, when the spike falls, the bounce/drop sound will sound similar.
}
else
{
sound (self, CHAN_VOICE, "weapons/ricostick2.wav", 1, ATTN_IDLE);
self.sticksoundtype = 2;
}
self.nextthink = time + random()*10; // Each spike that sticks will fall out different random times.
self.attack_finished = self.nextthink + 3; // Give the spike enough time to fall out and hit the floor.
}
else if (random() < .2) // Bounce away from a solid. Note: this is not a ricochet... more like a random bounce-away!
{
self.movetype = MOVETYPE_BOUNCE; // Enable the spike to bounce when it hits something
self.solid = SOLID_NOT; // Spike is no longer solid, now it can't do damage.
self.avelocity = v_forward*crandom()*600 + v_right*crandom()*600 + v_up*crandom()*600; // Randomly spin/rotate the spike in a direction
self.velocity = self.velocity*0.75 + v_forward*crandom()*200 + v_right*crandom()*200 + v_up*crandom()*200; // Randomly accelerate the spike at a random speed/velocity.
}
else //ricochet
{
self.angles = vectoangles(ReboVec(self.origin, self.velocity, FALSE)); // Orient the spike to the new direction of the ricochet
// Play random sound when the spike ricochets
if (ran_rico >= 0.666)
sound (self, CHAN_VOICE, "weapons/rico1.wav", 1, ATTN_IDLE);
else if (ran_rico >= 0.333)
sound (self, CHAN_VOICE, "weapons/rico2.wav", 1, ATTN_IDLE);
else if (ran_rico >= 0)
sound (self, CHAN_VOICE, "weapons/rico3.wav", 1, ATTN_IDLE);
}
};
void(vector org, vector dir) LaunchRicoSpike =
{
newmis = spawn ();
newmis.owner = self;
newmis.movetype = MOVETYPE_BOUNCEMISSILE;
newmis.solid = SOLID_BBOX;
newmis.angles = vectoangles(dir);
newmis.touch = RicoSpikeTouch;
newmis.classname = "spike";
newmis.think = RicoThink;
newmis.nextthink = time;
newmis.attack_finished = time + 10;
setmodel (newmis, "progs/spike.mdl");
setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
setorigin (newmis, org);
newmis.velocity = dir * 1000;
};
void(float ox) W_FireRicoSpikes =
{
local vector dir;
makevectors (self.v_angle);
if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)
{
sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
self.currentammo = self.ammo_nails = self.ammo_nails - 2;
dir = aim (self, 1000);
LaunchRicoSpike (self.origin + '0 0 16', dir);
self.punchangle_x = -2;
return;
}
if (self.ammo_nails < 1)
{
self.weapon = W_BestWeapon ();
W_SetCurrentAmmo ();
return;
}
sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM);
self.currentammo = self.ammo_nails = self.ammo_nails - 1;
dir = aim (self, 1000);
LaunchRicoSpike (self.origin + '0 0 16' + v_right*ox, dir);
self.punchangle_x = -2;
};
Bleeding Door/Wall/Exploding Box fix:
Code: Select all
Step 1 - Open def.qc and at the very bottom put:
.float i_bleed; // Bleeding Door Fix
Step 2 - Open each monster file and locate each function that starts with "monster_". At or near the bottom of each function put:
self.i_bleed = TRUE; //Bleeding Door Fix
Step 3 - Open client.qc and find PutClientInServer. At or near the bottom put:
self.i_bleed = TRUE; //Bleeding Door Fix
Step 4 - Open weapons.qc and find W_FireAxe function.
Replace:
if (trace_ent.takedamage)
{
trace_ent.axhitme = 1;
SpawnBlood (org, '0 0 0', 20);
T_Damage (trace_ent, self, self, 20);
}
else
{ // hit wall
sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
}
with:
if (trace_ent.takedamage)
{
trace_ent.axhitme = 1;
if (trace_ent.i_bleed)//bleeding Door fix
SpawnBlood (org, '0 0 0', 20);
T_Damage (trace_ent, self, self, 20);
}
if (!trace_ent.i_bleed)//bleeding Door fix
{ // hit wall
sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
}
Find TraceAttack function.
Replace:
if (trace_ent.takedamage)
{
SpawnBlood (org, vel*0.2, damage);
AddMultiDamage (trace_ent, damage);
}
else
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
}
With:
if (trace_ent.takedamage)
{
if (trace_ent.i_bleed)//bleeding Door fix
SpawnBlood (org, vel*0.2, damage);
AddMultiDamage (trace_ent, damage);
}
if (!trace_ent.i_bleed)//bleeding Door fix
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
}
Find LightningDamage function.
Replace all (three of them):
particle (trace_endpos, '0 0 100', 225, damage*4);
With:
if (trace_ent.i_bleed)//Bleeding Door Fix
particle (trace_endpos, '0 0 100', 225, damage*4);
Find spike_touch function.
Replace:
if (other.takedamage)
{
spawn_touchblood (9);
T_Damage (other, self, self.owner, 9);
}
else
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
if (self.classname == "wizspike")
WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
else if (self.classname == "knightspike")
WriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);
else
WriteByte (MSG_BROADCAST, TE_SPIKE);
WriteCoord (MSG_BROADCAST, self.origin_x);
WriteCoord (MSG_BROADCAST, self.origin_y);
WriteCoord (MSG_BROADCAST, self.origin_z);
}
With:
if (other.takedamage)
{
if (other.i_bleed)//bleeding Door Fix
spawn_touchblood (9);
T_Damage (other, self, self.owner, 9);
}
if (!other.i_bleed)//bleeding Door Fix
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
if (self.classname == "wizspike")
WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
else if (self.classname == "knightspike")
WriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);
else
WriteByte (MSG_BROADCAST, TE_SPIKE);
WriteCoord (MSG_BROADCAST, self.origin_x);
WriteCoord (MSG_BROADCAST, self.origin_y);
WriteCoord (MSG_BROADCAST, self.origin_z);
}
Find spike_superspike_touch function.
Replace:
if (other.takedamage)
{
spawn_touchblood (18);
T_Damage (other, self, self.owner, 18);
}
else
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
WriteCoord (MSG_BROADCAST, self.origin_x);
WriteCoord (MSG_BROADCAST, self.origin_y);
WriteCoord (MSG_BROADCAST, self.origin_z);
}
With:
if (other.takedamage)
{
if (other.i_bleed)//Bleeding Door Fix
spawn_touchblood (18);
T_Damage (other, self, self.owner, 18);
}
if (!other.i_bleed)//Bleeding Door Fix
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
WriteCoord (MSG_BROADCAST, self.origin_x);
WriteCoord (MSG_BROADCAST, self.origin_y);
WriteCoord (MSG_BROADCAST, self.origin_z);
}
Let me know what you think!