
Posted by WarZone (209.223.137.*) on April 11,
1999 at 16:06:08:
Assimilation Tutorial #1
By: WarZone
This tutorial will allow you to add a new firing mode to your rocket launcher.
In this secondary mode players will be able to control their rockets and guide
them into targets! In order to use this code, you MUST credit to Assimilation
in your documentation/web site, and link to Assimilation. This tutorials
is meant for intermediate to advanced programmers -- ie. there isn't much
explanation. Oh well, that's life kiddo.
Okay,
let's get started. Open up g_locals.h and add the following variables to the
end of the edict_t structure:
edict_t *chasetarget; //the rocket to guide
int charge; //acceleration of the rocket
Now add the following
variables to client_persistant_t:
typedef struct
{
...
qboolean view_rocket; // okay to guide a rocket?
qboolean guided; //fire guided rockets?
} client_persistant_t;
Now open up g_weapon.c and
add this code to the top of the file (after the #includes):
void guideThink (edict_t *self)
{
vec3_t offset;
vec3_t forward;
vec3_t tvect;
float dist;
if (!self->owner ||
!self->owner->inuse)
return;
self->charge += 2;
if (self->charge > 350)
self->charge =
350;
if (self->owner->chasetarget !=
self || !self->owner->inuse)
{
VectorCopy
(self->velocity, tvect);
VectorNormalize
(tvect);
}
else
{
VectorSet (offset, 0,
0, self->owner->viewheight);
VectorAdd
(self->owner->s.origin, offset, tvect);
VectorSubtract
(tvect, self->s.origin, tvect);
AngleVectors
(self->owner->client->v_angle, forward, NULL, NULL);
VectorScale (forward,
99999999, forward);
VectorAdd (forward,
tvect, tvect);
VectorNormalize
(tvect);
}
vectoangles (tvect,
self->s.angles);
VectorScale (tvect,
self->charge + 350.0, forward);
VectorCopy (forward,
self->velocity);
self->nextthink = level.time +
FRAMETIME;
}
void
Guided_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage,
vec3_t point)
{
self->takedamage = DAMAGE_NO;
self->nextthink = level.time +
.1;
self->think = rocket_explode;
}
Then skip down to
fire_rocket() and add the this indicated code:
...
rocket->touch = rocket_touch;
rocket->nextthink = level.time + 8000/speed;
rocket->think = G_FreeEdict;
rocket->dmg = damage;
rocket->radius_dmg = radius_damage;
rocket->dmg_radius = damage_radius;
rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
rocket->classname = "rocket";
if (self->client)
{
check_dodge (self,
rocket->s.origin, dir, speed);
// new code starts here
if
(self->client->pers.guided)
{
self->chasetarget = rocket;
VectorScale (dir, 350.0, rocket->velocity);
rocket->nextthink = level.time + 0.2;
rocket->radius_dmg = radius_damage * .9;
rocket->dmg_radius = damage_radius * 1.2;
rocket->think = guideThink;
rocket->classname = "guided rocket";
rocket->clipmask = MASK_SHOT;
rocket->solid = SOLID_BBOX;
rocket->ss_charge = 0;
VectorSet(rocket->mins, -6, -6, -6);
VectorSet(rocket->maxs, 6, 6, 6);
rocket->mass = 1;
rocket->health = 10;
rocket->die = Guided_Die;
rocket->takedamage = DAMAGE_YES;
rocket->monsterinfo.aiflags = AI_NOSTEP;
}
// new code ends here
....
Now open up p_view.c and
add the indicated line to SV_CalcViewOffset():
void SV_CalcViewOffset (edict_t *ent)
{
float *angles;
float bob;
float ratio;
float delta;
vec3_t v;
//===================================
UpdateRocketCam (ent); // new line!!
...
... now that was the easy
part. Next on the agenda is to open up p_client.c and scroll down to
player_die() and add the indicated line:
void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
int mod;
VectorClear (self->avelocity);
self->chasetarget = NULL; //
New line!
...
Now open p_weapon.c and
scroll down to the Weapon_Generic() function add add the indicated lines:
...
if (ent->client->weaponstate == WEAPON_READY)
{
if ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK)
{
if (!ent->client->pers.view_rocket) // new line!!
{ // if (pers.view_rocket) <---new line!!
if ((!ent->client->ammo_index) || (ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity))
{
ent->client->ps.gunframe = FRAME_FIRE_FIRST;
ent->client->weaponstate = WEAPON_FIRING;
// start the animation
ent->client->anim_priority = ANIM_ATTACK;
if
(ent->client->ps.pmove.pm_flags & PMF_DUCKED)
{
ent->s.frame = FRAME_crattak1-1;
ent->client->anim_end = FRAME_crattak9;
}
else
{
ent->s.frame = FRAME_attack1-1;
ent->client->anim_end = FRAME_attack8;
}
}
else
{
if (level.time
>= ent->pain_debounce_time)
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"),
1, ATTN_NORM, 0);
ent->pain_debounce_time = level.time + 1;
}
NoAmmoWeaponChange
(ent);
}
} // if
(pers.view_rocket) <---new
line!!
}
else
{
ent->client->pers.view_rocket = false; //new line
ent->chasetarget = NULL; //new line
...
if (ent->client->weaponstate
== WEAPON_FIRING)
{
if
(!ent->client->pers.view_rocket) // new line
{ // new line
for (n =
0; fire_frames[n]; n++)
{
if (ent->client->ps.gunframe == fire_frames[n])
{
if
(ent->client->pers.weapon == FindItem ("rocket launcher")
&& ent->client->pers.guided) //new line
ent->client->pers.view_rocket = true; //new line
fire (ent);
break;
}
}
}
else
{
// new code starts here
if
(!(ent->client->buttons & BUTTON_ATTACK) &&
!(ent->client->oldbuttons & BUTTON_ATTACK))
{
ent->client->pers.view_rocket = false;
ent->chasetarget = NULL;
}
// new code ends here
...
WHEW! That was a real pain,
but we're on the home stretch now.. open up p_view.c again and add this
function to the top:
void UpdateRocketCam (edict_t *ent)
{
vec3_t temp;
if
(!ent->client->pers.view_rocket || ent->deadflag)
ent->chasetarget =
NULL;
if (ent->chasetarget &&
ent->chasetarget->inuse &&
!Q_stricmp(ent->chasetarget->classname, "guided rocket"))
{
AngleVectors
(ent->chasetarget->s.angles, temp, NULL, NULL);
VectorMA
(ent->chasetarget->s.origin, 5, temp, temp);
VectorClear
(ent->client->ps.pmove.origin);
temp[0] *= 8;
temp[1] *= 8;
temp[2] += 2;
temp[2] *= 8;
VectorCopy (temp,
ent->client->ps.pmove.origin);
ent->client->ps.gunindex = 0;
VectorClear
(ent->client->ps.viewoffset);
return;
}
else
{
if (!ent->deadflag
&& ent->client->pers.weapon)
ent->client->ps.gunindex =
gi.modelindex(ent->client->pers.weapon->view_model);
ent->chasetarget =
NULL;
}
}
Now open up p_client.c and
add this line to the very end of ClientThink():
...
UpdateRocketCam (ent);
}
Then scroll up a bit and
add this line right above ClientThink():
void UpdateRocketCam (edict_t *ent); // new line
/*
==============
ClientThink
This
will be called once for each client frame, which will
usually be a couple times for each server frame.
==============
*/
void ClientThink (edict_t *ent, usercmd_t *ucmd)
{
...
All righty then, all that's
left to do is add a command to enable the guided rockets. Open up g_cmds.c and
add the indicated lines to ClientCommand():
void ClientCommand (edict_t *ent)
{
...
else if (Q_stricmp(cmd, "switch_rockets") == 0)
gi.cprintf(ent, PRINT_HIGH, (ent->client->pers.guided = !ent->client->pers.guided) ? "Guided rockets enabled.\n" : "Normal rockets enabled.\n");
else // anything that doesn't match a command will be a chat
Cmd_Say_f (ent, false, true);
}
Finally, we're done! Just compile and bind a key to "switch_rockets"
to use your guided rockets.
Enjoy!
-- WarZone