|
|
1.1 ! root 1: // g_combat.c ! 2: ! 3: #include "g_local.h" ! 4: ! 5: void M_SetEffects (edict_t *self); ! 6: ! 7: /* ! 8: ROGUE ! 9: clean up heal targets for medic ! 10: */ ! 11: void cleanupHealTarget (edict_t *ent) ! 12: { ! 13: ent->monsterinfo.healer = NULL; ! 14: ent->takedamage = DAMAGE_YES; ! 15: ent->monsterinfo.aiflags &= ~AI_RESURRECTING; ! 16: M_SetEffects (ent); ! 17: } ! 18: /* ! 19: ============ ! 20: CanDamage ! 21: ! 22: Returns true if the inflictor can directly damage the target. Used for ! 23: explosions and melee attacks. ! 24: ============ ! 25: */ ! 26: qboolean CanDamage (edict_t *targ, edict_t *inflictor) ! 27: { ! 28: vec3_t dest; ! 29: trace_t trace; ! 30: ! 31: // bmodels need special checking because their origin is 0,0,0 ! 32: if (targ->movetype == MOVETYPE_PUSH) ! 33: { ! 34: VectorAdd (targ->absmin, targ->absmax, dest); ! 35: VectorScale (dest, 0.5, dest); ! 36: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); ! 37: if (trace.fraction == 1.0) ! 38: return true; ! 39: if (trace.ent == targ) ! 40: return true; ! 41: return false; ! 42: } ! 43: ! 44: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID); ! 45: if (trace.fraction == 1.0) ! 46: return true; ! 47: ! 48: VectorCopy (targ->s.origin, dest); ! 49: dest[0] += 15.0; ! 50: dest[1] += 15.0; ! 51: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); ! 52: if (trace.fraction == 1.0) ! 53: return true; ! 54: ! 55: VectorCopy (targ->s.origin, dest); ! 56: dest[0] += 15.0; ! 57: dest[1] -= 15.0; ! 58: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); ! 59: if (trace.fraction == 1.0) ! 60: return true; ! 61: ! 62: VectorCopy (targ->s.origin, dest); ! 63: dest[0] -= 15.0; ! 64: dest[1] += 15.0; ! 65: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); ! 66: if (trace.fraction == 1.0) ! 67: return true; ! 68: ! 69: VectorCopy (targ->s.origin, dest); ! 70: dest[0] -= 15.0; ! 71: dest[1] -= 15.0; ! 72: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); ! 73: if (trace.fraction == 1.0) ! 74: return true; ! 75: ! 76: ! 77: return false; ! 78: } ! 79: ! 80: ! 81: /* ! 82: ============ ! 83: Killed ! 84: ============ ! 85: */ ! 86: void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 87: { ! 88: if (targ->health < -999) ! 89: targ->health = -999; ! 90: ! 91: if (targ->monsterinfo.aiflags & AI_MEDIC) ! 92: { ! 93: if (targ->enemy) // god, I hope so ! 94: { ! 95: cleanupHealTarget (targ->enemy); ! 96: } ! 97: ! 98: // clean up self ! 99: targ->monsterinfo.aiflags &= ~AI_MEDIC; ! 100: targ->enemy = attacker; ! 101: } ! 102: else ! 103: { ! 104: targ->enemy = attacker; ! 105: } ! 106: ! 107: if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) ! 108: { ! 109: // targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type ! 110: //ROGUE - free up slot for spawned monster if it's spawned ! 111: if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER) ! 112: { ! 113: if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse && ! 114: !strcmp(targ->monsterinfo.commander->classname, "monster_carrier")) ! 115: { ! 116: targ->monsterinfo.commander->monsterinfo.monster_slots++; ! 117: // if ((g_showlogic) && (g_showlogic->value)) ! 118: // gi.dprintf ("g_combat: freeing up carrier slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots); ! 119: } ! 120: } ! 121: if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C) ! 122: { ! 123: if (targ->monsterinfo.commander) ! 124: { ! 125: if (targ->monsterinfo.commander->inuse && !strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander")) ! 126: { ! 127: targ->monsterinfo.commander->monsterinfo.monster_slots++; ! 128: // if ((g_showlogic) && (g_showlogic->value)) ! 129: // gi.dprintf ("g_combat: freeing up medic slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots); ! 130: } ! 131: else ! 132: if ((g_showlogic) && (g_showlogic->value)) ! 133: gi.dprintf ("my commander is dead! he's a %s\n", targ->monsterinfo.commander->classname); ! 134: } ! 135: else if ((g_showlogic) && (g_showlogic->value)) ! 136: gi.dprintf ("My commander is GONE\n"); ! 137: ! 138: } ! 139: if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW) ! 140: { ! 141: // need to check this because we can have variable numbers of coop players ! 142: if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse && ! 143: !strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13)) ! 144: { ! 145: if (targ->monsterinfo.commander->monsterinfo.monster_used > 0) ! 146: targ->monsterinfo.commander->monsterinfo.monster_used--; ! 147: // if ((g_showlogic) && (g_showlogic->value)) ! 148: // gi.dprintf ("g_combat: freeing up black widow slot - %d used\n", targ->monsterinfo.commander->monsterinfo.monster_used); ! 149: } ! 150: } ! 151: //rogue ! 152: if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT))) ! 153: { ! 154: level.killed_monsters++; ! 155: if (coop->value && attacker->client) ! 156: attacker->client->resp.score++; ! 157: // medics won't heal monsters that they kill themselves ! 158: // PMM - now they will ! 159: // if (strcmp(attacker->classname, "monster_medic") == 0) ! 160: // targ->owner = attacker; ! 161: } ! 162: } ! 163: ! 164: if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE) ! 165: { // doors, triggers, etc ! 166: targ->die (targ, inflictor, attacker, damage, point); ! 167: return; ! 168: } ! 169: ! 170: if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) ! 171: { ! 172: targ->touch = NULL; ! 173: monster_death_use (targ); ! 174: } ! 175: ! 176: targ->die (targ, inflictor, attacker, damage, point); ! 177: } ! 178: ! 179: ! 180: /* ! 181: ================ ! 182: SpawnDamage ! 183: ================ ! 184: */ ! 185: void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage) ! 186: { ! 187: if (damage > 255) ! 188: damage = 255; ! 189: gi.WriteByte (svc_temp_entity); ! 190: gi.WriteByte (type); ! 191: // gi.WriteByte (damage); ! 192: gi.WritePosition (origin); ! 193: gi.WriteDir (normal); ! 194: gi.multicast (origin, MULTICAST_PVS); ! 195: } ! 196: ! 197: ! 198: /* ! 199: ============ ! 200: T_Damage ! 201: ! 202: targ entity that is being damaged ! 203: inflictor entity that is causing the damage ! 204: attacker entity that caused the inflictor to damage targ ! 205: example: targ=monster, inflictor=rocket, attacker=player ! 206: ! 207: dir direction of the attack ! 208: point point at which the damage is being inflicted ! 209: normal normal vector from that point ! 210: damage amount of damage being inflicted ! 211: knockback force to be applied against targ as a result of the damage ! 212: ! 213: dflags these flags are used to control how T_Damage works ! 214: DAMAGE_RADIUS damage was indirect (from a nearby explosion) ! 215: DAMAGE_NO_ARMOR armor does not protect from this damage ! 216: DAMAGE_ENERGY damage is from an energy based weapon ! 217: DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles ! 218: DAMAGE_BULLET damage is from a bullet (used for ricochets) ! 219: DAMAGE_NO_PROTECTION kills godmode, armor, everything ! 220: ============ ! 221: */ ! 222: static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags) ! 223: { ! 224: gclient_t *client; ! 225: int save; ! 226: int power_armor_type; ! 227: int index; ! 228: int damagePerCell; ! 229: int pa_te_type; ! 230: int power; ! 231: int power_used; ! 232: ! 233: if (!damage) ! 234: return 0; ! 235: ! 236: client = ent->client; ! 237: ! 238: if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR)) // PGM ! 239: return 0; ! 240: ! 241: if (client) ! 242: { ! 243: power_armor_type = PowerArmorType (ent); ! 244: if (power_armor_type != POWER_ARMOR_NONE) ! 245: { ! 246: index = ITEM_INDEX(FindItem("Cells")); ! 247: power = client->pers.inventory[index]; ! 248: } ! 249: } ! 250: else if (ent->svflags & SVF_MONSTER) ! 251: { ! 252: power_armor_type = ent->monsterinfo.power_armor_type; ! 253: power = ent->monsterinfo.power_armor_power; ! 254: } ! 255: else ! 256: return 0; ! 257: ! 258: if (power_armor_type == POWER_ARMOR_NONE) ! 259: return 0; ! 260: if (!power) ! 261: return 0; ! 262: ! 263: if (power_armor_type == POWER_ARMOR_SCREEN) ! 264: { ! 265: vec3_t vec; ! 266: float dot; ! 267: vec3_t forward; ! 268: ! 269: // only works if damage point is in front ! 270: AngleVectors (ent->s.angles, forward, NULL, NULL); ! 271: VectorSubtract (point, ent->s.origin, vec); ! 272: VectorNormalize (vec); ! 273: dot = DotProduct (vec, forward); ! 274: if (dot <= 0.3) ! 275: return 0; ! 276: ! 277: damagePerCell = 1; ! 278: pa_te_type = TE_SCREEN_SPARKS; ! 279: damage = damage / 3; ! 280: } ! 281: else ! 282: { ! 283: damagePerCell = 2; ! 284: pa_te_type = TE_SHIELD_SPARKS; ! 285: damage = (2 * damage) / 3; ! 286: } ! 287: ! 288: // etf rifle ! 289: if (dflags & DAMAGE_NO_REG_ARMOR) ! 290: save = (power * damagePerCell) / 2; ! 291: else ! 292: save = power * damagePerCell; ! 293: ! 294: if (!save) ! 295: return 0; ! 296: if (save > damage) ! 297: save = damage; ! 298: ! 299: SpawnDamage (pa_te_type, point, normal, save); ! 300: ent->powerarmor_time = level.time + 0.2; ! 301: ! 302: if (dflags & DAMAGE_NO_REG_ARMOR) ! 303: power_used = (save / damagePerCell) * 2; ! 304: else ! 305: power_used = save / damagePerCell; ! 306: ! 307: if (client) ! 308: client->pers.inventory[index] -= power_used; ! 309: else ! 310: ent->monsterinfo.power_armor_power -= power_used; ! 311: return save; ! 312: } ! 313: ! 314: static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags) ! 315: { ! 316: gclient_t *client; ! 317: int save; ! 318: int index; ! 319: gitem_t *armor; ! 320: ! 321: if (!damage) ! 322: return 0; ! 323: ! 324: client = ent->client; ! 325: ! 326: if (!client) ! 327: return 0; ! 328: ! 329: // ROGUE - added DAMAGE_NO_REG_ARMOR for atf rifle ! 330: if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR)) ! 331: return 0; ! 332: ! 333: index = ArmorIndex (ent); ! 334: if (!index) ! 335: return 0; ! 336: ! 337: armor = GetItemByIndex (index); ! 338: ! 339: if (dflags & DAMAGE_ENERGY) ! 340: save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage); ! 341: else ! 342: save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage); ! 343: if (save >= client->pers.inventory[index]) ! 344: save = client->pers.inventory[index]; ! 345: ! 346: if (!save) ! 347: return 0; ! 348: ! 349: client->pers.inventory[index] -= save; ! 350: SpawnDamage (te_sparks, point, normal, save); ! 351: ! 352: return save; ! 353: } ! 354: ! 355: void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor) ! 356: { ! 357: // pmm ! 358: qboolean new_tesla; ! 359: ! 360: if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) ! 361: return; ! 362: ! 363: //======= ! 364: //ROGUE ! 365: // logic for tesla - if you are hit by a tesla, and can't see who you should be mad at (attacker) ! 366: // attack the tesla ! 367: // also, target the tesla if it's a "new" tesla ! 368: if ((inflictor) && (!strcmp(inflictor->classname, "tesla"))) ! 369: { ! 370: new_tesla = MarkTeslaArea(targ, inflictor); ! 371: if (new_tesla) ! 372: TargetTesla (targ, inflictor); ! 373: return; ! 374: // FIXME - just ignore teslas when you're TARGET_ANGER or MEDIC ! 375: /* if (!(targ->enemy && (targ->monsterinfo.aiflags & (AI_TARGET_ANGER|AI_MEDIC)))) ! 376: { ! 377: // FIXME - coop issues? ! 378: if ((!targ->enemy) || (!visible(targ, targ->enemy))) ! 379: { ! 380: gi.dprintf ("can't see player, switching to tesla\n"); ! 381: TargetTesla (targ, inflictor); ! 382: return; ! 383: } ! 384: gi.dprintf ("can see player, ignoring tesla\n"); ! 385: } ! 386: else if ((g_showlogic) && (g_showlogic->value)) ! 387: gi.dprintf ("no enemy, or I'm doing other, more important things, than worrying about a damned tesla!\n"); ! 388: */ ! 389: } ! 390: //ROGUE ! 391: //======= ! 392: ! 393: if (attacker == targ || attacker == targ->enemy) ! 394: return; ! 395: ! 396: // if we are a good guy monster and our attacker is a player ! 397: // or another good guy, do not get mad at them ! 398: if (targ->monsterinfo.aiflags & AI_GOOD_GUY) ! 399: { ! 400: if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) ! 401: return; ! 402: } ! 403: ! 404: //PGM ! 405: // if we're currently mad at something a target_anger made us mad at, ignore ! 406: // damage ! 407: if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER) ! 408: { ! 409: float percentHealth; ! 410: ! 411: // make sure whatever we were pissed at is still around. ! 412: if(targ->enemy->inuse) ! 413: { ! 414: percentHealth = (float)(targ->health) / (float)(targ->max_health); ! 415: if( targ->enemy->inuse && percentHealth > 0.33) ! 416: return; ! 417: } ! 418: ! 419: // remove the target anger flag ! 420: targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER; ! 421: } ! 422: //PGM ! 423: ! 424: // PMM ! 425: // if we're healing someone, do like above and try to stay with them ! 426: if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC)) ! 427: { ! 428: float percentHealth; ! 429: ! 430: percentHealth = (float)(targ->health) / (float)(targ->max_health); ! 431: // ignore it some of the time ! 432: if( targ->enemy->inuse && percentHealth > 0.25) ! 433: return; ! 434: ! 435: // remove the medic flag ! 436: targ->monsterinfo.aiflags &= ~AI_MEDIC; ! 437: cleanupHealTarget (targ->enemy); ! 438: } ! 439: // PMM ! 440: ! 441: // we now know that we are not both good guys ! 442: ! 443: // if attacker is a client, get mad at them because he's good and we're not ! 444: if (attacker->client) ! 445: { ! 446: targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET; ! 447: ! 448: // this can only happen in coop (both new and old enemies are clients) ! 449: // only switch if can't see the current enemy ! 450: if (targ->enemy && targ->enemy->client) ! 451: { ! 452: if (visible(targ, targ->enemy)) ! 453: { ! 454: targ->oldenemy = attacker; ! 455: return; ! 456: } ! 457: targ->oldenemy = targ->enemy; ! 458: } ! 459: targ->enemy = attacker; ! 460: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) ! 461: FoundTarget (targ); ! 462: return; ! 463: } ! 464: ! 465: // it's the same base (walk/swim/fly) type and a different classname and it's not a tank ! 466: // (they spray too much), get mad at them ! 467: // PMM ! 468: // added medics to this ! 469: // FIXME - ! 470: // this really should be turned into an AI flag marking appropriate monsters as "don't shoot me" ! 471: // this also leads to the problem of tanks and medics being able to, at will, kill monsters with ! 472: // no chance of retaliation. My vote is to make those monsters who are designed as "don't shoot me" ! 473: // such that they also ignore being shot by monsters as well ! 474: /* ! 475: if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) && ! 476: (strcmp (targ->classname, attacker->classname) != 0) && ! 477: (strcmp(attacker->classname, "monster_tank") != 0) && ! 478: (strcmp(attacker->classname, "monster_supertank") != 0) && ! 479: (strcmp(attacker->classname, "monster_makron") != 0) && ! 480: (strcmp(attacker->classname, "monster_jorg") != 0) && ! 481: (strcmp(attacker->classname, "monster_carrier") != 0) && ! 482: (strncmp(attacker->classname, "monster_medic", 12) != 0) ) // this should get medics & medic_commanders ! 483: */ ! 484: if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) && ! 485: (strcmp (targ->classname, attacker->classname) != 0) && ! 486: !(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) && ! 487: !(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS) ) ! 488: { ! 489: if (targ->enemy && targ->enemy->client) ! 490: targ->oldenemy = targ->enemy; ! 491: targ->enemy = attacker; ! 492: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) ! 493: FoundTarget (targ); ! 494: } ! 495: // if they *meant* to shoot us, then shoot back ! 496: else if (attacker->enemy == targ) ! 497: { ! 498: if (targ->enemy && targ->enemy->client) ! 499: targ->oldenemy = targ->enemy; ! 500: targ->enemy = attacker; ! 501: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) ! 502: FoundTarget (targ); ! 503: } ! 504: // otherwise get mad at whoever they are mad at (help our buddy) unless it is us! ! 505: else if (attacker->enemy && attacker->enemy != targ) ! 506: { ! 507: if (targ->enemy && targ->enemy->client) ! 508: targ->oldenemy = targ->enemy; ! 509: targ->enemy = attacker->enemy; ! 510: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) ! 511: FoundTarget (targ); ! 512: } ! 513: } ! 514: ! 515: qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker) ! 516: { ! 517: //FIXME make the next line real and uncomment this block ! 518: // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team)) ! 519: return false; ! 520: } ! 521: ! 522: void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) ! 523: { ! 524: gclient_t *client; ! 525: int take; ! 526: int save; ! 527: int asave; ! 528: int psave; ! 529: int te_sparks; ! 530: int sphere_notified; // PGM ! 531: ! 532: if (!targ->takedamage) ! 533: return; ! 534: ! 535: sphere_notified = false; // PGM ! 536: ! 537: // friendly fire avoidance ! 538: // if enabled you can't hurt teammates (but you can hurt yourself) ! 539: // knockback still occurs ! 540: if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) ! 541: { ! 542: if (OnSameTeam (targ, attacker)) ! 543: { ! 544: // PMM - nukes kill everyone ! 545: if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && (mod != MOD_NUKE)) ! 546: damage = 0; ! 547: else ! 548: mod |= MOD_FRIENDLY_FIRE; ! 549: } ! 550: } ! 551: meansOfDeath = mod; ! 552: ! 553: //ROGUE ! 554: // allow the deathmatch game to change values ! 555: if (deathmatch->value && gamerules && gamerules->value) ! 556: { ! 557: if(DMGame.ChangeDamage) ! 558: damage = DMGame.ChangeDamage(targ, attacker, damage, mod); ! 559: if(DMGame.ChangeKnockback) ! 560: knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod); ! 561: ! 562: if(!damage) ! 563: return; ! 564: } ! 565: //ROGUE ! 566: ! 567: // easy mode takes half damage ! 568: if (skill->value == 0 && deathmatch->value == 0 && targ->client) ! 569: { ! 570: damage *= 0.5; ! 571: if (!damage) ! 572: damage = 1; ! 573: } ! 574: ! 575: client = targ->client; ! 576: ! 577: // PMM - defender sphere takes half damage ! 578: if ((client) && (client->owned_sphere) && (client->owned_sphere->spawnflags == 1)) ! 579: { ! 580: damage *= 0.5; ! 581: if (!damage) ! 582: damage = 1; ! 583: } ! 584: ! 585: if (dflags & DAMAGE_BULLET) ! 586: te_sparks = TE_BULLET_SPARKS; ! 587: else ! 588: te_sparks = TE_SPARKS; ! 589: ! 590: VectorNormalize(dir); ! 591: ! 592: // bonus damage for suprising a monster ! 593: if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) ! 594: damage *= 2; ! 595: ! 596: if (targ->flags & FL_NO_KNOCKBACK) ! 597: knockback = 0; ! 598: ! 599: // figure momentum add ! 600: if (!(dflags & DAMAGE_NO_KNOCKBACK)) ! 601: { ! 602: if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) ! 603: { ! 604: vec3_t kvel; ! 605: float mass; ! 606: ! 607: if (targ->mass < 50) ! 608: mass = 50; ! 609: else ! 610: mass = targ->mass; ! 611: ! 612: if (targ->client && attacker == targ) ! 613: VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... ! 614: else ! 615: VectorScale (dir, 500.0 * (float)knockback / mass, kvel); ! 616: ! 617: VectorAdd (targ->velocity, kvel, targ->velocity); ! 618: } ! 619: } ! 620: ! 621: take = damage; ! 622: save = 0; ! 623: ! 624: // check for godmode ! 625: if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) ! 626: { ! 627: take = 0; ! 628: save = damage; ! 629: SpawnDamage (te_sparks, point, normal, save); ! 630: } ! 631: ! 632: // check for invincibility ! 633: if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) ! 634: { ! 635: if (targ->pain_debounce_time < level.time) ! 636: { ! 637: gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); ! 638: targ->pain_debounce_time = level.time + 2; ! 639: } ! 640: take = 0; ! 641: save = damage; ! 642: } ! 643: // ROGUE ! 644: // check for monster invincibility ! 645: if (((targ->svflags & SVF_MONSTER) && targ->monsterinfo.invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) ! 646: { ! 647: if (targ->pain_debounce_time < level.time) ! 648: { ! 649: gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); ! 650: targ->pain_debounce_time = level.time + 2; ! 651: } ! 652: take = 0; ! 653: save = damage; ! 654: } ! 655: // ROGUE ! 656: ! 657: psave = CheckPowerArmor (targ, point, normal, take, dflags); ! 658: take -= psave; ! 659: ! 660: asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); ! 661: take -= asave; ! 662: ! 663: //treat cheat/powerup savings the same as armor ! 664: asave += save; ! 665: ! 666: // team damage avoidance ! 667: if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) ! 668: return; ! 669: ! 670: // ROGUE - this option will do damage both to the armor and person. originally for DPU rounds ! 671: if (dflags & DAMAGE_DESTROY_ARMOR) ! 672: { ! 673: if(!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) && ! 674: !(client && client->invincible_framenum > level.framenum)) ! 675: { ! 676: take = damage; ! 677: } ! 678: } ! 679: // ROGUE ! 680: ! 681: // do the damage ! 682: if (take) ! 683: { ! 684: //PGM need more blood for chainfist. ! 685: if(targ->flags & FL_MECHANICAL) ! 686: { ! 687: SpawnDamage ( TE_ELECTRIC_SPARKS, point, normal, take); ! 688: } ! 689: else if ((targ->svflags & SVF_MONSTER) || (client)) ! 690: { ! 691: if(mod == MOD_CHAINFIST) ! 692: SpawnDamage (TE_MOREBLOOD, point, normal, 255); ! 693: else ! 694: SpawnDamage (TE_BLOOD, point, normal, take); ! 695: } ! 696: else ! 697: SpawnDamage (te_sparks, point, normal, take); ! 698: //PGM ! 699: ! 700: targ->health = targ->health - take; ! 701: ! 702: //PGM - spheres need to know who to shoot at ! 703: if(client && client->owned_sphere) ! 704: { ! 705: sphere_notified = true; ! 706: if(client->owned_sphere->pain) ! 707: client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0); ! 708: } ! 709: //PGM ! 710: ! 711: if (targ->health <= 0) ! 712: { ! 713: if ((targ->svflags & SVF_MONSTER) || (client)) ! 714: targ->flags |= FL_NO_KNOCKBACK; ! 715: Killed (targ, inflictor, attacker, take, point); ! 716: return; ! 717: } ! 718: } ! 719: ! 720: //PGM - spheres need to know who to shoot at ! 721: if (!sphere_notified) ! 722: { ! 723: if(client && client->owned_sphere) ! 724: { ! 725: sphere_notified = true; ! 726: if(client->owned_sphere->pain) ! 727: client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0); ! 728: } ! 729: } ! 730: //PGM ! 731: ! 732: if (targ->svflags & SVF_MONSTER) ! 733: { ! 734: M_ReactToDamage (targ, attacker, inflictor); ! 735: // PMM - fixme - if anyone else but the medic ever uses AI_MEDIC, check for it here instead ! 736: // of in the medic's pain function ! 737: if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) ! 738: { ! 739: targ->pain (targ, attacker, knockback, take); ! 740: // nightmare mode monsters don't go into pain frames often ! 741: if (skill->value == 3) ! 742: targ->pain_debounce_time = level.time + 5; ! 743: } ! 744: } ! 745: else if (client) ! 746: { ! 747: if (!(targ->flags & FL_GODMODE) && (take)) ! 748: targ->pain (targ, attacker, knockback, take); ! 749: } ! 750: else if (take) ! 751: { ! 752: if (targ->pain) ! 753: targ->pain (targ, attacker, knockback, take); ! 754: } ! 755: ! 756: // add to the damage inflicted on a player this frame ! 757: // the total will be turned into screen blends and view angle kicks ! 758: // at the end of the frame ! 759: if (client) ! 760: { ! 761: client->damage_parmor += psave; ! 762: client->damage_armor += asave; ! 763: client->damage_blood += take; ! 764: client->damage_knockback += knockback; ! 765: VectorCopy (point, client->damage_from); ! 766: } ! 767: } ! 768: ! 769: ! 770: /* ! 771: ============ ! 772: T_RadiusDamage ! 773: ============ ! 774: */ ! 775: void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) ! 776: { ! 777: float points; ! 778: edict_t *ent = NULL; ! 779: vec3_t v; ! 780: vec3_t dir; ! 781: ! 782: while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) ! 783: { ! 784: if (ent == ignore) ! 785: continue; ! 786: if (!ent->takedamage) ! 787: continue; ! 788: ! 789: VectorAdd (ent->mins, ent->maxs, v); ! 790: VectorMA (ent->s.origin, 0.5, v, v); ! 791: VectorSubtract (inflictor->s.origin, v, v); ! 792: points = damage - 0.5 * VectorLength (v); ! 793: if (ent == attacker) ! 794: points = points * 0.5; ! 795: if (points > 0) ! 796: { ! 797: if (CanDamage (ent, inflictor)) ! 798: { ! 799: VectorSubtract (ent->s.origin, inflictor->s.origin, dir); ! 800: T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); ! 801: } ! 802: } ! 803: } ! 804: } ! 805: ! 806: // ********************** ! 807: // ROGUE ! 808: ! 809: /* ! 810: ============ ! 811: T_RadiusNukeDamage ! 812: ! 813: Like T_RadiusDamage, but ignores walls (skips CanDamage check, among others) ! 814: // up to KILLZONE radius, do 10,000 points ! 815: // after that, do damage linearly out to KILLZONE2 radius ! 816: ============ ! 817: */ ! 818: ! 819: void T_RadiusNukeDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) ! 820: { ! 821: float points; ! 822: edict_t *ent = NULL; ! 823: vec3_t v; ! 824: vec3_t dir; ! 825: float len; ! 826: float killzone, killzone2; ! 827: trace_t tr; ! 828: float dist; ! 829: ! 830: killzone = radius; ! 831: killzone2 = radius*2.0; ! 832: ! 833: while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL) ! 834: { ! 835: // ignore nobody ! 836: if (ent == ignore) ! 837: continue; ! 838: if (!ent->takedamage) ! 839: continue; ! 840: if (!ent->inuse) ! 841: continue; ! 842: if (!(ent->client || (ent->svflags & SVF_MONSTER) || (ent->svflags & SVF_DAMAGEABLE))) ! 843: continue; ! 844: ! 845: VectorAdd (ent->mins, ent->maxs, v); ! 846: VectorMA (ent->s.origin, 0.5, v, v); ! 847: VectorSubtract (inflictor->s.origin, v, v); ! 848: len = VectorLength(v); ! 849: if (len <= killzone) ! 850: { ! 851: if (ent->client) ! 852: ent->flags |= FL_NOGIB; ! 853: points = 10000; ! 854: } ! 855: else if (len <= killzone2) ! 856: points = (damage/killzone)*(killzone2 - len); ! 857: else ! 858: points = 0; ! 859: // points = damage - 0.005 * len*len; ! 860: // if (ent == attacker) ! 861: // points = points * 0.5; ! 862: if ((g_showlogic) && (g_showlogic->value)) ! 863: { ! 864: if (!(strcmp(ent->classname, "player"))) ! 865: gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, inflictor->teammaster->client->pers.netname); ! 866: else ! 867: gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, ent->classname); ! 868: } ! 869: if (points > 0) ! 870: { ! 871: // if (CanDamage (ent, inflictor)) ! 872: // { ! 873: if (ent->client) ! 874: ent->client->nuke_framenum = level.framenum + 20; ! 875: VectorSubtract (ent->s.origin, inflictor->s.origin, dir); ! 876: T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); ! 877: // } ! 878: } ! 879: } ! 880: ent = g_edicts+1; // skip the worldspawn ! 881: // cycle through players ! 882: while (ent) ! 883: { ! 884: if ((ent->client) && (ent->client->nuke_framenum != level.framenum+20) && (ent->inuse)) ! 885: { ! 886: tr = gi.trace (inflictor->s.origin, NULL, NULL, ent->s.origin, inflictor, MASK_SOLID); ! 887: if (tr.fraction == 1.0) ! 888: { ! 889: if ((g_showlogic) && (g_showlogic->value)) ! 890: gi.dprintf ("Undamaged player in LOS with nuke, flashing!\n"); ! 891: ent->client->nuke_framenum = level.framenum + 20; ! 892: } ! 893: else ! 894: { ! 895: dist = realrange (ent, inflictor); ! 896: if (dist < 2048) ! 897: ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 15); ! 898: else ! 899: ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 10); ! 900: } ! 901: ent++; ! 902: } ! 903: else ! 904: ent = NULL; ! 905: } ! 906: } ! 907: ! 908: /* ! 909: ============ ! 910: T_RadiusClassDamage ! 911: ! 912: Like T_RadiusDamage, but ignores anything with classname=ignoreClass ! 913: ============ ! 914: */ ! 915: void T_RadiusClassDamage (edict_t *inflictor, edict_t *attacker, float damage, char *ignoreClass, float radius, int mod) ! 916: { ! 917: float points; ! 918: edict_t *ent = NULL; ! 919: vec3_t v; ! 920: vec3_t dir; ! 921: ! 922: while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) ! 923: { ! 924: if (ent->classname && !strcmp(ent->classname, ignoreClass)) ! 925: continue; ! 926: if (!ent->takedamage) ! 927: continue; ! 928: ! 929: VectorAdd (ent->mins, ent->maxs, v); ! 930: VectorMA (ent->s.origin, 0.5, v, v); ! 931: VectorSubtract (inflictor->s.origin, v, v); ! 932: points = damage - 0.5 * VectorLength (v); ! 933: if (ent == attacker) ! 934: points = points * 0.5; ! 935: if (points > 0) ! 936: { ! 937: if (CanDamage (ent, inflictor)) ! 938: { ! 939: VectorSubtract (ent->s.origin, inflictor->s.origin, dir); ! 940: T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); ! 941: } ! 942: } ! 943: } ! 944: } ! 945: ! 946: // ROGUE ! 947: // ********************
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.