Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

Ricocheting Spikes

| Inside3d Forums Forum Index -> QuakeC Programming

View previous topic :: View next topic
Author | Message
--- | ---
Junrall Joined: 21 Sep 2009 Posts: 136 Location: North West Oregon, USA |

Posted: Sat Apr 03, 2010 9:50 pm Post subject: Ricocheting Spikes |

Been working on this for a while and wish to give something back to the community for all of the questions I've been asking and receiving answers for. 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/txx144ol2e3g/2zwgyayzwnz/RicoSpikes.zip
If you aren't using the "Bleeding Door" fix then you'll need to uncomment two lines.
At the very top:

Code:
//.float i_bleed;

and in RicoSpikeTouch function in the fourth "if" statement:

Code:
//other.i_bleed = TRUE;

Ricocheting Spikes:

Code:
.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:
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);
}

One thing I should add is decreasing the damage after each ricochet and maybe a bounced spike should do some sort of small damage instead of none.
Let me know what you think!
_________________
Good God! You shot my leg off!

Back to top |

qbism Joined: 04 Nov 2004 Posts: 82 |

Posted: Tue Apr 20, 2010 5:34 am Post subject: |

Cool, Junrall! Just gave it a quick try and it works. Can bounce back and damage self. Sticks to plats. Fun! _________________ http://qbism.com

Back to top |

leileilol Joined: 15 Oct 2004 Posts: 1321 |

Posted: Tue Apr 20, 2010 8:23 am Post subject: Re: Ricocheting Spikes |

Junrall wrote:
maybe Darkplaces has a better/simpler way of doing something?

MOVETYPE_BOUNCEMISSILE
_________________

Back to top |

Teiman Joined: 03 Jun 2007 Posts: 309 |

Posted: Tue Apr 20, 2010 2:00 pm Post subject: |

What is that ginormeous code post? It bigger than some 8 bits computers.

Back to top |

Junrall Joined: 21 Sep 2009 Posts: 136 Location: North West Oregon, USA |

Posted: Tue Apr 20, 2010 11:36 pm Post subject: |

Don't know if you noticed... the spikes will also stick to and ricochet off of exploding boxes... at least the first few will then the box will explode! Kinda cool to see as it's not a normal thing that happens!

leileilol wrote:

Junrall wrote:
maybe Darkplaces has a better/simpler way of doing something?

MOVETYPE_BOUNCEMISSILE

Yep, that's what I used... I love this movetype!
I do have a rough version of the same ricocheting spikes that do not use MOVETYPE_BOUNCEMISSILE... what a pain!
_________________
Good God! You shot my leg off!

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> QuakeC Programming | All times are GMT
Page 1 of 1

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group