[code] Homing Grenades


(bacon) #1

This will make grenades follow a player, then blow up ten seconds after it starts chasing them.

In g_local.h
Find:

g_constructible_stats_t	constructibleStats;

Below Add:

//bacon
	int  specialTime;

This is how we’re going to control the explosion time.

Find:

//
// g_utils.c
//

Add Below:

// bacon
gentity_t *findradius(gentity_t *ent, vec3_t org, float radius);

In g_utils.c
At the very bottom of the file add:

gentity_t *findradius (gentity_t *ent, vec3_t org, float radius) {
	vec3_t	eorg;
	int		i;

	if (!ent)
		ent = g_entities;
	else
		ent++;

	for (; ent <= &g_entities[level.numConnectedClients-1]; ent++) {
		if (!ent->inuse)
			continue;

		for ( i = 0; i < 3; i++) {
			eorg[i] = org[i] - (ent->r.currentOrigin[i] + (ent->r.mins[i] + ent->r.maxs[i]) * 0.5);
		}

		if (VectorLengthSquared(eorg) > SQR(radius))
			continue;

		return ent;
	}

	return NULL;
}

In g_missile.c
Find:

/*
=================
fire_grenade

	NOTE!!!! NOTE!!!!!

	This accepts a /non-normalized/ direction vector to allow specification
	of how hard it's thrown. Â Please scale the vector before calling.

=================
*/
gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir, int grenadeWPID) {

ABOVE it add:

void G_FollowMe( gentity_t *self ) {
	gentity_t	*target = NULL;
	vec3_t  dir, start, end;

	if (!target) {
 Â while ((target = findradius(target, self->r.currentOrigin, 764)) != NULL) {

 Â 	// we must have a client
 Â 	if (!target->client) {
 Â  Â continue;
 Â 	}

 Â 	// the target must have health left
 Â 	if ( target->health <= 0 ) {
 Â  Â continue;
 Â 	}

 Â 	// the target must be able to take damage
 Â 	if ( !target->takedamage )
 Â  Â continue;

 Â 	// when ff is disabled we will not attack teammates
 Â 	if ( g_friendlyFire.integer == 0 && OnSameTeam(self, target) ) {
 Â  Â continue;
 Â 	}

 Â 	// only if there's no specialtime
 Â 	if (self->specialTime <= 0)
 Â  Â self->specialTime = level.time + 10000;

 Â 	VectorCopy(self->r.currentOrigin, start);
 Â 	VectorCopy(target->r.currentOrigin, end);

 Â 	VectorSubtract(end, start, dir);
 Â 	VectorNormalize(dir);

 Â 	// scale the movement
 Â 	VectorScale(dir, 200, self->s.pos.trDelta);
 Â 	VectorCopy(dir, self->movedir);

 Â 	SnapVector(self->s.pos.trDelta); Â // save net bandwidth
 Â }
	}

	// when we have reached our boom time, blow up
	if ( level.time > self->specialTime && self->specialTime > 0 ) {
 Â G_ExplodeMissile( self );
 Â return;
	}

	self->nextthink = level.time + 20;
	self->think = G_FollowMe;
}

This is the follow code. It will only follow clients with health, and can take damage. It will not follow teammates when friendly fire is disabled. It will find the distance between the client and the grenade, then the grenade will move closer to the client; it will hop towards the client!

Finally in fire_grenade()
Find:

case WP_GRENADE_LAUNCHER:
  	bolt->classname    = "grenade";
  	bolt->splashRadius  	= 300;
  	bolt->methodOfDeath  	= MOD_GRENADE_LAUNCHER;
 Â 	bolt->splashMethodOfDeath	= MOD_GRENADE_LAUNCHER;
  	bolt->s.eFlags    = EF_BOUNCE_HALF | EF_BOUNCE;
 Â 	break;
 Â case WP_GRENADE_PINEAPPLE:
  	bolt->classname    = "grenade";
  	bolt->splashRadius  	= 300;
  	bolt->methodOfDeath  	= MOD_GRENADE_LAUNCHER;
 Â 	bolt->splashMethodOfDeath	= MOD_GRENADE_LAUNCHER;
  	bolt->s.eFlags    = EF_BOUNCE_HALF | EF_BOUNCE;

Replace it with:

case WP_GRENADE_LAUNCHER:
 bolt->classname   = “grenade”;
 bolt->splashRadius  = 300;
 bolt->methodOfDeath  = MOD_GRENADE_LAUNCHER;
 bolt->splashMethodOfDeath = MOD_GRENADE_LAUNCHER;
 bolt->s.eFlags   = EF_BOUNCE_HALF | EF_BOUNCE;

  // bacon - stalk somebody;o
  //bolt->nextthink = level.time + 1000;
  bolt->nextthink = level.time + 10;  // 1s is buggy, sometimes the nade would sit in the ground
  bolt->think = G_FollowMe;
  bolt->parent = self;
 break;
 case WP_GRENADE_PINEAPPLE:
 bolt->classname   = “grenade”;
 bolt->splashRadius  = 300;
 bolt->methodOfDeath  = MOD_GRENADE_LAUNCHER;
 bolt->splashMethodOfDeath = MOD_GRENADE_LAUNCHER;
 bolt->s.eFlags   = EF_BOUNCE_HALF | EF_BOUNCE;

  // bacon - stalk somebody;o
  //bolt->nextthink = level.time + 1000;
  bolt->nextthink = level.time + 10;  // 1s is buggy, sometimes the nade would sit in the ground
  bolt->think = G_FollowMe;
  bolt->parent = self;



All done :)
Really all that needs to be done is a check to see how close the grenade is to a client and if it's too close it shouldn't move. Maybe also reset specialTime when there's no target found.

BTW, the aligning in the code is messed up because I copy + pasted from my post on an invisionboard.

(digibob) #2

You might want to consider working in squared space in findradius, will save all those square roots ( which will be either slow, or sloppy ) especially with large numbers of entities.

Though, since you only want to target clients, the loop in there should probably terminate at MAX_CLIENTS.


(=DaRk=CrAzY-NuTTeR) #3

looks good, it means shit to me but homeing granades sound good


(bacon) #4

^^ Updated.
findradius now deals with only connected clients, and uses squares.


(ujuwegein) #5

cool!


(bacon) #6

This will kill all homing grenades when you die:
In g_combat.c (player_die())
Find:

G_FadeItems( self, MOD_SATCHEL );

Below it add:

G_FadeItems( self, MOD_GRENADE_LAUNCHER );

Change the G_FollowMe() code to the following to make it chase the closest client who is not the parent.

void G_FollowMe( gentity_t *self ) {
	gentity_t	*target = self->parent;
	gentity_t	*newtarget = NULL;
	vec3_t		dir, start, end;

	if (!newtarget) {
		while ((newtarget = findradius(newtarget, self->r.currentOrigin, 764)) != NULL) {

			// we must have a client
			if (!newtarget->client) {
				continue;
			}

			// the target must have health left
			if ( newtarget->health <= 0 ) {
				continue;
			}

			// the target must be able to take damage
			if ( !newtarget->takedamage )
				continue;

			// the target cannot be the parent
			// UPDATE: yes it can...
			//if ( newtarget == self->parent )
			//	continue;

			// when ff is disabled we will not attack teammates
			if ( g_friendlyFire.integer == 0 && OnSameTeam(self, newtarget) ) {
				continue;
			}

			// if our target is our parent, the new target autotmatically becomes the target
			if ( target == self->parent ) {
				target = newtarget;
			}
			// if our new target is closer, he becomes the target
			else if (Distance(self->r.currentOrigin, newtarget->r.currentOrigin) < Distance(self->r.currentOrigin, target->r.currentOrigin)) {
				target = newtarget;
			}
		}
	}

	// only if there's no specialtime
	if (self->specialTime <= 0)
		self->specialTime = level.time + 10000;

	// do nothing if we're too close
	// this works somewhat, maybe make the radius bigger...
	if (!findradius(target, self->r.currentOrigin, 10)) {
		VectorCopy(self->r.currentOrigin, start);
		VectorCopy(target->r.currentOrigin, end);

		VectorSubtract(end, start, dir);
		VectorNormalize(dir);

		// scale the movement
		VectorScale(dir, 200, self->s.pos.trDelta);
		VectorCopy(dir, self->movedir);

		SnapVector(self->s.pos.trDelta);		// save net bandwidth
	}

	// we do not blow up if our parent is the target
	if ( target == self->parent ) {
		self->specialTime = 0;
	}
	// ff is off ;o
	if ( g_friendlyFire.integer == 0 && OnSameTeam( target, self->parent ) ) {
		self->specialTime = 0;
	}

	// when we have reached our boom time, blow up
	if ( level.time > self->specialTime && self->specialTime > 0 ) {
		G_ExplodeMissile( self );
		return;
	}

	self->nextthink = level.time + 30;
	self->think = G_FollowMe;
}

(pgh) #7

Should always shove a quick working compile up for us non coders to get an idea… as Crazy blabla said it means nothing… well parts but nothing else. :slight_smile:

Plus, think it’d be hilarious watching an Engy arming dyna then just running for his life… ;x


(Seph64) #8

You also might wamt to make it so that it won’t chase team mates PERIOD whether FF is on or off. It would be bad if you throw the grenade and the closest client happens to be someone on your team. And if he is TKed you might have to be looking for an excuse for the TK. Just my .02 cents.


(bacon) #9

Simple fix
Change:

if ( target == self->parent || ) {

To:

if ( target == self->parent || (OnSameTeam(target, self->parent) && !OnSameTeam(newtarget, target)) ) {

That way it’ll only go after teammates if it cannot find an enemy.


(vtxaussie) #10

Hello once again Thanks for the code.
im having some problem though, i used the code but it follows me instead of the other people.
does it have to do with this code?


if ( target == self->parent || (OnSameTeam(target, self->parent) && !OnSameTeam(newtarget, target)) ) {

Thx in advance!
aussie


(bani) #11

since it doesnt check vis, seems to me this could be (ab)used as a radar. just toss a nade and it will go towards the nearest enemy, even if they’re on the other side of a wall.