123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- /*
- 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);
- }
|