Inside3D!
     

Ricocheting Spikes

 
Post new topic   Reply to topic    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

PostPosted: Sat Apr 03, 2010 9:50 pm    Post subject: Ricocheting Spikes Reply with quote

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
View user's profile Send private message
qbism



Joined: 04 Nov 2004
Posts: 82

PostPosted: Tue Apr 20, 2010 5:34 am    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
leileilol



Joined: 15 Oct 2004
Posts: 1321

PostPosted: Tue Apr 20, 2010 8:23 am    Post subject: Re: Ricocheting Spikes Reply with quote

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


MOVETYPE_BOUNCEMISSILE Smile
_________________
Back to top
View user's profile Send private message
Teiman



Joined: 03 Jun 2007
Posts: 309

PostPosted: Tue Apr 20, 2010 2:00 pm    Post subject: Reply with quote

What is that ginormeous code post? It bigger than some 8 bits computers.
Back to top
View user's profile Send private message
Junrall



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

PostPosted: Tue Apr 20, 2010 11:36 pm    Post subject: Reply with quote

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 Smile


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
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    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