summaryrefslogtreecommitdiff
path: root/source/baseq2/g_turret.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/baseq2/g_turret.c')
-rw-r--r--source/baseq2/g_turret.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/source/baseq2/g_turret.c b/source/baseq2/g_turret.c
new file mode 100644
index 0000000..cd3c3a9
--- /dev/null
+++ b/source/baseq2/g_turret.c
@@ -0,0 +1,432 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// g_turret.c
+
+#include "g_local.h"
+
+
+void AnglesNormalize(vec3_t vec)
+{
+ while(vec[0] > 360)
+ vec[0] -= 360;
+ while(vec[0] < 0)
+ vec[0] += 360;
+ while(vec[1] > 360)
+ vec[1] -= 360;
+ while(vec[1] < 0)
+ vec[1] += 360;
+}
+
+float SnapToEights(float x)
+{
+ x *= 8.0;
+ if (x > 0.0)
+ x += 0.5;
+ else
+ x -= 0.5;
+ return 0.125 * (int)x;
+}
+
+
+void turret_blocked(edict_t *self, edict_t *other)
+{
+ edict_t *attacker;
+
+ if (other->takedamage)
+ {
+ if (self->teammaster->owner)
+ attacker = self->teammaster->owner;
+ else
+ attacker = self->teammaster;
+ T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
+ }
+}
+
+/*QUAKED turret_breach (0 0 0) ?
+This portion of the turret can change both pitch and yaw.
+The model should be made with a flat pitch.
+It (and the associated base) need to be oriented towards 0.
+Use "angle" to set the starting angle.
+
+"speed" default 50
+"dmg" default 10
+"angle" point this forward
+"target" point this at an info_notnull at the muzzle tip
+"minpitch" min acceptable pitch angle : default -30
+"maxpitch" max acceptable pitch angle : default 30
+"minyaw" min acceptable yaw angle : default 0
+"maxyaw" max acceptable yaw angle : default 360
+*/
+
+void turret_breach_fire (edict_t *self)
+{
+ vec3_t f, r, u;
+ vec3_t start;
+ int damage;
+ int speed;
+
+ AngleVectors (self->s.angles, f, r, u);
+ VectorMA (self->s.origin, self->move_origin[0], f, start);
+ VectorMA (start, self->move_origin[1], r, start);
+ VectorMA (start, self->move_origin[2], u, start);
+
+ damage = 100 + random() * 50;
+ speed = 550 + 50 * skill->value;
+ fire_rocket (self->teammaster->owner, start, f, damage, speed, 150, damage);
+ gi.positioned_sound (start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
+}
+
+void turret_breach_think (edict_t *self)
+{
+ edict_t *ent;
+ vec3_t current_angles;
+ vec3_t delta;
+
+ VectorCopy (self->s.angles, current_angles);
+ AnglesNormalize(current_angles);
+
+ AnglesNormalize(self->move_angles);
+ if (self->move_angles[PITCH] > 180)
+ self->move_angles[PITCH] -= 360;
+
+ // clamp angles to mins & maxs
+ if (self->move_angles[PITCH] > self->pos1[PITCH])
+ self->move_angles[PITCH] = self->pos1[PITCH];
+ else if (self->move_angles[PITCH] < self->pos2[PITCH])
+ self->move_angles[PITCH] = self->pos2[PITCH];
+
+ if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW]))
+ {
+ float dmin, dmax;
+
+ dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
+ if (dmin < -180)
+ dmin += 360;
+ else if (dmin > 180)
+ dmin -= 360;
+ dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
+ if (dmax < -180)
+ dmax += 360;
+ else if (dmax > 180)
+ dmax -= 360;
+ if (fabs(dmin) < fabs(dmax))
+ self->move_angles[YAW] = self->pos1[YAW];
+ else
+ self->move_angles[YAW] = self->pos2[YAW];
+ }
+
+ VectorSubtract (self->move_angles, current_angles, delta);
+ if (delta[0] < -180)
+ delta[0] += 360;
+ else if (delta[0] > 180)
+ delta[0] -= 360;
+ if (delta[1] < -180)
+ delta[1] += 360;
+ else if (delta[1] > 180)
+ delta[1] -= 360;
+ delta[2] = 0;
+
+ if (delta[0] > self->speed * FRAMETIME)
+ delta[0] = self->speed * FRAMETIME;
+ if (delta[0] < -1 * self->speed * FRAMETIME)
+ delta[0] = -1 * self->speed * FRAMETIME;
+ if (delta[1] > self->speed * FRAMETIME)
+ delta[1] = self->speed * FRAMETIME;
+ if (delta[1] < -1 * self->speed * FRAMETIME)
+ delta[1] = -1 * self->speed * FRAMETIME;
+
+ VectorScale (delta, 1.0/FRAMETIME, self->avelocity);
+
+ self->nextthink = level.time + FRAMETIME;
+
+ for (ent = self->teammaster; ent; ent = ent->teamchain)
+ ent->avelocity[1] = self->avelocity[1];
+
+ // if we have adriver, adjust his velocities
+ if (self->owner)
+ {
+ float angle;
+ float target_z;
+ float diff;
+ vec3_t target;
+ vec3_t dir;
+
+ // angular is easy, just copy ours
+ self->owner->avelocity[0] = self->avelocity[0];
+ self->owner->avelocity[1] = self->avelocity[1];
+
+ // x & y
+ angle = self->s.angles[1] + self->owner->move_origin[1];
+ angle *= (M_PI*2 / 360);
+ target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]);
+ target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]);
+ target[2] = self->owner->s.origin[2];
+
+ VectorSubtract (target, self->owner->s.origin, dir);
+ self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
+ self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;
+
+ // z
+ angle = self->s.angles[PITCH] * (M_PI*2 / 360);
+ target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]);
+
+ diff = target_z - self->owner->s.origin[2];
+ self->owner->velocity[2] = diff * 1.0 / FRAMETIME;
+
+ if (self->spawnflags & 65536)
+ {
+ turret_breach_fire (self);
+ self->spawnflags &= ~65536;
+ }
+ }
+}
+
+void turret_breach_finish_init (edict_t *self)
+{
+ // get and save info for muzzle location
+ if (!self->target)
+ {
+ gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin));
+ }
+ else
+ {
+ self->target_ent = G_PickTarget (self->target);
+ VectorSubtract (self->target_ent->s.origin, self->s.origin, self->move_origin);
+ G_FreeEdict(self->target_ent);
+ }
+
+ self->teammaster->dmg = self->dmg;
+ self->think = turret_breach_think;
+ self->think (self);
+}
+
+void SP_turret_breach (edict_t *self)
+{
+ self->solid = SOLID_BSP;
+ self->movetype = MOVETYPE_PUSH;
+ gi.setmodel (self, self->model);
+
+ if (!self->speed)
+ self->speed = 50;
+ if (!self->dmg)
+ self->dmg = 10;
+
+ if (!st.minpitch)
+ st.minpitch = -30;
+ if (!st.maxpitch)
+ st.maxpitch = 30;
+ if (!st.maxyaw)
+ st.maxyaw = 360;
+
+ self->pos1[PITCH] = -1 * st.minpitch;
+ self->pos1[YAW] = st.minyaw;
+ self->pos2[PITCH] = -1 * st.maxpitch;
+ self->pos2[YAW] = st.maxyaw;
+
+ self->ideal_yaw = self->s.angles[YAW];
+ self->move_angles[YAW] = self->ideal_yaw;
+
+ self->blocked = turret_blocked;
+
+ self->think = turret_breach_finish_init;
+ self->nextthink = level.time + FRAMETIME;
+ gi.linkentity (self);
+}
+
+
+/*QUAKED turret_base (0 0 0) ?
+This portion of the turret changes yaw only.
+MUST be teamed with a turret_breach.
+*/
+
+void SP_turret_base (edict_t *self)
+{
+ self->solid = SOLID_BSP;
+ self->movetype = MOVETYPE_PUSH;
+ gi.setmodel (self, self->model);
+ self->blocked = turret_blocked;
+ gi.linkentity (self);
+}
+
+
+/*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
+Must NOT be on the team with the rest of the turret parts.
+Instead it must target the turret_breach.
+*/
+
+void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage);
+void infantry_stand (edict_t *self);
+void monster_use (edict_t *self, edict_t *other, edict_t *activator);
+
+void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
+{
+ edict_t *ent;
+
+ // level the gun
+ self->target_ent->move_angles[0] = 0;
+
+ // remove the driver from the end of them team chain
+ for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain)
+ ;
+ ent->teamchain = NULL;
+ self->teammaster = NULL;
+ self->flags &= ~FL_TEAMSLAVE;
+
+ self->target_ent->owner = NULL;
+ self->target_ent->teammaster->owner = NULL;
+
+ infantry_die (self, inflictor, attacker, damage);
+}
+
+qboolean FindTarget (edict_t *self);
+
+void turret_driver_think (edict_t *self)
+{
+ vec3_t target;
+ vec3_t dir;
+ float reaction_time;
+
+ self->nextthink = level.time + FRAMETIME;
+
+ if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0))
+ self->enemy = NULL;
+
+ if (!self->enemy)
+ {
+ if (!FindTarget (self))
+ return;
+ self->monsterinfo.trail_time = level.time;
+ self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
+ }
+ else
+ {
+ if (visible (self, self->enemy))
+ {
+ if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
+ {
+ self->monsterinfo.trail_time = level.time;
+ self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
+ }
+ }
+ else
+ {
+ self->monsterinfo.aiflags |= AI_LOST_SIGHT;
+ return;
+ }
+ }
+
+ // let the turret know where we want it to aim
+ VectorCopy (self->enemy->s.origin, target);
+ target[2] += self->enemy->viewheight;
+ VectorSubtract (target, self->target_ent->s.origin, dir);
+ vectoangles (dir, self->target_ent->move_angles);
+
+ // decide if we should shoot
+ if (level.time < self->monsterinfo.attack_finished)
+ return;
+
+ reaction_time = (3 - skill->value) * 1.0;
+ if ((level.time - self->monsterinfo.trail_time) < reaction_time)
+ return;
+
+ self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
+ //FIXME how do we really want to pass this along?
+ self->target_ent->spawnflags |= 65536;
+}
+
+void turret_driver_link (edict_t *self)
+{
+ vec3_t vec;
+ edict_t *ent;
+
+ self->think = turret_driver_think;
+ self->nextthink = level.time + FRAMETIME;
+
+ self->target_ent = G_PickTarget (self->target);
+ self->target_ent->owner = self;
+ self->target_ent->teammaster->owner = self;
+ VectorCopy (self->target_ent->s.angles, self->s.angles);
+
+ vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
+ vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
+ vec[2] = 0;
+ self->move_origin[0] = VectorLength(vec);
+
+ VectorSubtract (self->s.origin, self->target_ent->s.origin, vec);
+ vectoangles (vec, vec);
+ AnglesNormalize(vec);
+ self->move_origin[1] = vec[1];
+
+ self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
+
+ // add the driver to the end of them team chain
+ for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
+ ;
+ ent->teamchain = self;
+ self->teammaster = self->target_ent->teammaster;
+ self->flags |= FL_TEAMSLAVE;
+}
+
+void SP_turret_driver (edict_t *self)
+{
+ if (deathmatch->value)
+ {
+ G_FreeEdict (self);
+ return;
+ }
+
+ self->movetype = MOVETYPE_PUSH;
+ self->solid = SOLID_BBOX;
+ self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
+ VectorSet (self->mins, -16, -16, -24);
+ VectorSet (self->maxs, 16, 16, 32);
+
+ self->health = 100;
+ self->gib_health = 0;
+ self->mass = 200;
+ self->viewheight = 24;
+
+ self->die = turret_driver_die;
+ self->monsterinfo.stand = infantry_stand;
+
+ self->flags |= FL_NO_KNOCKBACK;
+
+ level.total_monsters++;
+
+ self->svflags |= SVF_MONSTER;
+ self->s.renderfx |= RF_FRAMELERP;
+ self->takedamage = DAMAGE_AIM;
+ self->use = monster_use;
+ self->clipmask = MASK_MONSTERSOLID;
+ VectorCopy (self->s.origin, self->s.old_origin);
+ self->monsterinfo.aiflags |= AI_STAND_GROUND|AI_DUCKED;
+
+ if (st.item)
+ {
+ self->item = FindItemByClassname (st.item);
+ if (!self->item)
+ gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
+ }
+
+ self->think = turret_driver_link;
+ self->nextthink = level.time + FRAMETIME;
+
+ gi.linkentity (self);
+}