|
|
1.1 ! root 1: #include "g_local.h" ! 2: ! 3: #define INCLUDE_ETF_RIFLE 1 ! 4: #define INCLUDE_PROX 1 ! 5: //#define INCLUDE_FLAMETHROWER 1 ! 6: //#define INCLUDE_INCENDIARY 1 ! 7: #define INCLUDE_NUKE 1 ! 8: #define INCLUDE_MELEE 1 ! 9: #define INCLUDE_TESLA 1 ! 10: #define INCLUDE_BEAMS 1 ! 11: ! 12: extern void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed); ! 13: extern void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf); ! 14: extern void droptofloor (edict_t *ent); ! 15: extern void Grenade_Explode (edict_t *ent); ! 16: ! 17: extern void drawbbox (edict_t *ent); ! 18: ! 19: #ifdef INCLUDE_ETF_RIFLE ! 20: /* ! 21: ======================== ! 22: fire_flechette ! 23: ======================== ! 24: */ ! 25: void flechette_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 26: { ! 27: vec3_t dir; ! 28: ! 29: if (other == self->owner) ! 30: return; ! 31: ! 32: if (surf && (surf->flags & SURF_SKY)) ! 33: { ! 34: G_FreeEdict (self); ! 35: return; ! 36: } ! 37: ! 38: if (self->client) ! 39: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); ! 40: ! 41: if (other->takedamage) ! 42: { ! 43: //gi.dprintf("t_damage %s\n", other->classname); ! 44: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, ! 45: self->dmg, self->dmg_radius, DAMAGE_NO_REG_ARMOR, MOD_ETF_RIFLE); ! 46: } ! 47: else ! 48: { ! 49: if(!plane) ! 50: VectorClear (dir); ! 51: else ! 52: VectorScale (plane->normal, 256, dir); ! 53: gi.WriteByte (svc_temp_entity); ! 54: gi.WriteByte (TE_FLECHETTE); ! 55: gi.WritePosition (self->s.origin); ! 56: gi.WriteDir (dir); ! 57: gi.multicast (self->s.origin, MULTICAST_PVS); ! 58: ! 59: // T_RadiusDamage(self, self->owner, 24, self, 48, MOD_ETF_RIFLE); ! 60: } ! 61: ! 62: G_FreeEdict (self); ! 63: } ! 64: ! 65: void fire_flechette (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int kick) ! 66: { ! 67: edict_t *flechette; ! 68: ! 69: VectorNormalize (dir); ! 70: ! 71: flechette = G_Spawn(); ! 72: VectorCopy (start, flechette->s.origin); ! 73: VectorCopy (start, flechette->s.old_origin); ! 74: vectoangles2 (dir, flechette->s.angles); ! 75: ! 76: VectorScale (dir, speed, flechette->velocity); ! 77: flechette->movetype = MOVETYPE_FLYMISSILE; ! 78: flechette->clipmask = MASK_SHOT; ! 79: flechette->solid = SOLID_BBOX; ! 80: flechette->s.renderfx = RF_FULLBRIGHT; ! 81: VectorClear (flechette->mins); ! 82: VectorClear (flechette->maxs); ! 83: ! 84: flechette->s.modelindex = gi.modelindex ("models/proj/flechette/tris.md2"); ! 85: ! 86: // flechette->s.sound = gi.soundindex (""); // FIXME - correct sound! ! 87: flechette->owner = self; ! 88: flechette->touch = flechette_touch; ! 89: flechette->nextthink = level.time + 8000/speed; ! 90: flechette->think = G_FreeEdict; ! 91: flechette->dmg = damage; ! 92: flechette->dmg_radius = kick; ! 93: ! 94: gi.linkentity (flechette); ! 95: ! 96: if (self->client) ! 97: check_dodge (self, flechette->s.origin, dir, speed); ! 98: } ! 99: #endif ! 100: ! 101: // ************************** ! 102: // PROX ! 103: // ************************** ! 104: ! 105: #ifdef INCLUDE_PROX ! 106: #define PROX_TIME_TO_LIVE 45 // 45, 30, 15, 10 ! 107: #define PROX_TIME_DELAY 0.5 ! 108: #define PROX_BOUND_SIZE 96 ! 109: #define PROX_DAMAGE_RADIUS 192 ! 110: #define PROX_HEALTH 20 ! 111: #define PROX_DAMAGE 90 ! 112: ! 113: //=============== ! 114: //=============== ! 115: void Prox_Explode (edict_t *ent) ! 116: { ! 117: vec3_t origin; ! 118: edict_t *owner; ! 119: ! 120: // free the trigger field ! 121: ! 122: //PMM - changed teammaster to "mover" .. owner of the field is the prox ! 123: if(ent->teamchain && ent->teamchain->owner == ent) ! 124: G_FreeEdict(ent->teamchain); ! 125: ! 126: owner = ent; ! 127: if(ent->teammaster) ! 128: { ! 129: owner = ent->teammaster; ! 130: PlayerNoise(owner, ent->s.origin, PNOISE_IMPACT); ! 131: } ! 132: ! 133: // play quad sound if appopriate ! 134: if (ent->dmg > PROX_DAMAGE) ! 135: gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); ! 136: ! 137: ent->takedamage = DAMAGE_NO; ! 138: T_RadiusDamage(ent, owner, ent->dmg, ent, PROX_DAMAGE_RADIUS, MOD_PROX); ! 139: ! 140: VectorMA (ent->s.origin, -0.02, ent->velocity, origin); ! 141: gi.WriteByte (svc_temp_entity); ! 142: if (ent->groundentity) ! 143: gi.WriteByte (TE_GRENADE_EXPLOSION); ! 144: else ! 145: gi.WriteByte (TE_ROCKET_EXPLOSION); ! 146: gi.WritePosition (origin); ! 147: gi.multicast (ent->s.origin, MULTICAST_PVS); ! 148: ! 149: G_FreeEdict (ent); ! 150: } ! 151: ! 152: //=============== ! 153: //=============== ! 154: void prox_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 155: { ! 156: // gi.dprintf("prox_die\n"); ! 157: // if set off by another prox, delay a little (chained explosions) ! 158: if (strcmp(inflictor->classname, "prox")) ! 159: { ! 160: self->takedamage = DAMAGE_NO; ! 161: Prox_Explode(self); ! 162: } ! 163: else ! 164: { ! 165: self->takedamage = DAMAGE_NO; ! 166: self->think = Prox_Explode; ! 167: self->nextthink = level.time + FRAMETIME; ! 168: } ! 169: } ! 170: ! 171: //=============== ! 172: //=============== ! 173: void Prox_Field_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) ! 174: { ! 175: edict_t *prox; ! 176: ! 177: if (!(other->svflags & SVF_MONSTER) && !other->client) ! 178: return; ! 179: ! 180: // trigger the prox mine if it's still there, and still mine. ! 181: prox = ent->owner; ! 182: ! 183: if (other == prox) // don't set self off ! 184: return; ! 185: ! 186: if (prox->think == Prox_Explode) // we're set to blow! ! 187: { ! 188: // if ((g_showlogic) && (g_showlogic->value)) ! 189: // gi.dprintf ("%f - prox already gone off!\n", level.time); ! 190: return; ! 191: } ! 192: ! 193: if(prox->teamchain == ent) ! 194: { ! 195: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0); ! 196: prox->think = Prox_Explode; ! 197: prox->nextthink = level.time + PROX_TIME_DELAY; ! 198: return; ! 199: } ! 200: ! 201: ent->solid = SOLID_NOT; ! 202: G_FreeEdict(ent); ! 203: } ! 204: ! 205: //=============== ! 206: //=============== ! 207: void prox_seek (edict_t *ent) ! 208: { ! 209: if(level.time > ent->wait) ! 210: { ! 211: Prox_Explode(ent); ! 212: } ! 213: else ! 214: { ! 215: ent->s.frame++; ! 216: if(ent->s.frame > 13) ! 217: ent->s.frame = 9; ! 218: ent->think = prox_seek; ! 219: ent->nextthink = level.time + 0.1; ! 220: } ! 221: } ! 222: ! 223: //=============== ! 224: //=============== ! 225: void prox_open (edict_t *ent) ! 226: { ! 227: edict_t *search; ! 228: ! 229: search = NULL; ! 230: // gi.dprintf("prox_open %d\n", ent->s.frame); ! 231: // gi.dprintf("%f\n", ent->velocity[2]); ! 232: if(ent->s.frame == 9) // end of opening animation ! 233: { ! 234: // set the owner to NULL so the owner can shoot it, etc. needs to be done here so the owner ! 235: // doesn't get stuck on it while it's opening if fired at point blank wall ! 236: ent->owner = NULL; ! 237: if(ent->teamchain) ! 238: ent->teamchain->touch = Prox_Field_Touch; ! 239: while ((search = findradius(search, ent->s.origin, PROX_DAMAGE_RADIUS+10)) != NULL) ! 240: { ! 241: if (!search->classname) // tag token and other weird shit ! 242: continue; ! 243: ! 244: // if (!search->takedamage) ! 245: // continue; ! 246: // if it's a monster or player with health > 0 ! 247: // or it's a player start point ! 248: // and we can see it ! 249: // blow up ! 250: if ( ! 251: ( ! 252: (((search->svflags & SVF_MONSTER) || (search->client)) && (search->health > 0)) || ! 253: ( ! 254: (deathmatch->value) && ! 255: ( ! 256: (!strcmp(search->classname, "info_player_deathmatch")) || ! 257: (!strcmp(search->classname, "info_player_start")) || ! 258: (!strcmp(search->classname, "info_player_coop")) ! 259: ) ! 260: ) ! 261: ) ! 262: && (visible (search, ent)) ! 263: ) ! 264: { ! 265: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/proxwarn.wav"), 1, ATTN_NORM, 0); ! 266: Prox_Explode (ent); ! 267: return; ! 268: } ! 269: } ! 270: ! 271: switch (ent->dmg/PROX_DAMAGE) ! 272: { ! 273: case 1: ! 274: ent->wait = level.time + PROX_TIME_TO_LIVE; ! 275: break; ! 276: case 2: ! 277: ent->wait = level.time + 30; ! 278: break; ! 279: case 4: ! 280: ent->wait = level.time + 15; ! 281: break; ! 282: case 8: ! 283: ent->wait = level.time + 10; ! 284: break; ! 285: default: ! 286: if ((g_showlogic) && (g_showlogic->value)) ! 287: gi.dprintf ("prox with unknown multiplier %d!\n", ent->dmg/PROX_DAMAGE); ! 288: ent->wait = level.time + PROX_TIME_TO_LIVE; ! 289: break; ! 290: } ! 291: ! 292: // ent->wait = level.time + PROX_TIME_TO_LIVE; ! 293: ent->think = prox_seek; ! 294: ent->nextthink = level.time + 0.2; ! 295: } ! 296: else ! 297: { ! 298: ent->s.frame++; ! 299: ent->think = prox_open; ! 300: ent->nextthink = level.time + 0.05; ! 301: } ! 302: } ! 303: ! 304: //=============== ! 305: //=============== ! 306: void prox_land (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) ! 307: { ! 308: edict_t *field; ! 309: vec3_t dir; ! 310: vec3_t forward, right, up; ! 311: int makeslave = 0; ! 312: int movetype = MOVETYPE_NONE; ! 313: int stick_ok = 0; ! 314: vec3_t land_point; ! 315: ! 316: // must turn off owner so owner can shoot it and set it off ! 317: // moved to prox_open so owner can get away from it if fired at pointblank range into ! 318: // wall ! 319: // ent->owner = NULL; ! 320: ! 321: // if ((g_showlogic) && (g_showlogic->value)) ! 322: // gi.dprintf ("land - %2.2f %2.2f %2.2f\n", ent->velocity[0], ent->velocity[1], ent->velocity[2]); ! 323: ! 324: if (surf && (surf->flags & SURF_SKY)) ! 325: { ! 326: G_FreeEdict(ent); ! 327: return; ! 328: } ! 329: ! 330: if (plane->normal) ! 331: { ! 332: VectorMA (ent->s.origin, -10.0, plane->normal, land_point); ! 333: if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA)) ! 334: { ! 335: Prox_Explode (ent); ! 336: return; ! 337: } ! 338: } ! 339: ! 340: if ((other->svflags & SVF_MONSTER) || other->client || (other->svflags & SVF_DAMAGEABLE)) ! 341: { ! 342: if(other != ent->teammaster) ! 343: Prox_Explode(ent); ! 344: ! 345: return; ! 346: } ! 347: ! 348: #define STOP_EPSILON 0.1 ! 349: ! 350: else if (other != world) ! 351: { ! 352: //Here we need to check to see if we can stop on this entity. ! 353: //Note that plane can be NULL ! 354: ! 355: //PMM - code stolen from g_phys (ClipVelocity) ! 356: vec3_t out; ! 357: float backoff, change; ! 358: int i; ! 359: ! 360: if (!plane->normal) // this happens if you hit a point object, maybe other cases ! 361: { ! 362: // Since we can't tell what's going to happen, just blow up ! 363: if ((g_showlogic) && (g_showlogic->value)) ! 364: gi.dprintf ("bad normal for surface, exploding!\n"); ! 365: ! 366: Prox_Explode(ent); ! 367: return; ! 368: } ! 369: ! 370: if ((other->movetype == MOVETYPE_PUSH) && (plane->normal[2] > 0.7)) ! 371: stick_ok = 1; ! 372: else ! 373: stick_ok = 0; ! 374: ! 375: backoff = DotProduct (ent->velocity, plane->normal) * 1.5; ! 376: for (i=0 ; i<3 ; i++) ! 377: { ! 378: change = plane->normal[i]*backoff; ! 379: out[i] = ent->velocity[i] - change; ! 380: if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) ! 381: out[i] = 0; ! 382: } ! 383: ! 384: if (out[2] > 60) ! 385: return; ! 386: ! 387: movetype = MOVETYPE_BOUNCE; ! 388: ! 389: // if we're here, we're going to stop on an entity ! 390: if (stick_ok) ! 391: { // it's a happy entity ! 392: VectorCopy (vec3_origin, ent->velocity); ! 393: VectorCopy (vec3_origin, ent->avelocity); ! 394: } ! 395: else // no-stick. teflon time ! 396: { ! 397: if (plane->normal[2] > 0.7) ! 398: { ! 399: if ((g_showlogic) && (g_showlogic->value)) ! 400: gi.dprintf ("stuck on entity, blowing up!\n"); ! 401: ! 402: Prox_Explode(ent); ! 403: return; ! 404: } ! 405: return; ! 406: } ! 407: } ! 408: else if (other->s.modelindex != 1) ! 409: return; ! 410: ! 411: vectoangles2 (plane->normal, dir); ! 412: AngleVectors (dir, forward, right, up); ! 413: ! 414: if (gi.pointcontents (ent->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME)) ! 415: { ! 416: Prox_Explode (ent); ! 417: return; ! 418: } ! 419: ! 420: field = G_Spawn(); ! 421: ! 422: VectorCopy (ent->s.origin, field->s.origin); ! 423: VectorClear(field->velocity); ! 424: VectorClear(field->avelocity); ! 425: VectorSet(field->mins, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE, -PROX_BOUND_SIZE); ! 426: VectorSet(field->maxs, PROX_BOUND_SIZE, PROX_BOUND_SIZE, PROX_BOUND_SIZE); ! 427: field->movetype = MOVETYPE_NONE; ! 428: field->solid = SOLID_TRIGGER; ! 429: field->owner = ent; ! 430: field->classname = "prox_field"; ! 431: field->teammaster = ent; ! 432: gi.linkentity (field); ! 433: ! 434: VectorClear(ent->velocity); ! 435: VectorClear(ent->avelocity); ! 436: // rotate to vertical ! 437: dir[PITCH] = dir[PITCH] + 90; ! 438: VectorCopy (dir, ent->s.angles); ! 439: ent->takedamage = DAMAGE_AIM; ! 440: ent->movetype = movetype; // either bounce or none, depending on whether we stuck to something ! 441: ent->die = prox_die; ! 442: ent->teamchain = field; ! 443: ent->health = PROX_HEALTH; ! 444: ent->nextthink = level.time + 0.05; ! 445: ent->think = prox_open; ! 446: ent->touch = NULL; ! 447: ent->solid = SOLID_BBOX; ! 448: // record who we're attached to ! 449: // ent->teammaster = other; ! 450: ! 451: gi.linkentity(ent); ! 452: } ! 453: ! 454: //=============== ! 455: //=============== ! 456: void fire_prox (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed) ! 457: { ! 458: edict_t *prox; ! 459: vec3_t dir; ! 460: vec3_t forward, right, up; ! 461: ! 462: vectoangles2 (aimdir, dir); ! 463: AngleVectors (dir, forward, right, up); ! 464: ! 465: // if ((g_showlogic) && (g_showlogic->value)) ! 466: // gi.dprintf ("start %s aim %s speed %d\n", vtos(start), vtos(aimdir), speed); ! 467: prox = G_Spawn(); ! 468: VectorCopy (start, prox->s.origin); ! 469: VectorScale (aimdir, speed, prox->velocity); ! 470: VectorMA (prox->velocity, 200 + crandom() * 10.0, up, prox->velocity); ! 471: VectorMA (prox->velocity, crandom() * 10.0, right, prox->velocity); ! 472: VectorCopy (dir, prox->s.angles); ! 473: prox->s.angles[PITCH]-=90; ! 474: prox->movetype = MOVETYPE_BOUNCE; ! 475: prox->solid = SOLID_BBOX; ! 476: prox->s.effects |= EF_GRENADE; ! 477: prox->clipmask = MASK_SHOT|CONTENTS_LAVA|CONTENTS_SLIME; ! 478: prox->s.renderfx |= RF_IR_VISIBLE; ! 479: //FIXME - this needs to be bigger. Has other effects, though. Maybe have to change origin to compensate ! 480: // so it sinks in correctly. Also in lavacheck, might have to up the distance ! 481: VectorSet (prox->mins, -6, -6, -6); ! 482: VectorSet (prox->maxs, 6, 6, 6); ! 483: prox->s.modelindex = gi.modelindex ("models/weapons/g_prox/tris.md2"); ! 484: prox->owner = self; ! 485: prox->teammaster = self; ! 486: prox->touch = prox_land; ! 487: // prox->nextthink = level.time + PROX_TIME_TO_LIVE; ! 488: prox->think = Prox_Explode; ! 489: prox->dmg = PROX_DAMAGE*damage_multiplier; ! 490: prox->classname = "prox"; ! 491: prox->svflags |= SVF_DAMAGEABLE; ! 492: prox->flags |= FL_MECHANICAL; ! 493: ! 494: switch (damage_multiplier) ! 495: { ! 496: case 1: ! 497: prox->nextthink = level.time + PROX_TIME_TO_LIVE; ! 498: break; ! 499: case 2: ! 500: prox->nextthink = level.time + 30; ! 501: break; ! 502: case 4: ! 503: prox->nextthink = level.time + 15; ! 504: break; ! 505: case 8: ! 506: prox->nextthink = level.time + 10; ! 507: break; ! 508: default: ! 509: if ((g_showlogic) && (g_showlogic->value)) ! 510: gi.dprintf ("prox with unknown multiplier %d!\n", damage_multiplier); ! 511: prox->nextthink = level.time + PROX_TIME_TO_LIVE; ! 512: break; ! 513: } ! 514: ! 515: gi.linkentity (prox); ! 516: } ! 517: #endif ! 518: ! 519: // ************************* ! 520: // FLAMETHROWER ! 521: // ************************* ! 522: ! 523: #ifdef INCLUDE_FLAMETHROWER ! 524: #define FLAMETHROWER_RADIUS 8 ! 525: ! 526: void fire_remove (edict_t *ent) ! 527: { ! 528: if(ent == ent->owner->teamchain) ! 529: ent->owner->teamchain = NULL; ! 530: ! 531: G_FreeEdict(ent); ! 532: } ! 533: ! 534: void fire_flame (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed) ! 535: { ! 536: edict_t *flame; ! 537: vec3_t dir; ! 538: vec3_t forward, right, up; ! 539: ! 540: vectoangles2 (aimdir, dir); ! 541: AngleVectors (dir, forward, right, up); ! 542: ! 543: flame = G_Spawn(); ! 544: ! 545: // the origin is the first control point, put it speed forward. ! 546: VectorMA(start, speed, forward, flame->s.origin); ! 547: ! 548: // record that velocity ! 549: VectorScale (aimdir, speed, flame->velocity); ! 550: ! 551: VectorCopy (dir, flame->s.angles); ! 552: flame->movetype = MOVETYPE_NONE; ! 553: flame->solid = SOLID_NOT; ! 554: ! 555: VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS); ! 556: VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS); ! 557: ! 558: flame->s.sound = gi.soundindex ("weapons/flame.wav"); ! 559: flame->owner = self; ! 560: flame->dmg = damage; ! 561: flame->classname = "flame"; ! 562: ! 563: // clear control points and velocities ! 564: VectorCopy (flame->s.origin, flame->flameinfo.pos1); ! 565: VectorCopy (flame->velocity, flame->flameinfo.vel1); ! 566: VectorCopy (flame->s.origin, flame->flameinfo.pos2); ! 567: VectorCopy (flame->velocity, flame->flameinfo.vel2); ! 568: VectorCopy (flame->s.origin, flame->flameinfo.pos3); ! 569: VectorCopy (flame->velocity, flame->flameinfo.vel3); ! 570: VectorCopy (flame->s.origin, flame->flameinfo.pos4); ! 571: ! 572: // hook flame stream to owner ! 573: self->teamchain = flame; ! 574: ! 575: gi.linkentity (flame); ! 576: } ! 577: ! 578: // fixme - change to use start location, not entity origin ! 579: void fire_maintain (edict_t *ent, edict_t *flame, vec3_t start, vec3_t aimdir, int damage, int speed) ! 580: { ! 581: trace_t tr; ! 582: ! 583: // move the control points out the appropriate direction and velocity ! 584: VectorAdd(flame->flameinfo.pos3, flame->flameinfo.vel3, flame->flameinfo.pos4); ! 585: VectorAdd(flame->flameinfo.pos2, flame->flameinfo.vel2, flame->flameinfo.pos3); ! 586: VectorAdd(flame->flameinfo.pos1, flame->flameinfo.vel1, flame->flameinfo.pos2); ! 587: VectorAdd(flame->s.origin, flame->velocity, flame->flameinfo.pos1); ! 588: ! 589: // move the velocities for the control points ! 590: VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3); ! 591: VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2); ! 592: VectorCopy(flame->velocity, flame->flameinfo.vel1); ! 593: ! 594: // set velocity and location for new control point 0. ! 595: VectorMA(start, speed, aimdir, flame->s.origin); ! 596: VectorScale(aimdir, speed, flame->velocity); ! 597: ! 598: // ! 599: // does it hit a wall? if so, when? ! 600: // ! 601: ! 602: // player fire point to flame origin. ! 603: tr = gi.trace(start, flame->mins, flame->maxs, ! 604: flame->s.origin, flame, MASK_SHOT); ! 605: if(tr.fraction == 1.0) ! 606: { ! 607: // origin to point 1 ! 608: tr = gi.trace(flame->s.origin, flame->mins, flame->maxs, ! 609: flame->flameinfo.pos1, flame, MASK_SHOT); ! 610: if(tr.fraction == 1.0) ! 611: { ! 612: // point 1 to point 2 ! 613: tr = gi.trace(flame->flameinfo.pos1, flame->mins, flame->maxs, ! 614: flame->flameinfo.pos2, flame, MASK_SHOT); ! 615: if(tr.fraction == 1.0) ! 616: { ! 617: // point 2 to point 3 ! 618: tr = gi.trace(flame->flameinfo.pos2, flame->mins, flame->maxs, ! 619: flame->flameinfo.pos3, flame, MASK_SHOT); ! 620: if(tr.fraction == 1.0) ! 621: { ! 622: // point 3 to point 4, point 3 valid ! 623: tr = gi.trace(flame->flameinfo.pos3, flame->mins, flame->maxs, ! 624: flame->flameinfo.pos4, flame, MASK_SHOT); ! 625: if(tr.fraction < 1.0) // point 4 blocked ! 626: { ! 627: VectorCopy(tr.endpos, flame->flameinfo.pos4); ! 628: } ! 629: } ! 630: else // point 3 blocked, point 2 valid ! 631: { ! 632: VectorCopy(flame->flameinfo.vel2, flame->flameinfo.vel3); ! 633: VectorCopy(tr.endpos, flame->flameinfo.pos3); ! 634: VectorCopy(tr.endpos, flame->flameinfo.pos4); ! 635: } ! 636: } ! 637: else // point 2 blocked, point 1 valid ! 638: { ! 639: VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel2); ! 640: VectorCopy(flame->flameinfo.vel1, flame->flameinfo.vel3); ! 641: VectorCopy(tr.endpos, flame->flameinfo.pos2); ! 642: VectorCopy(tr.endpos, flame->flameinfo.pos3); ! 643: VectorCopy(tr.endpos, flame->flameinfo.pos4); ! 644: } ! 645: } ! 646: else // point 1 blocked, origin valid ! 647: { ! 648: VectorCopy(flame->velocity, flame->flameinfo.vel1); ! 649: VectorCopy(flame->velocity, flame->flameinfo.vel2); ! 650: VectorCopy(flame->velocity, flame->flameinfo.vel3); ! 651: VectorCopy(tr.endpos, flame->flameinfo.pos1); ! 652: VectorCopy(tr.endpos, flame->flameinfo.pos2); ! 653: VectorCopy(tr.endpos, flame->flameinfo.pos3); ! 654: VectorCopy(tr.endpos, flame->flameinfo.pos4); ! 655: } ! 656: } ! 657: else // origin blocked! ! 658: { ! 659: // gi.dprintf("point 2 blocked\n"); ! 660: VectorCopy(flame->velocity, flame->flameinfo.vel1); ! 661: VectorCopy(flame->velocity, flame->flameinfo.vel2); ! 662: VectorCopy(flame->velocity, flame->flameinfo.vel3); ! 663: VectorCopy(tr.endpos, flame->s.origin); ! 664: VectorCopy(tr.endpos, flame->flameinfo.pos1); ! 665: VectorCopy(tr.endpos, flame->flameinfo.pos2); ! 666: VectorCopy(tr.endpos, flame->flameinfo.pos3); ! 667: VectorCopy(tr.endpos, flame->flameinfo.pos4); ! 668: } ! 669: ! 670: if(tr.fraction < 1.0 && tr.ent->takedamage) ! 671: { ! 672: T_Damage (tr.ent, flame, ent, flame->velocity, tr.endpos, tr.plane.normal, ! 673: damage, 0, DAMAGE_NO_KNOCKBACK | DAMAGE_ENERGY | DAMAGE_FIRE); ! 674: } ! 675: ! 676: gi.linkentity(flame); ! 677: ! 678: gi.WriteByte (svc_temp_entity); ! 679: gi.WriteByte (TE_FLAME); ! 680: gi.WriteShort(ent - g_edicts); ! 681: gi.WriteShort(6); ! 682: gi.WritePosition (start); ! 683: gi.WritePosition (flame->s.origin); ! 684: gi.WritePosition (flame->flameinfo.pos1); ! 685: gi.WritePosition (flame->flameinfo.pos2); ! 686: gi.WritePosition (flame->flameinfo.pos3); ! 687: gi.WritePosition (flame->flameinfo.pos4); ! 688: gi.multicast (flame->s.origin, MULTICAST_PVS); ! 689: } ! 690: ! 691: /*QUAKED trap_flameshooter (1 0 0) (-8 -8 -8) (8 8 8) ! 692: */ ! 693: #define FLAMESHOOTER_VELOCITY 50 ! 694: #define FLAMESHOOTER_DAMAGE 20 ! 695: #define FLAMESHOOTER_BURST_VELOCITY 300 ! 696: #define FLAMESHOOTER_BURST_DAMAGE 30 ! 697: ! 698: //#define FLAMESHOOTER_PUFF 1 ! 699: #define FLAMESHOOTER_STREAM 1 ! 700: ! 701: void flameshooter_think (edict_t *self) ! 702: { ! 703: vec3_t forward, right, up; ! 704: edict_t *flame; ! 705: ! 706: if(self->delay) ! 707: { ! 708: if(self->teamchain) ! 709: fire_remove (self->teamchain); ! 710: return; ! 711: } ! 712: ! 713: self->s.angles[1] += self->speed; ! 714: if(self->s.angles[1] > 135 || self->s.angles[1] < 45) ! 715: self->speed = -self->speed; ! 716: ! 717: AngleVectors (self->s.angles, forward, right, up); ! 718: ! 719: #ifdef FLAMESHOOTER_STREAM ! 720: flame = self->teamchain; ! 721: if(!self->teamchain) ! 722: fire_flame (self, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY); ! 723: else ! 724: fire_maintain (self, flame, self->s.origin, forward, FLAMESHOOTER_DAMAGE, FLAMESHOOTER_VELOCITY); ! 725: ! 726: self->think = flameshooter_think; ! 727: self->nextthink = level.time + 0.05; ! 728: #else ! 729: fire_burst (self, self->s.origin, forward, FLAMESHOOTER_BURST_DAMAGE, FLAMESHOOTER_BURST_VELOCITY); ! 730: ! 731: self->think = flameshooter_think; ! 732: self->nextthink = level.time + 0.1; ! 733: #endif ! 734: } ! 735: ! 736: void flameshooter_use (edict_t *self, edict_t *other, edict_t *activator) ! 737: { ! 738: if(self->delay) ! 739: { ! 740: self->delay = 0; ! 741: self->think = flameshooter_think; ! 742: self->nextthink = level.time + 0.1; ! 743: } ! 744: else ! 745: self->delay = 1; ! 746: } ! 747: ! 748: void SP_trap_flameshooter(edict_t *self) ! 749: { ! 750: vec3_t tempAngles; ! 751: ! 752: self->solid = SOLID_NOT; ! 753: self->movetype = MOVETYPE_NONE; ! 754: ! 755: self->delay = 0; ! 756: ! 757: self->use = flameshooter_use; ! 758: if(self->delay == 0) ! 759: { ! 760: self->think = flameshooter_think; ! 761: self->nextthink = level.time + 0.1; ! 762: } ! 763: ! 764: // self->flags |= FL_NOCLIENT; ! 765: ! 766: self->speed = 10; ! 767: ! 768: // self->speed = 0; // FIXME this stops the spraying ! 769: ! 770: VectorCopy(self->s.angles, tempAngles); ! 771: ! 772: if (!VectorCompare(self->s.angles, vec3_origin)) ! 773: G_SetMovedir (self->s.angles, self->movedir); ! 774: ! 775: VectorCopy(tempAngles, self->s.angles); ! 776: ! 777: // gi.setmodel (self, self->model); ! 778: gi.linkentity (self); ! 779: } ! 780: ! 781: // ************************* ! 782: // fire_burst ! 783: // ************************* ! 784: ! 785: #define FLAME_BURST_MAX_SIZE 64 ! 786: #define FLAME_BURST_FRAMES 20 ! 787: #define FLAME_BURST_MIDPOINT 10 ! 788: ! 789: void fire_burst_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) ! 790: { ! 791: int powerunits; ! 792: int damage, radius; ! 793: vec3_t origin; ! 794: ! 795: if (surf && (surf->flags & SURF_SKY)) ! 796: { ! 797: // gi.dprintf("Hit sky. Removed\n"); ! 798: G_FreeEdict (ent); ! 799: return; ! 800: } ! 801: ! 802: if(other == ent->owner || ent == other) ! 803: return; ! 804: ! 805: // don't let flame puffs blow each other up ! 806: if(other->classname && !strcmp(other->classname, ent->classname)) ! 807: return; ! 808: ! 809: if(ent->waterlevel) ! 810: { ! 811: // gi.dprintf("Hit water. Removed\n"); ! 812: G_FreeEdict(ent); ! 813: } ! 814: ! 815: if(!(other->svflags & SVF_MONSTER) && !other->client) ! 816: { ! 817: powerunits = FLAME_BURST_FRAMES - ent->s.frame; ! 818: damage = powerunits * 6; ! 819: radius = powerunits * 4; ! 820: ! 821: // T_RadiusDamage (inflictor, attacker, damage, ignore, radius) ! 822: T_RadiusDamage(ent, ent->owner, damage, ent, radius, DAMAGE_FIRE); ! 823: ! 824: // gi.dprintf("Hit world: %d pts, %d rad\n", damage, radius); ! 825: ! 826: // calculate position for the explosion entity ! 827: VectorMA (ent->s.origin, -0.02, ent->velocity, origin); ! 828: ! 829: gi.WriteByte (svc_temp_entity); ! 830: gi.WriteByte (TE_PLAIN_EXPLOSION); ! 831: gi.WritePosition (origin); ! 832: gi.multicast (ent->s.origin, MULTICAST_PVS); ! 833: ! 834: G_FreeEdict (ent); ! 835: } ! 836: } ! 837: ! 838: void fire_burst_think (edict_t *self) ! 839: { ! 840: int current_radius; ! 841: ! 842: if(self->waterlevel) ! 843: { ! 844: G_FreeEdict(self); ! 845: return; ! 846: } ! 847: ! 848: self->s.frame++; ! 849: if(self->s.frame >= FLAME_BURST_FRAMES) ! 850: { ! 851: G_FreeEdict(self); ! 852: return; ! 853: } ! 854: ! 855: else if(self->s.frame < FLAME_BURST_MIDPOINT) ! 856: { ! 857: current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * self->s.frame; ! 858: } ! 859: else ! 860: { ! 861: current_radius = (FLAME_BURST_MAX_SIZE / FLAME_BURST_MIDPOINT) * (FLAME_BURST_FRAMES - self->s.frame); ! 862: } ! 863: ! 864: if(self->s.frame == 3) ! 865: self->s.skinnum = 1; ! 866: else if (self->s.frame == 7) ! 867: self->s.skinnum = 2; ! 868: else if (self->s.frame == 10) ! 869: self->s.skinnum = 3; ! 870: else if (self->s.frame == 13) ! 871: self->s.skinnum = 4; ! 872: else if (self->s.frame == 16) ! 873: self->s.skinnum = 5; ! 874: else if (self->s.frame == 19) ! 875: self->s.skinnum = 6; ! 876: ! 877: if(current_radius < 8) ! 878: current_radius = 8; ! 879: else if(current_radius > FLAME_BURST_MAX_SIZE) ! 880: current_radius = FLAME_BURST_MAX_SIZE; ! 881: ! 882: T_RadiusDamage(self, self->owner, self->dmg, self, current_radius, DAMAGE_FIRE); ! 883: ! 884: self->think = fire_burst_think; ! 885: self->nextthink = level.time + 0.1; ! 886: } ! 887: ! 888: void fire_burst (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed) ! 889: { ! 890: edict_t *flame; ! 891: vec3_t dir; ! 892: vec3_t baseVel; ! 893: vec3_t forward, right, up; ! 894: ! 895: vectoangles2 (aimdir, dir); ! 896: AngleVectors (dir, forward, right, up); ! 897: ! 898: flame = G_Spawn(); ! 899: VectorCopy(start, flame->s.origin); ! 900: // VectorScale (aimdir, speed, flame->velocity); ! 901: ! 902: // scale down so only 30% of player's velocity is taken into account. ! 903: VectorScale (self->velocity, 0.3, baseVel); ! 904: VectorMA(baseVel, speed, aimdir, flame->velocity); ! 905: ! 906: VectorCopy (dir, flame->s.angles); ! 907: flame->movetype = MOVETYPE_FLY; ! 908: flame->solid = SOLID_TRIGGER; ! 909: ! 910: VectorSet(flame->mins, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS, -FLAMETHROWER_RADIUS); ! 911: VectorSet(flame->maxs, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS, FLAMETHROWER_RADIUS); ! 912: ! 913: flame->s.sound = gi.soundindex ("weapons/flame.wav"); ! 914: flame->s.modelindex = gi.modelindex ("models/projectiles/puff/tris.md2"); ! 915: flame->owner = self; ! 916: flame->touch = fire_burst_touch; ! 917: flame->think = fire_burst_think; ! 918: flame->nextthink = level.time + 0.1; ! 919: flame->dmg = damage; ! 920: flame->classname = "flameburst"; ! 921: flame->s.effects = EF_FIRE_PUFF; ! 922: ! 923: gi.linkentity (flame); ! 924: } ! 925: #endif ! 926: ! 927: // ************************* ! 928: // INCENDIARY GRENADES ! 929: // ************************* ! 930: ! 931: #ifdef INCLUDE_INCENDIARY ! 932: void FireThink (edict_t *ent) ! 933: { ! 934: if(level.time > ent->wait) ! 935: G_FreeEdict(ent); ! 936: else ! 937: { ! 938: ent->s.frame++; ! 939: if(ent->s.frame>10) ! 940: ent->s.frame = 0; ! 941: ent->nextthink = level.time + 0.05; ! 942: ent->think = FireThink; ! 943: } ! 944: } ! 945: ! 946: #define FIRE_HEIGHT 64 ! 947: #define FIRE_RADIUS 64 ! 948: #define FIRE_DAMAGE 3 ! 949: #define FIRE_DURATION 15 ! 950: ! 951: edict_t *StartFire(edict_t *fireOwner, vec3_t fireOrigin, float fireDuration, float fireDamage) ! 952: { ! 953: edict_t *fire; ! 954: ! 955: fire = G_Spawn(); ! 956: VectorCopy (fireOrigin, fire->s.origin); ! 957: fire->movetype = MOVETYPE_TOSS; ! 958: fire->solid = SOLID_TRIGGER; ! 959: VectorSet(fire->mins, -FIRE_RADIUS, -FIRE_RADIUS, 0); ! 960: VectorSet(fire->maxs, FIRE_RADIUS, FIRE_RADIUS, FIRE_HEIGHT); ! 961: ! 962: fire->s.sound = gi.soundindex ("weapons/incend.wav"); ! 963: fire->s.modelindex = gi.modelindex ("models/objects/fire/tris.md2"); ! 964: ! 965: fire->owner = fireOwner; ! 966: fire->touch = hurt_touch; ! 967: fire->nextthink = level.time + 0.05; ! 968: fire->wait = level.time + fireDuration; ! 969: fire->think = FireThink; ! 970: // fire->nextthink = level.time + fireDuration; ! 971: // fire->think = G_FreeEdict; ! 972: fire->dmg = fireDamage; ! 973: fire->classname = "incendiary_fire"; ! 974: ! 975: gi.linkentity (fire); ! 976: ! 977: // gi.sound (fire, CHAN_VOICE, gi.soundindex ("weapons/incend.wav"), 1, ATTN_NORM, 0); ! 978: return fire; ! 979: } ! 980: ! 981: static void Incendiary_Explode (edict_t *ent) ! 982: { ! 983: vec3_t origin; ! 984: ! 985: if (ent->owner->client) ! 986: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); ! 987: ! 988: //FIXME: if we are onground then raise our Z just a bit since we are a point? ! 989: T_RadiusDamage(ent, ent->owner, ent->dmg, NULL, ent->dmg_radius, DAMAGE_FIRE); ! 990: ! 991: VectorMA (ent->s.origin, -0.02, ent->velocity, origin); ! 992: gi.WriteByte (svc_temp_entity); ! 993: if (ent->groundentity) ! 994: gi.WriteByte (TE_GRENADE_EXPLOSION); ! 995: else ! 996: gi.WriteByte (TE_ROCKET_EXPLOSION); ! 997: gi.WritePosition (origin); ! 998: gi.multicast (ent->s.origin, MULTICAST_PVS); ! 999: ! 1000: StartFire(ent->owner, ent->s.origin, FIRE_DURATION, FIRE_DAMAGE); ! 1001: ! 1002: G_FreeEdict (ent); ! 1003: ! 1004: } ! 1005: ! 1006: static void Incendiary_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) ! 1007: { ! 1008: if (other == ent->owner) ! 1009: return; ! 1010: ! 1011: if (surf && (surf->flags & SURF_SKY)) ! 1012: { ! 1013: G_FreeEdict (ent); ! 1014: return; ! 1015: } ! 1016: ! 1017: if (!(other->svflags & SVF_MONSTER) && !(ent->client)) ! 1018: // if (!other->takedamage) ! 1019: { ! 1020: if (ent->spawnflags & 1) ! 1021: { ! 1022: if (random() > 0.5) ! 1023: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); ! 1024: else ! 1025: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); ! 1026: } ! 1027: else ! 1028: { ! 1029: if (random() > 0.5) ! 1030: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); ! 1031: else ! 1032: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb2b.wav"), 1, ATTN_NORM, 0); ! 1033: } ! 1034: return; ! 1035: } ! 1036: ! 1037: Incendiary_Explode (ent); ! 1038: } ! 1039: ! 1040: void fire_incendiary_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) ! 1041: { ! 1042: edict_t *grenade; ! 1043: vec3_t dir; ! 1044: vec3_t forward, right, up; ! 1045: ! 1046: vectoangles2 (aimdir, dir); ! 1047: AngleVectors (dir, forward, right, up); ! 1048: ! 1049: grenade = G_Spawn(); ! 1050: VectorCopy (start, grenade->s.origin); ! 1051: VectorScale (aimdir, speed, grenade->velocity); ! 1052: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); ! 1053: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); ! 1054: VectorSet (grenade->avelocity, 300, 300, 300); ! 1055: grenade->movetype = MOVETYPE_BOUNCE; ! 1056: grenade->clipmask = MASK_SHOT; ! 1057: grenade->solid = SOLID_BBOX; ! 1058: grenade->s.effects |= EF_GRENADE; ! 1059: // if (self->client) ! 1060: // grenade->s.effects &= ~EF_TELEPORT; ! 1061: VectorClear (grenade->mins); ! 1062: VectorClear (grenade->maxs); ! 1063: grenade->s.modelindex = gi.modelindex ("models/projectiles/incend/tris.md2"); ! 1064: grenade->owner = self; ! 1065: grenade->touch = Incendiary_Touch; ! 1066: grenade->nextthink = level.time + timer; ! 1067: grenade->think = Incendiary_Explode; ! 1068: grenade->dmg = damage; ! 1069: grenade->dmg_radius = damage_radius; ! 1070: grenade->classname = "incendiary_grenade"; ! 1071: ! 1072: gi.linkentity (grenade); ! 1073: } ! 1074: #endif ! 1075: ! 1076: // ************************* ! 1077: // MELEE WEAPONS ! 1078: // ************************* ! 1079: ! 1080: #ifdef INCLUDE_MELEE ! 1081: void fire_player_melee (edict_t *self, vec3_t start, vec3_t aim, int reach, int damage, int kick, int quiet, int mod) ! 1082: { ! 1083: vec3_t forward, right, up; ! 1084: vec3_t v; ! 1085: vec3_t point; ! 1086: trace_t tr; ! 1087: ! 1088: vectoangles2 (aim, v); ! 1089: AngleVectors (v, forward, right, up); ! 1090: VectorNormalize (forward); ! 1091: VectorMA( start, reach, forward, point); ! 1092: ! 1093: //see if the hit connects ! 1094: tr = gi.trace(start, NULL, NULL, point, self, MASK_SHOT); ! 1095: if(tr.fraction == 1.0) ! 1096: { ! 1097: if(!quiet) ! 1098: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/swish.wav"), 1, ATTN_NORM, 0); ! 1099: //FIXME some sound here? ! 1100: return; ! 1101: } ! 1102: ! 1103: if(tr.ent->takedamage == DAMAGE_YES || tr.ent->takedamage == DAMAGE_AIM) ! 1104: { ! 1105: // pull the player forward if you do damage ! 1106: VectorMA(self->velocity, 75, forward, self->velocity); ! 1107: VectorMA(self->velocity, 75, up, self->velocity); ! 1108: ! 1109: // do the damage ! 1110: // FIXME - make the damage appear at right spot and direction ! 1111: if(mod == MOD_CHAINFIST) ! 1112: T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2, ! 1113: DAMAGE_DESTROY_ARMOR | DAMAGE_NO_KNOCKBACK, mod); ! 1114: else ! 1115: T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, mod); ! 1116: ! 1117: if(!quiet) ! 1118: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/meatht.wav"), 1, ATTN_NORM, 0); ! 1119: } ! 1120: else ! 1121: { ! 1122: if(!quiet) ! 1123: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_NORM, 0); ! 1124: ! 1125: VectorScale (tr.plane.normal, 256, point); ! 1126: gi.WriteByte (svc_temp_entity); ! 1127: gi.WriteByte (TE_GUNSHOT); ! 1128: gi.WritePosition (tr.endpos); ! 1129: gi.WriteDir (point); ! 1130: gi.multicast (tr.endpos, MULTICAST_PVS); ! 1131: } ! 1132: } ! 1133: #endif ! 1134: ! 1135: // ************************* ! 1136: // NUKE ! 1137: // ************************* ! 1138: ! 1139: #ifdef INCLUDE_NUKE ! 1140: #define NUKE_DELAY 4 ! 1141: #define NUKE_TIME_TO_LIVE 6 ! 1142: //#define NUKE_TIME_TO_LIVE 40 ! 1143: #define NUKE_RADIUS 512 ! 1144: #define NUKE_DAMAGE 400 ! 1145: #define NUKE_QUAKE_TIME 3 ! 1146: #define NUKE_QUAKE_STRENGTH 100 ! 1147: ! 1148: void Nuke_Quake (edict_t *self) ! 1149: { ! 1150: int i; ! 1151: edict_t *e; ! 1152: ! 1153: if (self->last_move_time < level.time) ! 1154: { ! 1155: gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 0.75, ATTN_NONE, 0); ! 1156: self->last_move_time = level.time + 0.5; ! 1157: } ! 1158: ! 1159: for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++) ! 1160: { ! 1161: if (!e->inuse) ! 1162: continue; ! 1163: if (!e->client) ! 1164: continue; ! 1165: if (!e->groundentity) ! 1166: continue; ! 1167: ! 1168: e->groundentity = NULL; ! 1169: e->velocity[0] += crandom()* 150; ! 1170: e->velocity[1] += crandom()* 150; ! 1171: e->velocity[2] = self->speed * (100.0 / e->mass); ! 1172: } ! 1173: ! 1174: if (level.time < self->timestamp) ! 1175: self->nextthink = level.time + FRAMETIME; ! 1176: else ! 1177: G_FreeEdict (self); ! 1178: } ! 1179: ! 1180: ! 1181: static void Nuke_Explode (edict_t *ent) ! 1182: { ! 1183: // vec3_t origin; ! 1184: ! 1185: // nuke_framenum = level.framenum + 20; ! 1186: ! 1187: if (ent->teammaster->client) ! 1188: PlayerNoise(ent->teammaster, ent->s.origin, PNOISE_IMPACT); ! 1189: ! 1190: T_RadiusNukeDamage(ent, ent->teammaster, ent->dmg, ent, ent->dmg_radius, MOD_NUKE); ! 1191: ! 1192: // VectorMA (ent->s.origin, -0.02, ent->velocity, origin); ! 1193: if (ent->dmg > NUKE_DAMAGE) ! 1194: gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); ! 1195: ! 1196: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/grenlx1a.wav"), 1, ATTN_NONE, 0); ! 1197: /* ! 1198: gi.WriteByte (svc_temp_entity); ! 1199: if (ent->groundentity) ! 1200: gi.WriteByte (TE_GRENADE_EXPLOSION); ! 1201: else ! 1202: gi.WriteByte (TE_ROCKET_EXPLOSION); ! 1203: gi.WritePosition (origin); ! 1204: gi.multicast (ent->s.origin, MULTICAST_PVS); ! 1205: */ ! 1206: ! 1207: // BecomeExplosion1(ent); ! 1208: ! 1209: gi.WriteByte (svc_temp_entity); ! 1210: gi.WriteByte (TE_EXPLOSION1_BIG); ! 1211: gi.WritePosition (ent->s.origin); ! 1212: gi.multicast (ent->s.origin, MULTICAST_PVS); ! 1213: ! 1214: gi.WriteByte (svc_temp_entity); ! 1215: gi.WriteByte (TE_NUKEBLAST); ! 1216: gi.WritePosition (ent->s.origin); ! 1217: gi.multicast (ent->s.origin, MULTICAST_ALL); ! 1218: ! 1219: // become a quake ! 1220: ent->svflags |= SVF_NOCLIENT; ! 1221: ent->noise_index = gi.soundindex ("world/rumble.wav"); ! 1222: ent->think = Nuke_Quake; ! 1223: ent->speed = NUKE_QUAKE_STRENGTH; ! 1224: ent->timestamp = level.time + NUKE_QUAKE_TIME; ! 1225: ent->nextthink = level.time + FRAMETIME; ! 1226: ent->last_move_time = 0; ! 1227: } ! 1228: ! 1229: void nuke_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 1230: { ! 1231: self->takedamage = DAMAGE_NO; ! 1232: if ((attacker) && !(strcmp(attacker->classname, "nuke"))) ! 1233: { ! 1234: if ((g_showlogic) && (g_showlogic->value)) ! 1235: gi.dprintf ("nuke nuked by a nuke, not nuking\n"); ! 1236: G_FreeEdict (self); ! 1237: return; ! 1238: } ! 1239: Nuke_Explode(self); ! 1240: } ! 1241: ! 1242: void Nuke_Think(edict_t *ent) ! 1243: { ! 1244: float attenuation, default_atten = 1.8; ! 1245: int damage_multiplier, muzzleflash; ! 1246: ! 1247: // gi.dprintf ("player range: %2.2f damage radius: %2.2f\n", realrange (ent, ent->teammaster), ent->dmg_radius*2); ! 1248: ! 1249: damage_multiplier = ent->dmg/NUKE_DAMAGE; ! 1250: switch (damage_multiplier) ! 1251: { ! 1252: case 1: ! 1253: attenuation = default_atten/1.4; ! 1254: muzzleflash = MZ_NUKE1; ! 1255: break; ! 1256: case 2: ! 1257: attenuation = default_atten/2.0; ! 1258: muzzleflash = MZ_NUKE2; ! 1259: break; ! 1260: case 4: ! 1261: attenuation = default_atten/3.0; ! 1262: muzzleflash = MZ_NUKE4; ! 1263: break; ! 1264: case 8: ! 1265: attenuation = default_atten/5.0; ! 1266: muzzleflash = MZ_NUKE8; ! 1267: break; ! 1268: default: ! 1269: if ((g_showlogic) && (g_showlogic->value)) ! 1270: gi.dprintf ("default attenuation used for nuke!\n"); ! 1271: attenuation = default_atten; ! 1272: muzzleflash = MZ_NUKE1; ! 1273: break; ! 1274: } ! 1275: ! 1276: if(ent->wait < level.time) ! 1277: Nuke_Explode(ent); ! 1278: else if (level.time >= (ent->wait - NUKE_TIME_TO_LIVE)) ! 1279: { ! 1280: ent->s.frame++; ! 1281: // if ((g_showlogic) && (g_showlogic->value)) ! 1282: // gi.dprintf ("nuke frame %d\n", ent->s.frame); ! 1283: if(ent->s.frame > 11) ! 1284: ent->s.frame = 6; ! 1285: ! 1286: if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA)) ! 1287: { ! 1288: Nuke_Explode (ent); ! 1289: return; ! 1290: } ! 1291: ! 1292: ent->think = Nuke_Think; ! 1293: ent->nextthink = level.time + 0.1; ! 1294: ent->health = 1; ! 1295: ent->owner = NULL; ! 1296: ! 1297: gi.WriteByte (svc_muzzleflash); ! 1298: gi.WriteShort (ent-g_edicts); ! 1299: gi.WriteByte (muzzleflash); ! 1300: gi.multicast (ent->s.origin, MULTICAST_PVS); ! 1301: ! 1302: if (ent->timestamp <= level.time) ! 1303: { ! 1304: /* gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn.wav"), 1, ATTN_NORM, 0); ! 1305: ent->timestamp += 10.0; ! 1306: } ! 1307: */ ! 1308: ! 1309: if ((ent->wait - level.time) <= (NUKE_TIME_TO_LIVE/2.0)) ! 1310: { ! 1311: // ent->s.sound = gi.soundindex ("weapons/nukewarn.wav"); ! 1312: // gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0); ! 1313: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0); ! 1314: // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0); ! 1315: // gi.dprintf ("time %2.2f\n", ent->wait-level.time); ! 1316: ent->timestamp = level.time + 0.3; ! 1317: } ! 1318: else ! 1319: { ! 1320: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0); ! 1321: // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0); ! 1322: ent->timestamp = level.time + 0.5; ! 1323: // gi.dprintf ("time %2.2f\n", ent->wait-level.time); ! 1324: } ! 1325: } ! 1326: } ! 1327: else ! 1328: { ! 1329: if (ent->timestamp <= level.time) ! 1330: { ! 1331: gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, attenuation, 0); ! 1332: // gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/nukewarn2.wav"), 1, ATTN_NORM, 0); ! 1333: // gi.dprintf ("time %2.2f\n", ent->wait-level.time); ! 1334: ent->timestamp = level.time + 1.0; ! 1335: } ! 1336: ent->nextthink = level.time + FRAMETIME; ! 1337: } ! 1338: } ! 1339: ! 1340: void nuke_bounce (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) ! 1341: { ! 1342: if (random() > 0.5) ! 1343: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); ! 1344: else ! 1345: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); ! 1346: } ! 1347: ! 1348: ! 1349: extern byte P_DamageModifier(edict_t *ent); ! 1350: ! 1351: void fire_nuke (edict_t *self, vec3_t start, vec3_t aimdir, int speed) ! 1352: { ! 1353: edict_t *nuke; ! 1354: vec3_t dir; ! 1355: vec3_t forward, right, up; ! 1356: int damage_modifier; ! 1357: ! 1358: damage_modifier = (int) P_DamageModifier (self); ! 1359: ! 1360: vectoangles2 (aimdir, dir); ! 1361: AngleVectors (dir, forward, right, up); ! 1362: ! 1363: nuke = G_Spawn(); ! 1364: VectorCopy (start, nuke->s.origin); ! 1365: VectorScale (aimdir, speed, nuke->velocity); ! 1366: ! 1367: VectorMA (nuke->velocity, 200 + crandom() * 10.0, up, nuke->velocity); ! 1368: VectorMA (nuke->velocity, crandom() * 10.0, right, nuke->velocity); ! 1369: VectorClear (nuke->avelocity); ! 1370: VectorClear (nuke->s.angles); ! 1371: nuke->movetype = MOVETYPE_BOUNCE; ! 1372: nuke->clipmask = MASK_SHOT; ! 1373: nuke->solid = SOLID_BBOX; ! 1374: nuke->s.effects |= EF_GRENADE; ! 1375: nuke->s.renderfx |= RF_IR_VISIBLE; ! 1376: VectorSet (nuke->mins, -8, -8, 0); ! 1377: VectorSet (nuke->maxs, 8, 8, 16); ! 1378: nuke->s.modelindex = gi.modelindex ("models/weapons/g_nuke/tris.md2"); ! 1379: nuke->owner = self; ! 1380: nuke->teammaster = self; ! 1381: nuke->nextthink = level.time + FRAMETIME; ! 1382: nuke->wait = level.time + NUKE_DELAY + NUKE_TIME_TO_LIVE; ! 1383: nuke->think = Nuke_Think; ! 1384: nuke->touch = nuke_bounce; ! 1385: ! 1386: nuke->health = 10000; ! 1387: nuke->takedamage = DAMAGE_YES; ! 1388: nuke->svflags |= SVF_DAMAGEABLE; ! 1389: nuke->dmg = NUKE_DAMAGE * damage_modifier; ! 1390: if (damage_modifier == 1) ! 1391: nuke->dmg_radius = NUKE_RADIUS; ! 1392: else ! 1393: nuke->dmg_radius = NUKE_RADIUS + NUKE_RADIUS*(0.25*(float)damage_modifier); ! 1394: // this yields 1.0, 1.5, 2.0, 3.0 times radius ! 1395: ! 1396: if ((g_showlogic) && (g_showlogic->value)) ! 1397: gi.dprintf ("nuke modifier = %d, damage = %d, radius = %f\n", damage_modifier, nuke->dmg, nuke->dmg_radius); ! 1398: ! 1399: nuke->classname = "nuke"; ! 1400: nuke->die = nuke_die; ! 1401: ! 1402: gi.linkentity (nuke); ! 1403: } ! 1404: #endif ! 1405: ! 1406: // ************************* ! 1407: // TESLA ! 1408: // ************************* ! 1409: ! 1410: #ifdef INCLUDE_TESLA ! 1411: #define TESLA_TIME_TO_LIVE 30 ! 1412: #define TESLA_DAMAGE_RADIUS 128 ! 1413: #define TESLA_DAMAGE 3 // 3 ! 1414: #define TESLA_KNOCKBACK 8 ! 1415: ! 1416: #define TESLA_ACTIVATE_TIME 3 ! 1417: ! 1418: #define TESLA_EXPLOSION_DAMAGE_MULT 50 // this is the amount the damage is multiplied by for underwater explosions ! 1419: #define TESLA_EXPLOSION_RADIUS 200 ! 1420: ! 1421: void tesla_remove (edict_t *self) ! 1422: { ! 1423: edict_t *cur, *next; ! 1424: ! 1425: self->takedamage = DAMAGE_NO; ! 1426: if(self->teamchain) ! 1427: { ! 1428: cur = self->teamchain; ! 1429: while(cur) ! 1430: { ! 1431: next = cur->teamchain; ! 1432: G_FreeEdict ( cur ); ! 1433: cur = next; ! 1434: } ! 1435: } ! 1436: else if (self->air_finished) ! 1437: gi.dprintf ("tesla without a field!\n"); ! 1438: ! 1439: self->owner = self->teammaster; // Going away, set the owner correctly. ! 1440: // PGM - grenade explode does damage to self->enemy ! 1441: self->enemy = NULL; ! 1442: ! 1443: // play quad sound if quadded and an underwater explosion ! 1444: if ((self->dmg_radius) && (self->dmg > (TESLA_DAMAGE*TESLA_EXPLOSION_DAMAGE_MULT))) ! 1445: gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); ! 1446: ! 1447: Grenade_Explode(self); ! 1448: } ! 1449: ! 1450: void tesla_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 1451: { ! 1452: // gi.dprintf("tesla killed\n"); ! 1453: tesla_remove(self); ! 1454: } ! 1455: ! 1456: void tesla_blow (edict_t *self) ! 1457: { ! 1458: // T_RadiusDamage(self, self->owner, TESLA_EXPLOSION_DAMAGE, NULL, TESLA_EXPLOSION_RADIUS, MOD_TESLA); ! 1459: self->dmg = self->dmg * TESLA_EXPLOSION_DAMAGE_MULT; ! 1460: self->dmg_radius = TESLA_EXPLOSION_RADIUS; ! 1461: tesla_remove(self); ! 1462: } ! 1463: ! 1464: ! 1465: void tesla_zap (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 1466: { ! 1467: } ! 1468: ! 1469: void tesla_think_active (edict_t *self) ! 1470: { ! 1471: int i,num; ! 1472: edict_t *touch[MAX_EDICTS], *hit; ! 1473: vec3_t dir, start; ! 1474: trace_t tr; ! 1475: ! 1476: if(level.time > self->air_finished) ! 1477: { ! 1478: tesla_remove(self); ! 1479: return; ! 1480: } ! 1481: ! 1482: VectorCopy(self->s.origin, start); ! 1483: start[2] += 16; ! 1484: ! 1485: num = gi.BoxEdicts(self->teamchain->absmin, self->teamchain->absmax, touch, MAX_EDICTS, AREA_SOLID); ! 1486: for(i=0;i<num;i++) ! 1487: { ! 1488: hit=touch[i]; ! 1489: if(!hit->inuse) ! 1490: continue; ! 1491: if(hit == self) ! 1492: continue; ! 1493: if(hit->health < 1) ! 1494: continue; ! 1495: // don't hit clients in single-player or coop ! 1496: if(hit->client) ! 1497: if (coop->value || !deathmatch->value) ! 1498: continue; ! 1499: if(!(hit->svflags & (SVF_MONSTER | SVF_DAMAGEABLE)) && !hit->client) ! 1500: continue; ! 1501: ! 1502: tr = gi.trace(start, vec3_origin, vec3_origin, hit->s.origin, self, MASK_SHOT); ! 1503: if(tr.fraction==1 || tr.ent==hit)// || tr.ent->client || (tr.ent->svflags & (SVF_MONSTER | SVF_DAMAGEABLE))) ! 1504: { ! 1505: VectorSubtract(hit->s.origin, start, dir); ! 1506: ! 1507: // PMM - play quad sound if it's above the "normal" damage ! 1508: if (self->dmg > TESLA_DAMAGE) ! 1509: gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); ! 1510: ! 1511: // PGM - don't do knockback to walking monsters ! 1512: if((hit->svflags & SVF_MONSTER) && !(hit->flags & (FL_FLY|FL_SWIM))) ! 1513: T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal, ! 1514: self->dmg, 0, 0, MOD_TESLA); ! 1515: else ! 1516: T_Damage (hit, self, self->teammaster, dir, tr.endpos, tr.plane.normal, ! 1517: self->dmg, TESLA_KNOCKBACK, 0, MOD_TESLA); ! 1518: ! 1519: gi.WriteByte (svc_temp_entity); ! 1520: gi.WriteByte (TE_LIGHTNING); ! 1521: gi.WriteShort (hit - g_edicts); // destination entity ! 1522: gi.WriteShort (self - g_edicts); // source entity ! 1523: gi.WritePosition (tr.endpos); ! 1524: gi.WritePosition (start); ! 1525: gi.multicast (start, MULTICAST_PVS); ! 1526: } ! 1527: } ! 1528: ! 1529: if(self->inuse) ! 1530: { ! 1531: self->think = tesla_think_active; ! 1532: self->nextthink = level.time + FRAMETIME; ! 1533: } ! 1534: } ! 1535: ! 1536: void tesla_activate (edict_t *self) ! 1537: { ! 1538: edict_t *trigger; ! 1539: edict_t *search; ! 1540: ! 1541: if (gi.pointcontents (self->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WATER)) ! 1542: { ! 1543: tesla_blow (self); ! 1544: return; ! 1545: } ! 1546: ! 1547: // only check for spawn points in deathmatch ! 1548: if (deathmatch->value) ! 1549: { ! 1550: search = NULL; ! 1551: while ((search = findradius(search, self->s.origin, 1.5*TESLA_DAMAGE_RADIUS)) != NULL) ! 1552: { ! 1553: //if (!search->takedamage) ! 1554: // continue; ! 1555: // if it's a monster or player with health > 0 ! 1556: // or it's a deathmatch start point ! 1557: // and we can see it ! 1558: // blow up ! 1559: if(search->classname) ! 1560: { ! 1561: if ( ( (!strcmp(search->classname, "info_player_deathmatch")) ! 1562: || (!strcmp(search->classname, "info_player_start")) ! 1563: || (!strcmp(search->classname, "info_player_coop")) ! 1564: || (!strcmp(search->classname, "misc_teleporter_dest")) ! 1565: ) ! 1566: && (visible (search, self)) ! 1567: ) ! 1568: { ! 1569: if ((g_showlogic) && (g_showlogic->value)) ! 1570: gi.dprintf ("Tesla to close to %s, removing!\n", search->classname); ! 1571: tesla_remove (self); ! 1572: return; ! 1573: } ! 1574: } ! 1575: } ! 1576: } ! 1577: ! 1578: trigger = G_Spawn(); ! 1579: // if (trigger->nextthink) ! 1580: // { ! 1581: // if ((g_showlogic) && (g_showlogic->value)) ! 1582: // gi.dprintf ("tesla_activate: fixing nextthink\n"); ! 1583: // trigger->nextthink = 0; ! 1584: // } ! 1585: VectorCopy (self->s.origin, trigger->s.origin); ! 1586: VectorSet (trigger->mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, self->mins[2]); ! 1587: VectorSet (trigger->maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS); ! 1588: trigger->movetype = MOVETYPE_NONE; ! 1589: trigger->solid = SOLID_TRIGGER; ! 1590: trigger->owner = self; ! 1591: trigger->touch = tesla_zap; ! 1592: trigger->classname = "tesla trigger"; ! 1593: // doesn't need to be marked as a teamslave since the move code for bounce looks for teamchains ! 1594: gi.linkentity (trigger); ! 1595: ! 1596: VectorClear (self->s.angles); ! 1597: // clear the owner if in deathmatch ! 1598: if (deathmatch->value) ! 1599: self->owner = NULL; ! 1600: self->teamchain = trigger; ! 1601: self->think = tesla_think_active; ! 1602: self->nextthink = level.time + FRAMETIME; ! 1603: self->air_finished = level.time + TESLA_TIME_TO_LIVE; ! 1604: } ! 1605: ! 1606: void tesla_think (edict_t *ent) ! 1607: { ! 1608: if (gi.pointcontents (ent->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA)) ! 1609: { ! 1610: tesla_remove (ent); ! 1611: return; ! 1612: } ! 1613: VectorClear (ent->s.angles); ! 1614: ent->s.frame++; ! 1615: if(ent->s.frame > 14) ! 1616: { ! 1617: ent->s.frame = 14; ! 1618: ent->think = tesla_activate; ! 1619: ent->nextthink = level.time + 0.1; ! 1620: } ! 1621: else ! 1622: { ! 1623: if(ent->s.frame > 9) ! 1624: { ! 1625: if(ent->s.frame == 10) ! 1626: { ! 1627: if (ent->owner && ent->owner->client) ! 1628: { ! 1629: PlayerNoise(ent->owner, ent->s.origin, PNOISE_WEAPON); // PGM ! 1630: } ! 1631: ent->s.skinnum = 1; ! 1632: } ! 1633: else if(ent->s.frame == 12) ! 1634: ent->s.skinnum = 2; ! 1635: else if(ent->s.frame == 14) ! 1636: ent->s.skinnum = 3; ! 1637: } ! 1638: ent->think = tesla_think; ! 1639: ent->nextthink = level.time + 0.1; ! 1640: } ! 1641: } ! 1642: ! 1643: void tesla_lava (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) ! 1644: { ! 1645: vec3_t land_point; ! 1646: ! 1647: if (plane->normal) ! 1648: { ! 1649: VectorMA (ent->s.origin, -20.0, plane->normal, land_point); ! 1650: if (gi.pointcontents (land_point) & (CONTENTS_SLIME|CONTENTS_LAVA)) ! 1651: { ! 1652: tesla_blow (ent); ! 1653: return; ! 1654: } ! 1655: } ! 1656: if (random() > 0.5) ! 1657: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); ! 1658: else ! 1659: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); ! 1660: } ! 1661: ! 1662: void fire_tesla (edict_t *self, vec3_t start, vec3_t aimdir, int damage_multiplier, int speed) ! 1663: { ! 1664: edict_t *tesla; ! 1665: vec3_t dir; ! 1666: vec3_t forward, right, up; ! 1667: ! 1668: vectoangles2 (aimdir, dir); ! 1669: AngleVectors (dir, forward, right, up); ! 1670: ! 1671: tesla = G_Spawn(); ! 1672: VectorCopy (start, tesla->s.origin); ! 1673: VectorScale (aimdir, speed, tesla->velocity); ! 1674: VectorMA (tesla->velocity, 200 + crandom() * 10.0, up, tesla->velocity); ! 1675: VectorMA (tesla->velocity, crandom() * 10.0, right, tesla->velocity); ! 1676: // VectorCopy (dir, tesla->s.angles); ! 1677: VectorClear (tesla->s.angles); ! 1678: tesla->movetype = MOVETYPE_BOUNCE; ! 1679: tesla->solid = SOLID_BBOX; ! 1680: tesla->s.effects |= EF_GRENADE; ! 1681: tesla->s.renderfx |= RF_IR_VISIBLE; ! 1682: // VectorClear (tesla->mins); ! 1683: // VectorClear (tesla->maxs); ! 1684: VectorSet (tesla->mins, -12, -12, 0); ! 1685: VectorSet (tesla->maxs, 12, 12, 20); ! 1686: tesla->s.modelindex = gi.modelindex ("models/weapons/g_tesla/tris.md2"); ! 1687: ! 1688: tesla->owner = self; // PGM - we don't want it owned by self YET. ! 1689: tesla->teammaster = self; ! 1690: ! 1691: tesla->wait = level.time + TESLA_TIME_TO_LIVE; ! 1692: tesla->think = tesla_think; ! 1693: tesla->nextthink = level.time + TESLA_ACTIVATE_TIME; ! 1694: ! 1695: // blow up on contact with lava & slime code ! 1696: tesla->touch = tesla_lava; ! 1697: ! 1698: if(deathmatch->value) ! 1699: // PMM - lowered from 50 - 7/29/1998 ! 1700: tesla->health = 20; ! 1701: else ! 1702: tesla->health = 30; // FIXME - change depending on skill? ! 1703: ! 1704: tesla->takedamage = DAMAGE_YES; ! 1705: tesla->die = tesla_die; ! 1706: tesla->dmg = TESLA_DAMAGE*damage_multiplier; ! 1707: // tesla->dmg = 0; ! 1708: tesla->classname = "tesla"; ! 1709: tesla->svflags |= SVF_DAMAGEABLE; ! 1710: tesla->clipmask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; ! 1711: tesla->flags |= FL_MECHANICAL; ! 1712: ! 1713: gi.linkentity (tesla); ! 1714: } ! 1715: #endif ! 1716: ! 1717: // ************************* ! 1718: // HEATBEAM ! 1719: // ************************* ! 1720: ! 1721: #ifdef INCLUDE_BEAMS ! 1722: static void fire_beams (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, int te_beam, int te_impact, int mod) ! 1723: { ! 1724: trace_t tr; ! 1725: vec3_t dir; ! 1726: vec3_t forward, right, up; ! 1727: vec3_t end; ! 1728: vec3_t water_start, endpoint; ! 1729: qboolean water = false, underwater = false; ! 1730: int content_mask = MASK_SHOT | MASK_WATER; ! 1731: vec3_t beam_endpt; ! 1732: ! 1733: // tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); ! 1734: // if (!(tr.fraction < 1.0)) ! 1735: // { ! 1736: vectoangles2 (aimdir, dir); ! 1737: AngleVectors (dir, forward, right, up); ! 1738: ! 1739: VectorMA (start, 8192, forward, end); ! 1740: ! 1741: if (gi.pointcontents (start) & MASK_WATER) ! 1742: { ! 1743: // gi.dprintf ("Heat beam under water\n"); ! 1744: underwater = true; ! 1745: VectorCopy (start, water_start); ! 1746: content_mask &= ~MASK_WATER; ! 1747: } ! 1748: ! 1749: tr = gi.trace (start, NULL, NULL, end, self, content_mask); ! 1750: ! 1751: // see if we hit water ! 1752: if (tr.contents & MASK_WATER) ! 1753: { ! 1754: water = true; ! 1755: VectorCopy (tr.endpos, water_start); ! 1756: ! 1757: if (!VectorCompare (start, tr.endpos)) ! 1758: { ! 1759: gi.WriteByte (svc_temp_entity); ! 1760: gi.WriteByte (TE_HEATBEAM_SPARKS); ! 1761: // gi.WriteByte (50); ! 1762: gi.WritePosition (water_start); ! 1763: gi.WriteDir (tr.plane.normal); ! 1764: // gi.WriteByte (8); ! 1765: // gi.WriteShort (60); ! 1766: gi.multicast (tr.endpos, MULTICAST_PVS); ! 1767: } ! 1768: // re-trace ignoring water this time ! 1769: tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); ! 1770: } ! 1771: VectorCopy (tr.endpos, endpoint); ! 1772: // } ! 1773: ! 1774: // halve the damage if target underwater ! 1775: if (water) ! 1776: { ! 1777: damage = damage /2; ! 1778: } ! 1779: ! 1780: // send gun puff / flash ! 1781: if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) ! 1782: { ! 1783: if (tr.fraction < 1.0) ! 1784: { ! 1785: if (tr.ent->takedamage) ! 1786: { ! 1787: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_ENERGY, mod); ! 1788: } ! 1789: else ! 1790: { ! 1791: if ((!water) && (strncmp (tr.surface->name, "sky", 3))) ! 1792: { ! 1793: // This is the truncated steam entry - uses 1+1+2 extra bytes of data ! 1794: gi.WriteByte (svc_temp_entity); ! 1795: gi.WriteByte (TE_HEATBEAM_STEAM); ! 1796: // gi.WriteByte (20); ! 1797: gi.WritePosition (tr.endpos); ! 1798: gi.WriteDir (tr.plane.normal); ! 1799: // gi.WriteByte (0xe0); ! 1800: // gi.WriteShort (60); ! 1801: gi.multicast (tr.endpos, MULTICAST_PVS); ! 1802: ! 1803: if (self->client) ! 1804: PlayerNoise(self, tr.endpos, PNOISE_IMPACT); ! 1805: } ! 1806: } ! 1807: } ! 1808: } ! 1809: ! 1810: // if went through water, determine where the end and make a bubble trail ! 1811: if ((water) || (underwater)) ! 1812: { ! 1813: vec3_t pos; ! 1814: ! 1815: VectorSubtract (tr.endpos, water_start, dir); ! 1816: VectorNormalize (dir); ! 1817: VectorMA (tr.endpos, -2, dir, pos); ! 1818: if (gi.pointcontents (pos) & MASK_WATER) ! 1819: VectorCopy (pos, tr.endpos); ! 1820: else ! 1821: tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); ! 1822: ! 1823: VectorAdd (water_start, tr.endpos, pos); ! 1824: VectorScale (pos, 0.5, pos); ! 1825: ! 1826: gi.WriteByte (svc_temp_entity); ! 1827: gi.WriteByte (TE_BUBBLETRAIL2); ! 1828: // gi.WriteByte (8); ! 1829: gi.WritePosition (water_start); ! 1830: gi.WritePosition (tr.endpos); ! 1831: gi.multicast (pos, MULTICAST_PVS); ! 1832: } ! 1833: ! 1834: if ((!underwater) && (!water)) ! 1835: { ! 1836: VectorCopy (tr.endpos, beam_endpt); ! 1837: } ! 1838: else ! 1839: { ! 1840: VectorCopy (endpoint, beam_endpt); ! 1841: } ! 1842: ! 1843: gi.WriteByte (svc_temp_entity); ! 1844: gi.WriteByte (te_beam); ! 1845: gi.WriteShort (self - g_edicts); ! 1846: gi.WritePosition (start); ! 1847: gi.WritePosition (beam_endpt); ! 1848: gi.multicast (self->s.origin, MULTICAST_ALL); ! 1849: ! 1850: } ! 1851: ! 1852: ! 1853: /* ! 1854: ================= ! 1855: fire_heat ! 1856: ! 1857: Fires a single heat beam. Zap. ! 1858: ================= ! 1859: */ ! 1860: void fire_heat (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t offset, int damage, int kick, qboolean monster) ! 1861: { ! 1862: if (monster) ! 1863: fire_beams (self, start, aimdir, offset, damage, kick, TE_MONSTER_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM); ! 1864: else ! 1865: fire_beams (self, start, aimdir, offset, damage, kick, TE_HEATBEAM, TE_HEATBEAM_SPARKS, MOD_HEATBEAM); ! 1866: } ! 1867: ! 1868: #endif ! 1869: ! 1870: ! 1871: // ************************* ! 1872: // BLASTER 2 ! 1873: // ************************* ! 1874: ! 1875: /* ! 1876: ================= ! 1877: fire_blaster2 ! 1878: ! 1879: Fires a single green blaster bolt. Used by monsters, generally. ! 1880: ================= ! 1881: */ ! 1882: void blaster2_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 1883: { ! 1884: int mod; ! 1885: int damagestat; ! 1886: ! 1887: if (other == self->owner) ! 1888: return; ! 1889: ! 1890: if (surf && (surf->flags & SURF_SKY)) ! 1891: { ! 1892: G_FreeEdict (self); ! 1893: return; ! 1894: } ! 1895: ! 1896: if (self->owner && self->owner->client) ! 1897: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); ! 1898: ! 1899: if (other->takedamage) ! 1900: { ! 1901: // the only time players will be firing blaster2 bolts will be from the ! 1902: // defender sphere. ! 1903: if(self->owner->client) ! 1904: mod = MOD_DEFENDER_SPHERE; ! 1905: else ! 1906: mod = MOD_BLASTER2; ! 1907: ! 1908: if (self->owner) ! 1909: { ! 1910: damagestat = self->owner->takedamage; ! 1911: self->owner->takedamage = DAMAGE_NO; ! 1912: if (self->dmg >= 5) ! 1913: T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0); ! 1914: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); ! 1915: self->owner->takedamage = damagestat; ! 1916: } ! 1917: else ! 1918: { ! 1919: if (self->dmg >= 5) ! 1920: T_RadiusDamage(self, self->owner, self->dmg*3, other, self->dmg_radius, 0); ! 1921: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); ! 1922: } ! 1923: } ! 1924: else ! 1925: { ! 1926: //PMM - yeowch this will get expensive ! 1927: if (self->dmg >= 5) ! 1928: T_RadiusDamage(self, self->owner, self->dmg*3, self->owner, self->dmg_radius, 0); ! 1929: ! 1930: gi.WriteByte (svc_temp_entity); ! 1931: gi.WriteByte (TE_BLASTER2); ! 1932: gi.WritePosition (self->s.origin); ! 1933: if (!plane) ! 1934: gi.WriteDir (vec3_origin); ! 1935: else ! 1936: gi.WriteDir (plane->normal); ! 1937: gi.multicast (self->s.origin, MULTICAST_PVS); ! 1938: } ! 1939: ! 1940: G_FreeEdict (self); ! 1941: } ! 1942: ! 1943: void fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) ! 1944: { ! 1945: edict_t *bolt; ! 1946: trace_t tr; ! 1947: ! 1948: VectorNormalize (dir); ! 1949: ! 1950: bolt = G_Spawn(); ! 1951: VectorCopy (start, bolt->s.origin); ! 1952: VectorCopy (start, bolt->s.old_origin); ! 1953: vectoangles2 (dir, bolt->s.angles); ! 1954: VectorScale (dir, speed, bolt->velocity); ! 1955: bolt->movetype = MOVETYPE_FLYMISSILE; ! 1956: bolt->clipmask = MASK_SHOT; ! 1957: bolt->solid = SOLID_BBOX; ! 1958: bolt->s.effects |= effect; ! 1959: VectorClear (bolt->mins); ! 1960: VectorClear (bolt->maxs); ! 1961: ! 1962: if (effect) ! 1963: bolt->s.effects |= EF_TRACKER; ! 1964: bolt->dmg_radius = 128; ! 1965: bolt->s.modelindex = gi.modelindex ("models/proj/laser2/tris.md2"); ! 1966: bolt->touch = blaster2_touch; ! 1967: ! 1968: bolt->owner = self; ! 1969: bolt->nextthink = level.time + 2; ! 1970: bolt->think = G_FreeEdict; ! 1971: bolt->dmg = damage; ! 1972: bolt->classname = "bolt"; ! 1973: gi.linkentity (bolt); ! 1974: ! 1975: if (self->client) ! 1976: check_dodge (self, bolt->s.origin, dir, speed); ! 1977: ! 1978: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); ! 1979: if (tr.fraction < 1.0) ! 1980: { ! 1981: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); ! 1982: bolt->touch (bolt, tr.ent, NULL, NULL); ! 1983: } ! 1984: } ! 1985: ! 1986: // ************************* ! 1987: // tracker ! 1988: // ************************* ! 1989: ! 1990: /* ! 1991: void tracker_boom_think (edict_t *self) ! 1992: { ! 1993: self->s.frame--; ! 1994: if(self->s.frame < 0) ! 1995: G_FreeEdict(self); ! 1996: else ! 1997: self->nextthink = level.time + 0.1; ! 1998: } ! 1999: ! 2000: void tracker_boom_spawn (vec3_t origin) ! 2001: { ! 2002: edict_t *boom; ! 2003: ! 2004: boom = G_Spawn(); ! 2005: VectorCopy (origin, boom->s.origin); ! 2006: boom->s.modelindex = gi.modelindex ("models/items/spawngro/tris.md2"); ! 2007: boom->s.skinnum = 1; ! 2008: boom->s.frame = 2; ! 2009: boom->classname = "tracker boom"; ! 2010: gi.linkentity (boom); ! 2011: ! 2012: boom->think = tracker_boom_think; ! 2013: boom->nextthink = level.time + 0.1; ! 2014: //PMM ! 2015: // boom->s.renderfx |= RF_TRANSLUCENT; ! 2016: boom->s.effects |= EF_SPHERETRANS; ! 2017: //pmm ! 2018: } ! 2019: */ ! 2020: ! 2021: #define TRACKER_DAMAGE_FLAGS (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY | DAMAGE_NO_KNOCKBACK) ! 2022: #define TRACKER_IMPACT_FLAGS (DAMAGE_NO_POWER_ARMOR | DAMAGE_ENERGY) ! 2023: ! 2024: #define TRACKER_DAMAGE_TIME 0.5 // seconds ! 2025: ! 2026: void tracker_pain_daemon_think (edict_t *self) ! 2027: { ! 2028: static vec3_t pain_normal = { 0, 0, 1 }; ! 2029: int hurt; ! 2030: ! 2031: if(!self->inuse) ! 2032: return; ! 2033: ! 2034: if((level.time - self->timestamp) > TRACKER_DAMAGE_TIME) ! 2035: { ! 2036: if(!self->enemy->client) ! 2037: self->enemy->s.effects &= ~EF_TRACKERTRAIL; ! 2038: G_FreeEdict (self); ! 2039: } ! 2040: else ! 2041: { ! 2042: if(self->enemy->health > 0) ! 2043: { ! 2044: // gi.dprintf("ouch %x\n", self); ! 2045: T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin, pain_normal, ! 2046: self->dmg, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER); ! 2047: ! 2048: // if we kill the player, we'll be removed. ! 2049: if(self->inuse) ! 2050: { ! 2051: // if we killed a monster, gib them. ! 2052: if (self->enemy->health < 1) ! 2053: { ! 2054: if(self->enemy->gib_health) ! 2055: hurt = - self->enemy->gib_health; ! 2056: else ! 2057: hurt = 500; ! 2058: ! 2059: // gi.dprintf("non-player killed. ensuring gib! %d\n", hurt); ! 2060: T_Damage (self->enemy, self, self->owner, vec3_origin, self->enemy->s.origin, ! 2061: pain_normal, hurt, 0, TRACKER_DAMAGE_FLAGS, MOD_TRACKER); ! 2062: } ! 2063: ! 2064: if(self->enemy->client) ! 2065: self->enemy->client->tracker_pain_framenum = level.framenum + 1; ! 2066: else ! 2067: self->enemy->s.effects |= EF_TRACKERTRAIL; ! 2068: ! 2069: self->nextthink = level.time + FRAMETIME; ! 2070: } ! 2071: } ! 2072: else ! 2073: { ! 2074: if(!self->enemy->client) ! 2075: self->enemy->s.effects &= ~EF_TRACKERTRAIL; ! 2076: G_FreeEdict (self); ! 2077: } ! 2078: } ! 2079: } ! 2080: ! 2081: void tracker_pain_daemon_spawn (edict_t *owner, edict_t *enemy, int damage) ! 2082: { ! 2083: edict_t *daemon; ! 2084: ! 2085: if(enemy == NULL) ! 2086: return; ! 2087: ! 2088: daemon = G_Spawn(); ! 2089: daemon->classname = "pain daemon"; ! 2090: daemon->think = tracker_pain_daemon_think; ! 2091: daemon->nextthink = level.time + FRAMETIME; ! 2092: daemon->timestamp = level.time; ! 2093: daemon->owner = owner; ! 2094: daemon->enemy = enemy; ! 2095: daemon->dmg = damage; ! 2096: } ! 2097: ! 2098: void tracker_explode (edict_t *self, cplane_t *plane) ! 2099: { ! 2100: vec3_t dir; ! 2101: ! 2102: if(!plane) ! 2103: VectorClear (dir); ! 2104: else ! 2105: VectorScale (plane->normal, 256, dir); ! 2106: ! 2107: gi.WriteByte (svc_temp_entity); ! 2108: gi.WriteByte (TE_TRACKER_EXPLOSION); ! 2109: gi.WritePosition (self->s.origin); ! 2110: gi.multicast (self->s.origin, MULTICAST_PVS); ! 2111: ! 2112: // gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/disrupthit.wav"), 1, ATTN_NORM, 0); ! 2113: // tracker_boom_spawn(self->s.origin); ! 2114: ! 2115: G_FreeEdict (self); ! 2116: } ! 2117: ! 2118: void tracker_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 2119: { ! 2120: float damagetime; ! 2121: ! 2122: if (other == self->owner) ! 2123: return; ! 2124: ! 2125: if (surf && (surf->flags & SURF_SKY)) ! 2126: { ! 2127: G_FreeEdict (self); ! 2128: return; ! 2129: } ! 2130: ! 2131: if (self->client) ! 2132: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); ! 2133: ! 2134: if (other->takedamage) ! 2135: { ! 2136: if((other->svflags & SVF_MONSTER) || other->client) ! 2137: { ! 2138: if(other->health > 0) // knockback only for living creatures ! 2139: { ! 2140: // PMM - kickback was times 4 .. reduced to 3 ! 2141: // now this does no damage, just knockback ! 2142: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, ! 2143: /* self->dmg */ 0, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER); ! 2144: ! 2145: if (!(other->flags & (FL_FLY|FL_SWIM))) ! 2146: other->velocity[2] += 140; ! 2147: ! 2148: damagetime = ((float)self->dmg)*FRAMETIME; ! 2149: damagetime = damagetime / TRACKER_DAMAGE_TIME; ! 2150: // gi.dprintf ("damage is %f\n", damagetime); ! 2151: ! 2152: tracker_pain_daemon_spawn (self->owner, other, (int)damagetime); ! 2153: } ! 2154: else // lots of damage (almost autogib) for dead bodies ! 2155: { ! 2156: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, ! 2157: self->dmg*4, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER); ! 2158: } ! 2159: } ! 2160: else // full damage in one shot for inanimate objects ! 2161: { ! 2162: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, ! 2163: self->dmg, (self->dmg*3), TRACKER_IMPACT_FLAGS, MOD_TRACKER); ! 2164: } ! 2165: } ! 2166: ! 2167: tracker_explode (self, plane); ! 2168: return; ! 2169: } ! 2170: ! 2171: void tracker_fly (edict_t *self) ! 2172: { ! 2173: vec3_t dest; ! 2174: vec3_t dir; ! 2175: vec3_t center; ! 2176: ! 2177: if ((!self->enemy) || (!self->enemy->inuse) || (self->enemy->health < 1)) ! 2178: { ! 2179: tracker_explode (self, NULL); ! 2180: return; ! 2181: } ! 2182: /* ! 2183: VectorCopy (self->enemy->s.origin, dest); ! 2184: if(self->enemy->client) ! 2185: dest[2] += self->enemy->viewheight; ! 2186: */ ! 2187: // PMM - try to hunt for center of enemy, if possible and not client ! 2188: if(self->enemy->client) ! 2189: { ! 2190: VectorCopy (self->enemy->s.origin, dest); ! 2191: dest[2] += self->enemy->viewheight; ! 2192: } ! 2193: // paranoia ! 2194: else if (VectorCompare(self->enemy->absmin, vec3_origin) || VectorCompare(self->enemy->absmax, vec3_origin)) ! 2195: { ! 2196: VectorCopy (self->enemy->s.origin, dest); ! 2197: } ! 2198: else ! 2199: { ! 2200: VectorMA (vec3_origin, 0.5, self->enemy->absmin, center); ! 2201: VectorMA (center, 0.5, self->enemy->absmax, center); ! 2202: VectorCopy (center, dest); ! 2203: } ! 2204: ! 2205: VectorSubtract (dest, self->s.origin, dir); ! 2206: VectorNormalize (dir); ! 2207: vectoangles2 (dir, self->s.angles); ! 2208: VectorScale (dir, self->speed, self->velocity); ! 2209: VectorCopy(dest, self->monsterinfo.saved_goal); ! 2210: ! 2211: self->nextthink = level.time + 0.1; ! 2212: } ! 2213: ! 2214: void fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy) ! 2215: { ! 2216: edict_t *bolt; ! 2217: trace_t tr; ! 2218: ! 2219: VectorNormalize (dir); ! 2220: ! 2221: bolt = G_Spawn(); ! 2222: VectorCopy (start, bolt->s.origin); ! 2223: VectorCopy (start, bolt->s.old_origin); ! 2224: vectoangles2 (dir, bolt->s.angles); ! 2225: VectorScale (dir, speed, bolt->velocity); ! 2226: bolt->movetype = MOVETYPE_FLYMISSILE; ! 2227: bolt->clipmask = MASK_SHOT; ! 2228: bolt->solid = SOLID_BBOX; ! 2229: bolt->speed = speed; ! 2230: bolt->s.effects = EF_TRACKER; ! 2231: bolt->s.sound = gi.soundindex ("weapons/disrupt.wav"); ! 2232: VectorClear (bolt->mins); ! 2233: VectorClear (bolt->maxs); ! 2234: ! 2235: bolt->s.modelindex = gi.modelindex ("models/proj/disintegrator/tris.md2"); ! 2236: bolt->touch = tracker_touch; ! 2237: bolt->enemy = enemy; ! 2238: bolt->owner = self; ! 2239: bolt->dmg = damage; ! 2240: bolt->classname = "tracker"; ! 2241: gi.linkentity (bolt); ! 2242: ! 2243: if(enemy) ! 2244: { ! 2245: bolt->nextthink = level.time + 0.1; ! 2246: bolt->think = tracker_fly; ! 2247: } ! 2248: else ! 2249: { ! 2250: bolt->nextthink = level.time + 10; ! 2251: bolt->think = G_FreeEdict; ! 2252: } ! 2253: ! 2254: if (self->client) ! 2255: check_dodge (self, bolt->s.origin, dir, speed); ! 2256: ! 2257: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); ! 2258: if (tr.fraction < 1.0) ! 2259: { ! 2260: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); ! 2261: bolt->touch (bolt, tr.ent, NULL, NULL); ! 2262: } ! 2263: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.