Code3Arena

PlanetQuake | Code3Arena | Tutorials | << Prev | Tutorial 11 | Next >>

menu

  • Home/News
  • ModSource
  • Compiling
  • Help!!!
  • Submission
  • Contributors
  • Staff
  • Downloads

    Tutorials
    < Index >
    1. Mod making 101
    2. Up 'n running
    3. Hello, QWorld!
    4. Infinite Haste
    5. Armor Piercing Rails
    6. Bouncing Rockets
    7. Cloaking
    8. Ladders
    9. Favourite Server
    10. Flame Thrower
    11. Vortex Grenades
    12. Grapple
    13. Lightning Discharge
    14. Locational Damage
    15. Leg Shots
    16. Weapon Switching
    17. Scoreboard frag-rate
    18. Vortex Grenades II
    19. Vulnerable Missiles
    20. Creating Classes
    21. Scrolling Credits
    22. Weapon Dropping
    23. Anti-Gravity Boots
    24. HUD scoreboard
    25. Flashlight and laser
    26. Weapon Positioning
    27. Weapon Reloading
    28. Progressive Zooming
    29. Rotating Doors
    30. Beheading (headshot!)
    31. Alt Weapon Fire
    32. Popup Menus I
    33. Popup Menus II
    34. Cluster Grenades
    35. Homing Rockets
    36. Spreadfire Powerup
    37. Instagib gameplay
    38. Accelerating rockets
    39. Server only Instagib
    40. Advanced Grapple Hook
    41. Unlagging your mod


    Articles
    < Index >
    1. Entities
    2. Vectors
    3. Good Coding
    4. Compilers I
    5. Compilers II
    6. UI Menu Primer I
    7. UI Menu Primer II
    8. UI Menu Primer III
    9. QVM Communication, Cvars, commands
    10. Metrowerks CodeWarrior
    11. 1.27g code, bugs, batch


    Links

  • Quake3 Files
  • Quake3 Forums
  • Q3A Editing Message Board
  • Quake3 Editing


    Feedback

  • SumFuka
  • Calrathan
  • HypoThermia
  • WarZone





    Site Design by:
    ICEmosis Design


  •  
    TUTORIAL 11 - Vortex Grenades
    by AssKicka

    This tutorial will show you how to convert the grenade launcher's grenades into Vortex Grenades. Vortex Grenades ? They are simply grenades that suck players closer and when the player hits the grenade it explodes.

    First we need to add a new utility function that can search the world to find a target that will be sucked into our vortex grenade. Open g_utils.c and add the bellow function to the end of the file :

    /*
    ================
    findradius
    ================
    */
    gentity_t *findradius (gentity_t *ent, vec3_t org, float rad) {
    
    	vec3_t eorg;
    	int j;
    
    	if (!ent)
    		ent = g_entities;
    	else
    		ent++;
    
    	for (; ent < &g_entities[level.num_entities]; ent++)
    		{
    		if (!ent->inuse)
    			continue;
    
    		for (j=0; j<3; j++)
    			eorg[j] = org[j] - (ent->r.currentOrigin[j] + 
    			(ent->r.mins[j] + ent->r.maxs[j])*0.5);
    		if (VectorLength(eorg) > rad)
    			continue;
    		return ent;
    	}
    	return NULL;
    } 

    I did not code the findradius function but it has been re-done many times from the q2 days. Lets see if we can disect this function.
    This function runs through all the entities in a level and checks if any entities are in the radius (rad) of the entity 'ent'. The 'ent' entity and 'rad' float are passed to this function from the calling function, you will see how this works in a second.

    Now that our findradius is complete we need to add the function prototype in g_local.h. goto line 438 and add the code bellow :

    gentity_t *findradius (gentity_t *ent, vec3_t org, float rad); 

    Now we will add our grenade think function, goto line 326 in g_missile.c and add the following function :

    /*
    =================
    G_Suck
    =================
    */
    static void G_Suck( gentity_t *self ) {
    	gentity_t *target;
    	vec3_t start,dir,end;
    
    	target = NULL;
    
    //check if there are any entity's within a radius of 500 units.
    	while ((target = findradius(target, self->r.currentOrigin, 500)) != NULL)
    
    		{
    // target must not be vortex grenade
    		if (target == self) 
    			continue;
    
    // target must be a client
    		if (!target->client) 
    			continue;
    
    // target must not be the player who fired the vortex grenade 
    		if (target == self->parent) 
    			continue;
    
    // target must be able to take damage
    		if (!target->takedamage) 
    			continue;
    
    // put target position in start
    		VectorCopy(target->r.currentOrigin, start); 
    // put grenade position in end
    		VectorCopy(self->r.currentOrigin, end); 
    // subtract start from end to get directional vector
    		VectorSubtract(end, start, dir); 
    		VectorNormalize(dir); 
    // scale directional vector by 200 and add to the targets velocity
    		VectorScale(dir,200, target->client->ps.velocity);
    // make targets move direction = to directional vector.
    		VectorCopy(dir, target->movedir); 
           
    		}
    
    		self->nextthink = level.time + 20; 
    
    // check if vortext grenade is older than 20 seconds.
    		if (level.time > self->wait) 
    			G_ExplodeMissile( self);
    }

    Now for the explanation :
    The first thing we did is to create new three dimensional vectors, nl. start,dir and end, we will use these vectors when modifying the velocity of the affected entity. We also created a new entity type called 'target', we will use this to identify with the entity that will be sucked into the vortex grenade.
    On line 12 you will see a while statement that checks if target is NOT NULL after target is returned from the findradius function (Rmember the findradius function we added earlier ?).
    If target is NOT NULL (target has been found by the findradius function) we then do some checks on what entity the target is.
    If the target is itself we continue the while statement (search for new target).
    If the target is NOT a client then search for new target.
    If the target is the entity (player) that fired the grenade then search for a new target.
    If the target can't be damaged then search for a new target.
    If NONE of the above conditions apply then we modify the velocity of the target to nudge it towards the grenade. Let's look at how we did this :
    1) On line 26 we copied the target's current position into the 'start' vector.
    2) On line 27 we copied the grenade's current position into the 'end' vector.
    3) On line 28 we subtracted the 'start' vector from the 'end' vector to get the directional (dir) vector.
    4) On line 30 we scaled the directional vector by 200 and put the result into the target velocity, this is what makes the target nudge closer to the grenade.
    5) On line 31 we change the players direction to match the directional vector.
    On line 35 we make sure our G_Suck function is executed in .02 seconds from now.
    On line 37 we check if the grenade has been in the world for more than 20 seconds, if it has been we explode the vortext grenade, we have achived this my making the grenades self->wait = to 20000.

    We will now change the fire_grenade function to use our G_Suck function as the grenades think function. Find the 'fire_grenade' function and modify the bellow code :

    bolt = G_Spawn();
    bolt->classname = "grenade";
    bolt->nextthink = level.time + 2500;
    bolt->think = G_ExplodeMissile;

    to look like this

    bolt = G_Spawn();
    bolt->classname = "grenade";
    bolt->nextthink = level.time + 1000; // call G_Suck in 1 second
    bolt->think = G_Suck;
    bolt->wait = level.time + 20000; // vortext grenade lifetime.

    All we have done here is changed the think function of the grenade to G_Suck and set it to be called 1 second after the grenade is fired. We also set the bolt->wait to the current time + 20 seconds. This is the amount of time that will be allowed to pass before the grenade explodes, that's if a player has not been sucked into the grenade yet !

    That's it, compile and enjoy !

    PlanetQuake | Code3Arena | Tutorials | << Prev | Tutorial 11 | Next >>