Home | What's New | Communities | Contact Us  
Search:   
Home >> Computers >> Genres >> Shooter >> Quake series >> Quake 3 >> Editing >> Coding

   Q3SEEK

   Main
   About
   Playing
   Multiplay
   Console
   Technical
   Hardware
   Files
   Editing
      Modding
      Shaders
      Coding
      Mapping
      Modelling
      Skinning
      Texturing
   Cheats
   Ergonomics

   Contact


   SUBMISSIONS

   Submit Articles
   Submit News
   Submit a Site
   Submit a Bug


   HOSTED SITES

   Fightclub
   Genocidal
   LeafNode
   Q3 Bot Designer
   Q3 Post
   QUCT
   Weaponmod

 
image by Septik septik@home.com and Decayed the_decayed@hotmail.com
Coding

    Tutorial 5: Fun With Explosives

Prereqs:

be able to compile a q3 mod

By Cricel
Difficulty: Easy

An intro: I subscribe to the "walk you through the learning process" philosphy. That means I will walk you through it as if I were learning it -- for the most part (I usually have to do a LOT more work for these, mostly because I do some heavy exploring) this is exactly the way I learned how to do what is described in the tutorial -- so this will be a bit slow. However, this is a really easy tutorial so I'm expecting that mostly newbie programmers will want to use this. If you're more advanced though, you might still learn something so don't dismiss it out of hand. With that as a warning, let's begin the tutorial...

Now it's time to have fun: We're going to blow people up. In other words, we're going to add a bit to your q3 code such that when a person dies, an explosion goes off where they are.

So how do you start something like this? I want to create an explosion on top of a dead body. Well, first there's the explosion. What am I basing this explosion thing on? I want it to look like a rocket hit the person as they die. Ash... Now we can do something: hunt down the code for a missile hit. So, jump into g_missile.c. Go to line 285, it says ROCKET... Yeah, this must be what we want...Look around, what do we see? Nothing, it's useless... Agh, scroll down out of frustration, in fact, you're so frustrated you scroll down to LIGHTNING GUN and low and BEHOLD... There is this strange little line that says something about EV_MISSILE_HIT in nice big little letters... Sounds like something useful, looks like something interesting, [sniff screen here] smells like something useful...It's in big capital letters.... (note: do NOT subscribe to this logc in the future....) Yes dear children, we are on a mission from God.

Well, now what? It looks neat and it probably does something but... Search. Always search. If I find anything that looks even vaguely interesting I start doing searches (Find In Files...)and follow where the trails lead. You can find some MIGHTY interesting stuff that way... I highly recommend it. Click on the first result from a search on the missile hit thing, it's the enum for events, special but not right now. Click on the second one (should look like G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal) ); ), hm, funky, find out what function this is (always find out what function you're in when you go searching otherwise it is no where near as helpful) -- G_MissileImpact, sounds useful.scroll through it a bit, passing the instance of EV_MISSILE_HIT that got us here and hey, what's this, there's another one! Let's look at that one, it looks neat... G_AddEvent eh? Let's do a search on that, this time find the definition of it (it will end with an open bracket in the find files list) and take a look. Intersting but not useful. Hm, no luck that route, let's go back to the ever faithful EV_MISSILE_HIT.

Try one of the lower ones on the results list, the ones that start with G_TempEntity -- go to the very last one. Ha, we're back in g_weapon.c in the Lightning gun bit... Well, let's take a look. What does this do? Search for G_TempEntity, find the definition. Conveniently located just above the beginning of the definition is a section all about G_TempEntity and what do you know? It's all about making a temporary entity (read: "thingy")in q3 (entities you need to get the hang of fast, they're the building blocks of the q3 world). Sounds about right, explosions are as temporary as they come. So, let's get on with it: go back to the lightning gun code.

First line: create a temporary entity. Second: get the entity number... Entity number? What's the right hand value? Look around, the traceEnt thing sounds useful, scroll up a bit and look around some and do a little bit of regular logical figuring stuff out and we find out that the traceEnt is actually the entity that was hit by the lightning gun. Ok, so then, this is the number of the entity we hit. Q3 must keep track of these numbers somewhere. So be it (i.e. it's a tool to use, for _now_ we don't need to care about it any more than that). Third line: eventParam... yeah....whatever. I think we're going to need some explanation on this one: search it. In this particular instance though, a hint: when searching for things like this in q3 where they are a part of an entity or client state or entity state (you'll find em eventually, don't worry) and they have the -> thing (identifying a member of a pointer to a class) then odds are you should do your search for the thing starting with the ->. So, search for this (without the quotation marks): "->s.eventParm"

Scroll through the results, just look them over and you'll see that this is used for a whole bunch of stuff. Obviously id needed some random variable for holding any old value to identify something for an event; it changes depending on the event. So for right now, we don't need to worry too much about what it really is, just that it is some number based of off DirToByte (whatever that is).

Fourth line: identifies the weapon that was fired, ok that works for me.

So now we more or less have some way to create an explosion somewhere. Now we need to find a dead body to attach it to....Search for death. Oh, wow, back up, no, too much death thank you very much... Hmm, how can we find this a bit faster than sorting through all ~200 of those results?... How about MOD_[weapon]?It showed up a couple of times if we looked over the results a bit so... Search for "MOD_". Ack, quite a few still. BUT... The last one on the list says something about G_Damage...hm, maybe that is the function that does damage to people?...Let's find out. Double click the line about G_Damage in the results.

Oh, we're in the railgun firing function. How interesting, but not useful. So that was not specific enough, let's do a search on "G_Damage" itself... blah, blah, blah, crap and more crap, oh hey, the definition, let's jump to that (so do it). Allsorts of damage dealing stuff, nothing that looks like quite what we want though... Scroll down through it and... ah, wait, there at the bottom: targ->health <= 0... that sounds like a dead body waiting to happen to me... and right below that what do we find?... targ->die, how convenient. OK, do a search on "->die". Not a very long list this time, is it? OK, what have we got? body_die, player_die, body_die and targ->die where we started. How about...body_die since it comes up twice. Click on the self->die =body_die line.

It says in a comment right above the line we jumped to: "bodycan still be gibbed" Agh, body destruction. Not useful. So...Do a search on player_die. Jump to the definition. Whoa, look at that, we were IN player_die with that last body_die thing. Ah well, we're here now. Must be the place. Scope the place out. Who killed us... Tell everyone we died and how... scoring, flags, scores, yep looks about right to me. So now all we need to do is paste in our bit about an explosion. Drop this in right above the (soon to be other) TeamEntity line with EV_OBITUARY: (copy and paste it from g_weapon.c's lightning gun bit where we first saw this)

tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
tent->s.eventParm = DirToByte( tr.plane.normal );
tent->s.weapon = ent->s.weapon;

Now we're going to have to change a bit of this. We no longer have a tr (it's a trace from the railgun trail, but needless to say, we don't need it) so we're going to have to do something with that. Then we need to define some ent above to actually assign this to, tent doesn't work for me, let's make it explode (or whatever works for you). We're also going to have to figure out that DirToBytebit looks like...

So first things first: declare a gentity_t pointer called explode:

gentity_t *explode; // cricel: the explosion when you die.

then change all the "tent"s from above into "explode"s.Then since we need a place to start this explosion and we're intelligent enough to figure out that tr.endpos must be the finishing point of the railgun shot so that's where it does it's missile hit (we'll deal with the fact that the railgun is not a rocket in a second in case you're wondering about that) then we need to replace it with the spot where the dead body is: look right below at the other TempEntity bit with the orbituary: self->r.currentOrigin...Sounds about right to me... So:

explode = G_TempEntity( self->r.currentOrigin, EV_MISSILE_HIT);

is our first line now. But we don't have any traceEnt either, that's self again though because that's the person who got hit --self. So:

explode->s.otherEntityNum = self->s.number;

is our second line now. Let's do something about that DirToByte thing now. Search for it and get the definition. Blah blah blah, dotproduct, vector crap (which I assure you I know more than enough about so I'm not skipping it because I can't do it but because it is irrelevant) so it says if no vec3_t, we return 0 eh?...Let's make it zero. So:

explode->s.eventParm = 0;

is our third line now. What about this s.weapon? It doesn't immediately seem like a problem, so if you want, compile and run and see what happens. But I'll tell you now if you want to know: it won't quite work. See what's happening there right now is that it's going to do whatever "Missile Hit" is associated with that weapon type: for a railgun it will make the little circle thing, for a plasma gun or a BFG it will make a bigger circle thing and for the Rocket launcher it will make the explosion. So if you want, compile, kill a bot with a rocket launcher and it will make a SECOND explosion. We must be doing something right...But kill them with a machinegun or shotgun or anything else and they just die. How not interesting. So:

explode->s.weapon = WP_ROCKET_LAUNCHER; // makes it look like an RL hit

is our fourth line because we have figured out by doing a search on "->s.weapon" (you did right?...) that it takes a #define constant of the different weapons. So we give it the RL number and voila, compile and run it: everyone who dies gets an explosion over them when they die. WE DID IT!!!!! Bask in the glorious majesty of it all....

And then try something more... Let's add a second explosion. And a third, in fact, let's just make it a loop, I'll make the example here for three, you can go overboard if you want. We need to make sure that the explosions are not all on top of each other, so we're going to have to move them around a bit and we're going to have to make explode into an array. So:

gentity_t *explode[3]; // cricel: the explosions when you die.
vec3_t explodecent; // the center for the explosions
int loopi; // the loop counter

.....

// Cricel: the for loop for multiple explosions
for(loopi=0; loopi<3; loopi++)
{
VectorCopy(self->r.currentOrigin, explodecent);
// the above give us a center point at the dead body, if you want, move this above
// the loop and what will happen is the explosions will be able to move around a bit more

// this moves the explosions around
// crandom is a built in q3 function that gives a number between[-1, 1] inclusive
explodecent[0] += crandom() * 50;
explodecent[1] += crandom() * 50;
explodecent[2] += crandom() * 50;

// what we did before but updated to be an array
explode[loopi] = G_TempEntity( explodecent, EV_MISSILE_HIT );
explode[loopi]->s.otherEntityNum = self->s.number;
explode[loopi]->s.eventParm = 0;
explode[loopi]->s.weapon = WP_ROCKET_LAUNCHER; // makes itlook like an RL hit
}

That's our final code in g_combat.c. Nothing else is needed. All players that die now will have as many explosions on them when they die as you want... Possible extensions: change 3 in the loop initialization to random()*3 (random() is crandom except that instead of going from -1 to 1, it goes from 0 to 1) so that it will have 0 to 3 explosions when a person dies or perhaps random()*2+1for 1-3 explosions. The only other one that I really want is for time staggered explosions but due to the nature of TempEntity, my efforts to get this to work so far have failed. That and perhaps making it so that each explosion that popped off of a dead body was actually linked to a real explosion that did radius damage....

The way I attempted to add staggered time for those who would be interested in fixing it/learning from it (add this right after the ->s.weapon line, right before finishing the loop):

explode[loopi]->eventTime += random() * 1000; // explodes within the next second


Have fun
Cricel

1 Comments | Next Section  >> 
Related Q3Seek.com Links:

What is ANSI C?
Do you have an ANSI C Guide?
What is the Quake 3 VM (virtual machine)?
What preparations do I have to make to start compiling?
How do I compile with Microsoft Visual C?
My code wont compile, what's wrong?
Why does MSVC give me a 'not enough enviroment space' message?
Why do I get an "User interface version 4..." error?
    Tutorial Introduction
    Tutorial Prologue
    Tutorial 1: Vampire Mod Tutorial
    Tutorial 2: 3-Way Plasma Weapon
    Tutorial 3: Making a Laserbeam
    Tutorial 4: Model-Manager Tutorial
    Tutorial 6: Q3 Weapons 101
 

Related Internet Links:

The Hobgoblins Realm
Character Stats
http://www.geocities.com/TimesSquare/Realm/6249/

GameFAQs
"FAQS, walthroughs, codes and more for all video and computer games."
http://www.gamefaqs.com

GameSpot's videogames.com
"Information on Sega Saturn, Sony Playstation and Nintendo 64 video game products."
http://www.videogamespot.com

Sega (Japan)
Sega's Japanese website
http://www.sega.co.jp/

Sega (Japan)
Sega's Japanese website (english)
http://www.sega.co.jp/home_e.html

 
Want to help out your fellow Quakers? Why not submit an article?
Home Contact
 © 1996-2000 Seek Internet Publishing. All Rights Reserved. See our Privacy Statement