|
|
1.1 ! root 1: // g_turret.c ! 2: ! 3: #include "g_local.h" ! 4: ! 5: void SpawnTargetingSystem (edict_t *turret); // PGM ! 6: ! 7: void AnglesNormalize(vec3_t vec) ! 8: { ! 9: while(vec[0] > 360) ! 10: vec[0] -= 360; ! 11: while(vec[0] < 0) ! 12: vec[0] += 360; ! 13: while(vec[1] > 360) ! 14: vec[1] -= 360; ! 15: while(vec[1] < 0) ! 16: vec[1] += 360; ! 17: } ! 18: ! 19: float SnapToEights(float x) ! 20: { ! 21: x *= 8.0; ! 22: if (x > 0.0) ! 23: x += 0.5; ! 24: else ! 25: x -= 0.5; ! 26: return 0.125 * (int)x; ! 27: } ! 28: ! 29: ! 30: void turret_blocked(edict_t *self, edict_t *other) ! 31: { ! 32: edict_t *attacker; ! 33: ! 34: if (other->takedamage) ! 35: { ! 36: if (self->teammaster->owner) ! 37: attacker = self->teammaster->owner; ! 38: else ! 39: attacker = self->teammaster; ! 40: T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH); ! 41: } ! 42: } ! 43: ! 44: /*QUAKED turret_breach (0 0 0) ? ! 45: This portion of the turret can change both pitch and yaw. ! 46: The model should be made with a flat pitch. ! 47: It (and the associated base) need to be oriented towards 0. ! 48: Use "angle" to set the starting angle. ! 49: ! 50: "speed" default 50 ! 51: "dmg" default 10 ! 52: "angle" point this forward ! 53: "target" point this at an info_notnull at the muzzle tip ! 54: "minpitch" min acceptable pitch angle : default -30 ! 55: "maxpitch" max acceptable pitch angle : default 30 ! 56: "minyaw" min acceptable yaw angle : default 0 ! 57: "maxyaw" max acceptable yaw angle : default 360 ! 58: */ ! 59: ! 60: void turret_breach_fire (edict_t *self) ! 61: { ! 62: vec3_t f, r, u; ! 63: vec3_t start; ! 64: int damage; ! 65: int speed; ! 66: ! 67: AngleVectors (self->s.angles, f, r, u); ! 68: VectorMA (self->s.origin, self->move_origin[0], f, start); ! 69: VectorMA (start, self->move_origin[1], r, start); ! 70: VectorMA (start, self->move_origin[2], u, start); ! 71: ! 72: damage = 100 + random() * 50; ! 73: speed = 550 + 50 * skill->value; ! 74: fire_rocket (self->teammaster->owner, start, f, damage, speed, 150, damage); ! 75: gi.positioned_sound (start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0); ! 76: } ! 77: ! 78: void turret_breach_think (edict_t *self) ! 79: { ! 80: edict_t *ent; ! 81: vec3_t current_angles; ! 82: vec3_t delta; ! 83: ! 84: VectorCopy (self->s.angles, current_angles); ! 85: AnglesNormalize(current_angles); ! 86: ! 87: AnglesNormalize(self->move_angles); ! 88: if (self->move_angles[PITCH] > 180) ! 89: self->move_angles[PITCH] -= 360; ! 90: ! 91: // clamp angles to mins & maxs ! 92: if (self->move_angles[PITCH] > self->pos1[PITCH]) ! 93: self->move_angles[PITCH] = self->pos1[PITCH]; ! 94: else if (self->move_angles[PITCH] < self->pos2[PITCH]) ! 95: self->move_angles[PITCH] = self->pos2[PITCH]; ! 96: ! 97: if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW])) ! 98: { ! 99: float dmin, dmax; ! 100: ! 101: dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]); ! 102: if (dmin < -180) ! 103: dmin += 360; ! 104: else if (dmin > 180) ! 105: dmin -= 360; ! 106: dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]); ! 107: if (dmax < -180) ! 108: dmax += 360; ! 109: else if (dmax > 180) ! 110: dmax -= 360; ! 111: if (fabs(dmin) < fabs(dmax)) ! 112: self->move_angles[YAW] = self->pos1[YAW]; ! 113: else ! 114: self->move_angles[YAW] = self->pos2[YAW]; ! 115: } ! 116: ! 117: VectorSubtract (self->move_angles, current_angles, delta); ! 118: if (delta[0] < -180) ! 119: delta[0] += 360; ! 120: else if (delta[0] > 180) ! 121: delta[0] -= 360; ! 122: if (delta[1] < -180) ! 123: delta[1] += 360; ! 124: else if (delta[1] > 180) ! 125: delta[1] -= 360; ! 126: delta[2] = 0; ! 127: ! 128: if (delta[0] > self->speed * FRAMETIME) ! 129: delta[0] = self->speed * FRAMETIME; ! 130: if (delta[0] < -1 * self->speed * FRAMETIME) ! 131: delta[0] = -1 * self->speed * FRAMETIME; ! 132: if (delta[1] > self->speed * FRAMETIME) ! 133: delta[1] = self->speed * FRAMETIME; ! 134: if (delta[1] < -1 * self->speed * FRAMETIME) ! 135: delta[1] = -1 * self->speed * FRAMETIME; ! 136: ! 137: VectorScale (delta, 1.0/FRAMETIME, self->avelocity); ! 138: ! 139: self->nextthink = level.time + FRAMETIME; ! 140: ! 141: for (ent = self->teammaster; ent; ent = ent->teamchain) ! 142: ent->avelocity[1] = self->avelocity[1]; ! 143: ! 144: // if we have adriver, adjust his velocities ! 145: if (self->owner) ! 146: { ! 147: float angle; ! 148: float target_z; ! 149: float diff; ! 150: vec3_t target; ! 151: vec3_t dir; ! 152: ! 153: // angular is easy, just copy ours ! 154: self->owner->avelocity[0] = self->avelocity[0]; ! 155: self->owner->avelocity[1] = self->avelocity[1]; ! 156: ! 157: // x & y ! 158: angle = self->s.angles[1] + self->owner->move_origin[1]; ! 159: angle *= (M_PI*2 / 360); ! 160: target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]); ! 161: target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]); ! 162: target[2] = self->owner->s.origin[2]; ! 163: ! 164: VectorSubtract (target, self->owner->s.origin, dir); ! 165: self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME; ! 166: self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME; ! 167: ! 168: // z ! 169: angle = self->s.angles[PITCH] * (M_PI*2 / 360); ! 170: target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]); ! 171: ! 172: diff = target_z - self->owner->s.origin[2]; ! 173: self->owner->velocity[2] = diff * 1.0 / FRAMETIME; ! 174: ! 175: if (self->spawnflags & 65536) ! 176: { ! 177: turret_breach_fire (self); ! 178: self->spawnflags &= ~65536; ! 179: } ! 180: } ! 181: } ! 182: ! 183: void turret_breach_finish_init (edict_t *self) ! 184: { ! 185: // get and save info for muzzle location ! 186: if (!self->target) ! 187: { ! 188: gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin)); ! 189: } ! 190: else ! 191: { ! 192: self->target_ent = G_PickTarget (self->target); ! 193: if(self->target_ent) ! 194: { ! 195: VectorSubtract (self->target_ent->s.origin, self->s.origin, self->move_origin); ! 196: G_FreeEdict(self->target_ent); ! 197: } ! 198: else ! 199: gi.dprintf("could not find target entity for %s at %s\n", self->classname, vtos(self->s.origin)); ! 200: } ! 201: ! 202: self->teammaster->dmg = self->dmg; ! 203: self->think = turret_breach_think; ! 204: self->think (self); ! 205: } ! 206: ! 207: void SP_turret_breach (edict_t *self) ! 208: { ! 209: self->solid = SOLID_BSP; ! 210: self->movetype = MOVETYPE_PUSH; ! 211: gi.setmodel (self, self->model); ! 212: ! 213: if (!self->speed) ! 214: self->speed = 50; ! 215: if (!self->dmg) ! 216: self->dmg = 10; ! 217: ! 218: if (!st.minpitch) ! 219: st.minpitch = -30; ! 220: if (!st.maxpitch) ! 221: st.maxpitch = 30; ! 222: if (!st.maxyaw) ! 223: st.maxyaw = 360; ! 224: ! 225: self->pos1[PITCH] = -1 * st.minpitch; ! 226: self->pos1[YAW] = st.minyaw; ! 227: self->pos2[PITCH] = -1 * st.maxpitch; ! 228: self->pos2[YAW] = st.maxyaw; ! 229: ! 230: self->ideal_yaw = self->s.angles[YAW]; ! 231: self->move_angles[YAW] = self->ideal_yaw; ! 232: ! 233: self->blocked = turret_blocked; ! 234: ! 235: self->think = turret_breach_finish_init; ! 236: self->nextthink = level.time + FRAMETIME; ! 237: gi.linkentity (self); ! 238: } ! 239: ! 240: ! 241: /*QUAKED turret_base (0 0 0) ? ! 242: This portion of the turret changes yaw only. ! 243: MUST be teamed with a turret_breach. ! 244: */ ! 245: ! 246: void SP_turret_base (edict_t *self) ! 247: { ! 248: self->solid = SOLID_BSP; ! 249: self->movetype = MOVETYPE_PUSH; ! 250: gi.setmodel (self, self->model); ! 251: self->blocked = turret_blocked; ! 252: gi.linkentity (self); ! 253: } ! 254: ! 255: ! 256: /*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32) ! 257: Must NOT be on the team with the rest of the turret parts. ! 258: Instead it must target the turret_breach. ! 259: */ ! 260: ! 261: void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage); ! 262: void infantry_stand (edict_t *self); ! 263: void monster_use (edict_t *self, edict_t *other, edict_t *activator); ! 264: ! 265: void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 266: { ! 267: edict_t *ent; ! 268: ! 269: // level the gun ! 270: self->target_ent->move_angles[0] = 0; ! 271: ! 272: // remove the driver from the end of them team chain ! 273: for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain) ! 274: ; ! 275: ent->teamchain = NULL; ! 276: self->teammaster = NULL; ! 277: self->flags &= ~FL_TEAMSLAVE; ! 278: ! 279: self->target_ent->owner = NULL; ! 280: self->target_ent->teammaster->owner = NULL; ! 281: ! 282: infantry_die (self, inflictor, attacker, damage); ! 283: } ! 284: ! 285: qboolean FindTarget (edict_t *self); ! 286: ! 287: void turret_driver_think (edict_t *self) ! 288: { ! 289: vec3_t target; ! 290: vec3_t dir; ! 291: float reaction_time; ! 292: ! 293: self->nextthink = level.time + FRAMETIME; ! 294: ! 295: if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0)) ! 296: self->enemy = NULL; ! 297: ! 298: if (!self->enemy) ! 299: { ! 300: if (!FindTarget (self)) ! 301: return; ! 302: self->monsterinfo.trail_time = level.time; ! 303: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; ! 304: } ! 305: else ! 306: { ! 307: if (visible (self, self->enemy)) ! 308: { ! 309: if (self->monsterinfo.aiflags & AI_LOST_SIGHT) ! 310: { ! 311: self->monsterinfo.trail_time = level.time; ! 312: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; ! 313: } ! 314: } ! 315: else ! 316: { ! 317: self->monsterinfo.aiflags |= AI_LOST_SIGHT; ! 318: return; ! 319: } ! 320: } ! 321: ! 322: // let the turret know where we want it to aim ! 323: VectorCopy (self->enemy->s.origin, target); ! 324: target[2] += self->enemy->viewheight; ! 325: VectorSubtract (target, self->target_ent->s.origin, dir); ! 326: vectoangles (dir, self->target_ent->move_angles); ! 327: ! 328: // decide if we should shoot ! 329: if (level.time < self->monsterinfo.attack_finished) ! 330: return; ! 331: ! 332: reaction_time = (3 - skill->value) * 1.0; ! 333: if ((level.time - self->monsterinfo.trail_time) < reaction_time) ! 334: return; ! 335: ! 336: self->monsterinfo.attack_finished = level.time + reaction_time + 1.0; ! 337: //FIXME how do we really want to pass this along? ! 338: self->target_ent->spawnflags |= 65536; ! 339: } ! 340: ! 341: void turret_driver_link (edict_t *self) ! 342: { ! 343: vec3_t vec; ! 344: edict_t *ent; ! 345: ! 346: self->think = turret_driver_think; ! 347: self->nextthink = level.time + FRAMETIME; ! 348: ! 349: self->target_ent = G_PickTarget (self->target); ! 350: self->target_ent->owner = self; ! 351: self->target_ent->teammaster->owner = self; ! 352: VectorCopy (self->target_ent->s.angles, self->s.angles); ! 353: ! 354: vec[0] = self->target_ent->s.origin[0] - self->s.origin[0]; ! 355: vec[1] = self->target_ent->s.origin[1] - self->s.origin[1]; ! 356: vec[2] = 0; ! 357: self->move_origin[0] = VectorLength(vec); ! 358: ! 359: VectorSubtract (self->s.origin, self->target_ent->s.origin, vec); ! 360: vectoangles (vec, vec); ! 361: AnglesNormalize(vec); ! 362: self->move_origin[1] = vec[1]; ! 363: ! 364: self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2]; ! 365: ! 366: // add the driver to the end of them team chain ! 367: for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain) ! 368: ; ! 369: ent->teamchain = self; ! 370: self->teammaster = self->target_ent->teammaster; ! 371: self->flags |= FL_TEAMSLAVE; ! 372: } ! 373: ! 374: void SP_turret_driver (edict_t *self) ! 375: { ! 376: if (deathmatch->value) ! 377: { ! 378: G_FreeEdict (self); ! 379: return; ! 380: } ! 381: ! 382: self->movetype = MOVETYPE_PUSH; ! 383: self->solid = SOLID_BBOX; ! 384: self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2"); ! 385: VectorSet (self->mins, -16, -16, -24); ! 386: VectorSet (self->maxs, 16, 16, 32); ! 387: ! 388: self->health = 100; ! 389: self->gib_health = 0; ! 390: self->mass = 200; ! 391: self->viewheight = 24; ! 392: ! 393: self->die = turret_driver_die; ! 394: self->monsterinfo.stand = infantry_stand; ! 395: ! 396: self->flags |= FL_NO_KNOCKBACK; ! 397: ! 398: level.total_monsters++; ! 399: ! 400: self->svflags |= SVF_MONSTER; ! 401: self->s.renderfx |= RF_FRAMELERP; ! 402: self->takedamage = DAMAGE_AIM; ! 403: self->use = monster_use; ! 404: self->clipmask = MASK_MONSTERSOLID; ! 405: VectorCopy (self->s.origin, self->s.old_origin); ! 406: self->monsterinfo.aiflags |= AI_STAND_GROUND|AI_DUCKED; ! 407: ! 408: if (st.item) ! 409: { ! 410: self->item = FindItemByClassname (st.item); ! 411: if (!self->item) ! 412: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item); ! 413: } ! 414: ! 415: self->think = turret_driver_link; ! 416: self->nextthink = level.time + FRAMETIME; ! 417: ! 418: gi.linkentity (self); ! 419: } ! 420: ! 421: //============ ! 422: // ROGUE ! 423: ! 424: // invisible turret drivers so we can have unmanned turrets. ! 425: // originally designed to shoot at func_trains and such, so they ! 426: // fire at the center of the bounding box, rather than the entity's ! 427: // origin. ! 428: ! 429: void turret_brain_think (edict_t *self) ! 430: { ! 431: vec3_t target; ! 432: vec3_t dir; ! 433: vec3_t endpos; ! 434: float reaction_time; ! 435: trace_t trace; ! 436: ! 437: self->nextthink = level.time + FRAMETIME; ! 438: ! 439: if (self->enemy) ! 440: { ! 441: if(!self->enemy->inuse) ! 442: self->enemy = NULL; ! 443: else if(self->enemy->takedamage && self->enemy->health <= 0) ! 444: self->enemy = NULL; ! 445: } ! 446: ! 447: if (!self->enemy) ! 448: { ! 449: if (!FindTarget (self)) ! 450: return; ! 451: self->monsterinfo.trail_time = level.time; ! 452: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; ! 453: } ! 454: else ! 455: { ! 456: VectorAdd (self->enemy->absmax, self->enemy->absmin, endpos); ! 457: VectorScale (endpos, 0.5, endpos); ! 458: ! 459: trace = gi.trace (self->target_ent->s.origin, vec3_origin, vec3_origin, endpos, self->target_ent, MASK_SHOT); ! 460: if(trace.fraction == 1 || trace.ent == self->enemy) ! 461: { ! 462: if (self->monsterinfo.aiflags & AI_LOST_SIGHT) ! 463: { ! 464: self->monsterinfo.trail_time = level.time; ! 465: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; ! 466: } ! 467: } ! 468: else ! 469: { ! 470: self->monsterinfo.aiflags |= AI_LOST_SIGHT; ! 471: return; ! 472: } ! 473: } ! 474: ! 475: // let the turret know where we want it to aim ! 476: VectorCopy (endpos, target); ! 477: VectorSubtract (target, self->target_ent->s.origin, dir); ! 478: vectoangles (dir, self->target_ent->move_angles); ! 479: ! 480: // decide if we should shoot ! 481: if (level.time < self->monsterinfo.attack_finished) ! 482: return; ! 483: ! 484: if(self->delay) ! 485: reaction_time = self->delay; ! 486: else ! 487: reaction_time = (3 - skill->value) * 1.0; ! 488: if ((level.time - self->monsterinfo.trail_time) < reaction_time) ! 489: return; ! 490: ! 491: self->monsterinfo.attack_finished = level.time + reaction_time + 1.0; ! 492: //FIXME how do we really want to pass this along? ! 493: self->target_ent->spawnflags |= 65536; ! 494: } ! 495: ! 496: // ================= ! 497: // ================= ! 498: void turret_brain_link (edict_t *self) ! 499: { ! 500: vec3_t vec; ! 501: edict_t *ent; ! 502: ! 503: if (self->killtarget) ! 504: { ! 505: self->enemy = G_PickTarget (self->killtarget); ! 506: } ! 507: ! 508: self->think = turret_brain_think; ! 509: self->nextthink = level.time + FRAMETIME; ! 510: ! 511: self->target_ent = G_PickTarget (self->target); ! 512: self->target_ent->owner = self; ! 513: self->target_ent->teammaster->owner = self; ! 514: VectorCopy (self->target_ent->s.angles, self->s.angles); ! 515: ! 516: vec[0] = self->target_ent->s.origin[0] - self->s.origin[0]; ! 517: vec[1] = self->target_ent->s.origin[1] - self->s.origin[1]; ! 518: vec[2] = 0; ! 519: self->move_origin[0] = VectorLength(vec); ! 520: ! 521: VectorSubtract (self->s.origin, self->target_ent->s.origin, vec); ! 522: vectoangles (vec, vec); ! 523: AnglesNormalize(vec); ! 524: self->move_origin[1] = vec[1]; ! 525: ! 526: self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2]; ! 527: ! 528: // add the driver to the end of them team chain ! 529: for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain) ! 530: ; ! 531: ent->teamchain = self; ! 532: self->teammaster = self->target_ent->teammaster; ! 533: self->flags |= FL_TEAMSLAVE; ! 534: } ! 535: ! 536: // ================= ! 537: // ================= ! 538: void turret_brain_deactivate (edict_t *self, edict_t *other, edict_t *activator) ! 539: { ! 540: self->think = NULL; ! 541: self->nextthink = 0; ! 542: } ! 543: ! 544: // ================= ! 545: // ================= ! 546: void turret_brain_activate (edict_t *self, edict_t *other, edict_t *activator) ! 547: { ! 548: if (!self->enemy) ! 549: { ! 550: self->enemy = activator; ! 551: } ! 552: ! 553: // wait at least 3 seconds to fire. ! 554: self->monsterinfo.attack_finished = level.time + 3; ! 555: self->use = turret_brain_deactivate; ! 556: ! 557: self->think = turret_brain_link; ! 558: self->nextthink = level.time + FRAMETIME; ! 559: } ! 560: ! 561: /*QUAKED turret_invisible_brain (1 .5 0) (-16 -16 -16) (16 16 16) ! 562: Invisible brain to drive the turret. ! 563: ! 564: Does not search for targets. If targeted, can only be turned on once ! 565: and then off once. After that they are completely disabled. ! 566: ! 567: "delay" the delay between firing (default ramps for skill level) ! 568: "Target" the turret breach ! 569: "Killtarget" the item you want it to attack. ! 570: Target the brain if you want it activated later, instead of immediately. It will wait 3 seconds ! 571: before firing to acquire the target. ! 572: */ ! 573: void SP_turret_invisible_brain (edict_t *self) ! 574: { ! 575: if (!self->killtarget) ! 576: { ! 577: gi.dprintf("turret_invisible_brain with no killtarget!\n"); ! 578: G_FreeEdict (self); ! 579: return; ! 580: } ! 581: if (!self->target) ! 582: { ! 583: gi.dprintf("turret_invisible_brain with no target!\n"); ! 584: G_FreeEdict (self); ! 585: return; ! 586: } ! 587: ! 588: if (self->targetname) ! 589: { ! 590: self->use = turret_brain_activate; ! 591: } ! 592: else ! 593: { ! 594: self->think = turret_brain_link; ! 595: self->nextthink = level.time + FRAMETIME; ! 596: } ! 597: ! 598: self->movetype = MOVETYPE_PUSH; ! 599: gi.linkentity (self); ! 600: } ! 601: ! 602: // ROGUE ! 603: //============
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.