Quake Devels - Rotation Locking Code

Tutorial Author: Cryect

Difficulty: Easy

This is a great little tutorial. Do you remember in quake 1 there was the remote control tag bombs that stick to guys till it blows up. Well this tutorial lets you do that and more. Lets say you see a turning gear shoot it and you will see the shot move with it if you use this code. I even have used this in fact I thank WolfWings ShadowFlight for saving me all this time I would have spent doing this. If you wish to check out how I used the code go see ST Weapon Factory. Unfortunately we have not released the code since currently we are in beta but soon I will go over a tutorial on a sentry gun I think. Well here is the code.

/*

=================

stuck_prethink

This is a series of support routines for locking an object against another,

I.E. Proximity mines, Arrows, C4, etc, etc...

The other object can rotate and move at will, the 'stuck to' object won't

budge from the spot it hit, period.

stick(edict_t *projectile, edict_t *object)

Here's the entry point to this function.

Projectile is, I.E. the arrow.

Object is, I.E. the wall, or tank, or icarus, or makron, etc, etc...

Stick will set up all the required stuff properly,

including movetype, solid, etc, etc.

To use, simply call the stick(projectile, object) function.

projectile is then 'glued' to object, unless object is the world,

in which case it is simply held in place. :-)

=================

*/

void VectorRotate(vec3_t in, vec3_t angles, vec3_t out) {

float cv, sv, angle, tv;

VectorCopy(in, out);

angle = (-angles[PITCH]) * M_PI / 180;

cv = cos(angle);

sv = sin(angle);

tv = (out[0] * cv) - (out[2] * sv);

out[2] = (out[2] * cv) + (out[0] * sv);

out[0] = tv;

angle = (angles[YAW]) * M_PI / 180;

cv = cos(angle);

sv = sin(angle);

tv = (out[0] * cv) - (out[1] * sv);

out[1] = (out[1] * cv) + (out[0] * sv);

out[0] = tv;

angle = (angles[ROLL]) * M_PI / 180;

cv = cos(angle);

sv = sin(angle);

tv = (out[1] * cv) - (out[2] * sv);

out[2] = (out[2] * cv) + (out[1] * sv);

out[1] = tv;

}

void VectorUnrotate(vec3_t in, vec3_t angles, vec3_t out) {

float cv, sv, angle, tv;

VectorCopy(in, out);

angle = (-angles[ROLL]) * M_PI / 180;

cv = cos(angle);

sv = sin(angle);

tv = (out[1] * cv) - (out[2] * sv);

out[2] = (out[2] * cv) + (out[1] * sv);

out[1] = tv;

angle = (-angles[YAW]) * M_PI / 180;

cv = cos(angle);

sv = sin(angle);

tv = (out[0] * cv) - (out[1] * sv);

out[1] = (out[1] * cv) + (out[0] * sv);

out[0] = tv;

angle = (angles[PITCH]) * M_PI / 180;

cv = cos(angle);

sv = sin(angle);

tv = (out[0] * cv) - (out[2] * sv);

out[2] = (out[2] * cv) + (out[0] * sv);

out[0] = tv;

}

void stuck_prethink (edict_t *self)

{

vec3_t temp, new;

vec_t tv, xv, yv, zv;

float angle, cv, sv;

edict_t *other;

other = self->goalentity;

if (!other->inuse) {

}

VectorRotate(self->pos1, other->s.angles, temp);

VectorRotate(self->pos2, other->s.angles, new);

VectorSubtract(new, temp, new);

vectoangles(new, self->s.angles);

}

void Calc_StuckOffset(edict_t *self, edict_t *other) {

float cv, sv;

float angle;

vec_t tv, xv, yv, zv;

vec3_t forward;

VectorSubtract(self->s.origin, other->s.origin, forward);

VectorUnrotate(forward, other->s.angles, self->pos1);

AngleVectors(self->s.angles, forward, NULL, NULL);

VectorMA(self->s.origin, 64, forward, forward);

VectorSubtract(forward, other->s.origin, forward);

VectorUnrotate(forward, other->s.angles, self->pos2);

}

void stick(edict_t *projectile, edict_t *object) {

projectile->solid = SOLID_NOT;

projectile->movetype = MOVETYPE_FLY;

VectorClear(projectile->velocity);

VectorClear(projectile->avelocity);

if (object != g_edicts) {

Calc_StuckOffset(projectile, object);

projectile->goalentity = object;

projectile->prethink = stuck_prethink;

} else

projectile->prethink = NULL;

}

/*

=================

dart_prethink

This is a support routine for keeping an

object velocity-aligned, for I.E. arrows.

=================

*/

void dart_prethink (edict_t *ent) {

vec3_t move;

vectoangles(ent->velocity, move);

VectorSubtract(move, ent->s.angles, move);

move[0] = fmod((move[0] + 180), 360) - 180;

move[1] = fmod((move[1] + 180), 360) - 180;

move[2] = fmod((move[2] + 180), 360) - 180;

VectorScale(move, 1/FRAMETIME, ent->avelocity);

}

/*

=================

fire_arrow

=================

*/

void arrow_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)

{

vec3_t origin;

int n;

if (other == ent->owner) // FIXME: Let arrow hit player, but not when they've just fired it

return;

if (surf && (surf->flags & SURF_SKY))

{

G_FreeEdict (ent);

return;

}

if (ent->owner->client)

PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);

if (other->takedamage)

T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_UNKNOWN);

ent->s.sound = 0;

ent->think = G_FreeEdict;

ent->nextthink = level.time + (FRAMETIME * 100);

stick(ent, other);

}

void fire_arrow (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed)

{

edict_t *arrow;

vec3_t forward, right, up, dir;

vectoangles (aimdir, dir);

AngleVectors (dir, forward, right, up);

arrow = G_Spawn();

VectorScale (aimdir, speed, arrow->velocity);

VectorMA (arrow->velocity, 100 + crandom() * 10.0, up, arrow->velocity);

VectorMA (arrow->velocity, crandom() * 10.0, right, arrow->velocity);

VectorCopy (start, arrow->s.origin);

vectoangles (dir, arrow->s.angles);

arrow->prethink = dart_prethink; // Keeps the arrow aligned, so it arcs through the air nicely.

arrow->movetype = MOVETYPE_TOSS;

arrow->gravity = 0.5;

arrow->solid = SOLID_BBOX;

VectorClear (arrow->mins);

VectorClear (arrow->maxs);

arrow->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2"); // The Chaos DM arrow works great, BTW.

arrow->owner = self;

arrow->touch = arrow_touch;

arrow->nextthink = level.time + 30;

arrow->think = G_FreeEdict;

arrow->dmg = damage;

arrow->s.sound = gi.soundindex ("misc/lasfly.wav");

arrow->classname = "arrow";

if (self->client)

check_dodge (self, arrow->s.origin, dir, speed);