|
|
1.1.1.2 ! root 1: /* ! 2: Copyright (C) 1997-2001 Id Software, Inc. ! 3: ! 4: This program is free software; you can redistribute it and/or ! 5: modify it under the terms of the GNU General Public License ! 6: as published by the Free Software Foundation; either version 2 ! 7: of the License, or (at your option) any later version. ! 8: ! 9: This program is distributed in the hope that it will be useful, ! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ! 12: ! 13: See the GNU General Public License for more details. ! 14: ! 15: You should have received a copy of the GNU General Public License ! 16: along with this program; if not, write to the Free Software ! 17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ! 18: ! 19: */ 1.1 root 20: // g_turret.c 21: 22: #include "g_local.h" 23: 24: 25: void AnglesNormalize(vec3_t vec) 26: { 27: while(vec[0] > 360) 28: vec[0] -= 360; 29: while(vec[0] < 0) 30: vec[0] += 360; 31: while(vec[1] > 360) 32: vec[1] -= 360; 33: while(vec[1] < 0) 34: vec[1] += 360; 35: } 36: 37: float SnapToEights(float x) 38: { 39: x *= 8.0; 40: if (x > 0.0) 41: x += 0.5; 42: else 43: x -= 0.5; 44: return 0.125 * (int)x; 45: } 46: 47: 48: void turret_blocked(edict_t *self, edict_t *other) 49: { 50: edict_t *attacker; 51: 52: if (other->takedamage) 53: { 54: if (self->teammaster->owner) 55: attacker = self->teammaster->owner; 56: else 57: attacker = self->teammaster; 58: T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH); 59: } 60: } 61: 62: /*QUAKED turret_breach (0 0 0) ? 63: This portion of the turret can change both pitch and yaw. 64: The model should be made with a flat pitch. 65: It (and the associated base) need to be oriented towards 0. 66: Use "angle" to set the starting angle. 67: 68: "speed" default 50 69: "dmg" default 10 70: "angle" point this forward 71: "target" point this at an info_notnull at the muzzle tip 72: "minpitch" min acceptable pitch angle : default -30 73: "maxpitch" max acceptable pitch angle : default 30 74: "minyaw" min acceptable yaw angle : default 0 75: "maxyaw" max acceptable yaw angle : default 360 76: */ 77: 78: void turret_breach_fire (edict_t *self) 79: { 80: vec3_t f, r, u; 81: vec3_t start; 82: int damage; 83: int speed; 84: 85: AngleVectors (self->s.angles, f, r, u); 86: VectorMA (self->s.origin, self->move_origin[0], f, start); 87: VectorMA (start, self->move_origin[1], r, start); 88: VectorMA (start, self->move_origin[2], u, start); 89: 90: damage = 100 + random() * 50; 91: speed = 550 + 50 * skill->value; 92: fire_rocket (self->teammaster->owner, start, f, damage, speed, 150, damage); 93: gi.positioned_sound (start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0); 94: } 95: 96: void turret_breach_think (edict_t *self) 97: { 98: edict_t *ent; 99: vec3_t current_angles; 100: vec3_t delta; 101: 102: VectorCopy (self->s.angles, current_angles); 103: AnglesNormalize(current_angles); 104: 105: AnglesNormalize(self->move_angles); 106: if (self->move_angles[PITCH] > 180) 107: self->move_angles[PITCH] -= 360; 108: 109: // clamp angles to mins & maxs 110: if (self->move_angles[PITCH] > self->pos1[PITCH]) 111: self->move_angles[PITCH] = self->pos1[PITCH]; 112: else if (self->move_angles[PITCH] < self->pos2[PITCH]) 113: self->move_angles[PITCH] = self->pos2[PITCH]; 114: 115: if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW])) 116: { 117: float dmin, dmax; 118: 119: dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]); 120: if (dmin < -180) 121: dmin += 360; 122: else if (dmin > 180) 123: dmin -= 360; 124: dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]); 125: if (dmax < -180) 126: dmax += 360; 127: else if (dmax > 180) 128: dmax -= 360; 129: if (fabs(dmin) < fabs(dmax)) 130: self->move_angles[YAW] = self->pos1[YAW]; 131: else 132: self->move_angles[YAW] = self->pos2[YAW]; 133: } 134: 135: VectorSubtract (self->move_angles, current_angles, delta); 136: if (delta[0] < -180) 137: delta[0] += 360; 138: else if (delta[0] > 180) 139: delta[0] -= 360; 140: if (delta[1] < -180) 141: delta[1] += 360; 142: else if (delta[1] > 180) 143: delta[1] -= 360; 144: delta[2] = 0; 145: 146: if (delta[0] > self->speed * FRAMETIME) 147: delta[0] = self->speed * FRAMETIME; 148: if (delta[0] < -1 * self->speed * FRAMETIME) 149: delta[0] = -1 * self->speed * FRAMETIME; 150: if (delta[1] > self->speed * FRAMETIME) 151: delta[1] = self->speed * FRAMETIME; 152: if (delta[1] < -1 * self->speed * FRAMETIME) 153: delta[1] = -1 * self->speed * FRAMETIME; 154: 155: VectorScale (delta, 1.0/FRAMETIME, self->avelocity); 156: 157: self->nextthink = level.time + FRAMETIME; 158: 159: for (ent = self->teammaster; ent; ent = ent->teamchain) 160: ent->avelocity[1] = self->avelocity[1]; 161: 162: // if we have adriver, adjust his velocities 163: if (self->owner) 164: { 165: float angle; 166: float target_z; 167: float diff; 168: vec3_t target; 169: vec3_t dir; 170: 171: // angular is easy, just copy ours 172: self->owner->avelocity[0] = self->avelocity[0]; 173: self->owner->avelocity[1] = self->avelocity[1]; 174: 175: // x & y 176: angle = self->s.angles[1] + self->owner->move_origin[1]; 177: angle *= (M_PI*2 / 360); 178: target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]); 179: target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]); 180: target[2] = self->owner->s.origin[2]; 181: 182: VectorSubtract (target, self->owner->s.origin, dir); 183: self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME; 184: self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME; 185: 186: // z 187: angle = self->s.angles[PITCH] * (M_PI*2 / 360); 188: target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]); 189: 190: diff = target_z - self->owner->s.origin[2]; 191: self->owner->velocity[2] = diff * 1.0 / FRAMETIME; 192: 193: if (self->spawnflags & 65536) 194: { 195: turret_breach_fire (self); 196: self->spawnflags &= ~65536; 197: } 198: } 199: } 200: 201: void turret_breach_finish_init (edict_t *self) 202: { 203: // get and save info for muzzle location 204: if (!self->target) 205: { 206: gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin)); 207: } 208: else 209: { 210: self->target_ent = G_PickTarget (self->target); 211: VectorSubtract (self->target_ent->s.origin, self->s.origin, self->move_origin); 212: G_FreeEdict(self->target_ent); 213: } 214: 215: self->teammaster->dmg = self->dmg; 216: self->think = turret_breach_think; 217: self->think (self); 218: } 219: 220: void SP_turret_breach (edict_t *self) 221: { 222: self->solid = SOLID_BSP; 223: self->movetype = MOVETYPE_PUSH; 224: gi.setmodel (self, self->model); 225: 226: if (!self->speed) 227: self->speed = 50; 228: if (!self->dmg) 229: self->dmg = 10; 230: 231: if (!st.minpitch) 232: st.minpitch = -30; 233: if (!st.maxpitch) 234: st.maxpitch = 30; 235: if (!st.maxyaw) 236: st.maxyaw = 360; 237: 238: self->pos1[PITCH] = -1 * st.minpitch; 239: self->pos1[YAW] = st.minyaw; 240: self->pos2[PITCH] = -1 * st.maxpitch; 241: self->pos2[YAW] = st.maxyaw; 242: 243: self->ideal_yaw = self->s.angles[YAW]; 244: self->move_angles[YAW] = self->ideal_yaw; 245: 246: self->blocked = turret_blocked; 247: 248: self->think = turret_breach_finish_init; 249: self->nextthink = level.time + FRAMETIME; 250: gi.linkentity (self); 251: } 252: 253: 254: /*QUAKED turret_base (0 0 0) ? 255: This portion of the turret changes yaw only. 256: MUST be teamed with a turret_breach. 257: */ 258: 259: void SP_turret_base (edict_t *self) 260: { 261: self->solid = SOLID_BSP; 262: self->movetype = MOVETYPE_PUSH; 263: gi.setmodel (self, self->model); 264: self->blocked = turret_blocked; 265: gi.linkentity (self); 266: } 267: 268: 269: /*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32) 270: Must NOT be on the team with the rest of the turret parts. 271: Instead it must target the turret_breach. 272: */ 273: 274: void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage); 275: void infantry_stand (edict_t *self); 276: void monster_use (edict_t *self, edict_t *other, edict_t *activator); 277: 278: void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) 279: { 280: edict_t *ent; 281: 282: // level the gun 283: self->target_ent->move_angles[0] = 0; 284: 285: // remove the driver from the end of them team chain 286: for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain) 287: ; 288: ent->teamchain = NULL; 289: self->teammaster = NULL; 290: self->flags &= ~FL_TEAMSLAVE; 291: 292: self->target_ent->owner = NULL; 293: self->target_ent->teammaster->owner = NULL; 294: 295: infantry_die (self, inflictor, attacker, damage); 296: } 297: 298: qboolean FindTarget (edict_t *self); 299: 300: void turret_driver_think (edict_t *self) 301: { 302: vec3_t target; 303: vec3_t dir; 304: float reaction_time; 305: 306: self->nextthink = level.time + FRAMETIME; 307: 308: if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0)) 309: self->enemy = NULL; 310: 311: if (!self->enemy) 312: { 313: if (!FindTarget (self)) 314: return; 315: self->monsterinfo.trail_time = level.time; 316: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; 317: } 318: else 319: { 320: if (visible (self, self->enemy)) 321: { 322: if (self->monsterinfo.aiflags & AI_LOST_SIGHT) 323: { 324: self->monsterinfo.trail_time = level.time; 325: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; 326: } 327: } 328: else 329: { 330: self->monsterinfo.aiflags |= AI_LOST_SIGHT; 331: return; 332: } 333: } 334: 335: // let the turret know where we want it to aim 336: VectorCopy (self->enemy->s.origin, target); 337: target[2] += self->enemy->viewheight; 338: VectorSubtract (target, self->target_ent->s.origin, dir); 339: vectoangles (dir, self->target_ent->move_angles); 340: 341: // decide if we should shoot 342: if (level.time < self->monsterinfo.attack_finished) 343: return; 344: 345: reaction_time = (3 - skill->value) * 1.0; 346: if ((level.time - self->monsterinfo.trail_time) < reaction_time) 347: return; 348: 349: self->monsterinfo.attack_finished = level.time + reaction_time + 1.0; 350: //FIXME how do we really want to pass this along? 351: self->target_ent->spawnflags |= 65536; 352: } 353: 354: void turret_driver_link (edict_t *self) 355: { 356: vec3_t vec; 357: edict_t *ent; 358: 359: self->think = turret_driver_think; 360: self->nextthink = level.time + FRAMETIME; 361: 362: self->target_ent = G_PickTarget (self->target); 363: self->target_ent->owner = self; 364: self->target_ent->teammaster->owner = self; 365: VectorCopy (self->target_ent->s.angles, self->s.angles); 366: 367: vec[0] = self->target_ent->s.origin[0] - self->s.origin[0]; 368: vec[1] = self->target_ent->s.origin[1] - self->s.origin[1]; 369: vec[2] = 0; 370: self->move_origin[0] = VectorLength(vec); 371: 372: VectorSubtract (self->s.origin, self->target_ent->s.origin, vec); 373: vectoangles (vec, vec); 374: AnglesNormalize(vec); 375: self->move_origin[1] = vec[1]; 376: 377: self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2]; 378: 379: // add the driver to the end of them team chain 380: for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain) 381: ; 382: ent->teamchain = self; 383: self->teammaster = self->target_ent->teammaster; 384: self->flags |= FL_TEAMSLAVE; 385: } 386: 387: void SP_turret_driver (edict_t *self) 388: { 389: if (deathmatch->value) 390: { 391: G_FreeEdict (self); 392: return; 393: } 394: 395: self->movetype = MOVETYPE_PUSH; 396: self->solid = SOLID_BBOX; 397: self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2"); 398: VectorSet (self->mins, -16, -16, -24); 399: VectorSet (self->maxs, 16, 16, 32); 400: 401: self->health = 100; 402: self->gib_health = 0; 403: self->mass = 200; 404: self->viewheight = 24; 405: 406: self->die = turret_driver_die; 407: self->monsterinfo.stand = infantry_stand; 408: 409: self->flags |= FL_NO_KNOCKBACK; 410: 411: level.total_monsters++; 412: 413: self->svflags |= SVF_MONSTER; 414: self->s.renderfx |= RF_FRAMELERP; 415: self->takedamage = DAMAGE_AIM; 416: self->use = monster_use; 417: self->clipmask = MASK_MONSTERSOLID; 418: VectorCopy (self->s.origin, self->s.old_origin); 419: self->monsterinfo.aiflags |= AI_STAND_GROUND|AI_DUCKED; 420: 421: if (st.item) 422: { 423: self->item = FindItemByClassname (st.item); 424: if (!self->item) 425: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item); 426: } 427: 428: self->think = turret_driver_link; 429: self->nextthink = level.time + FRAMETIME; 430: 431: gi.linkentity (self); 432: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.