|
|
1.1.1.2 ! root 1: /* ! 2: Copyright (C) 1997-2001 Id Software, Inc. ! 3: ! 4: This program is free software; you can redistribute it and/or ! 5: modify it under the terms of the GNU General Public License ! 6: as published by the Free Software Foundation; either version 2 ! 7: of the License, or (at your option) any later version. ! 8: ! 9: This program is distributed in the hope that it will be useful, ! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ! 12: ! 13: See the GNU General Public License for more details. ! 14: ! 15: You should have received a copy of the GNU General Public License ! 16: along with this program; if not, write to the Free Software ! 17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ! 18: ! 19: */ 1.1 root 20: // g_combat.c 21: 22: #include "g_local.h" 23: 24: /* 25: ============ 26: CanDamage 27: 28: Returns true if the inflictor can directly damage the target. Used for 29: explosions and melee attacks. 30: ============ 31: */ 32: qboolean CanDamage (edict_t *targ, edict_t *inflictor) 33: { 34: vec3_t dest; 35: trace_t trace; 36: 37: // bmodels need special checking because their origin is 0,0,0 38: if (targ->movetype == MOVETYPE_PUSH) 39: { 40: VectorAdd (targ->absmin, targ->absmax, dest); 41: VectorScale (dest, 0.5, dest); 42: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); 43: if (trace.fraction == 1.0) 44: return true; 45: if (trace.ent == targ) 46: return true; 47: return false; 48: } 49: 50: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID); 51: if (trace.fraction == 1.0) 52: return true; 53: 54: VectorCopy (targ->s.origin, dest); 55: dest[0] += 15.0; 56: dest[1] += 15.0; 57: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); 58: if (trace.fraction == 1.0) 59: return true; 60: 61: VectorCopy (targ->s.origin, dest); 62: dest[0] += 15.0; 63: dest[1] -= 15.0; 64: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); 65: if (trace.fraction == 1.0) 66: return true; 67: 68: VectorCopy (targ->s.origin, dest); 69: dest[0] -= 15.0; 70: dest[1] += 15.0; 71: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); 72: if (trace.fraction == 1.0) 73: return true; 74: 75: VectorCopy (targ->s.origin, dest); 76: dest[0] -= 15.0; 77: dest[1] -= 15.0; 78: trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); 79: if (trace.fraction == 1.0) 80: return true; 81: 82: 83: return false; 84: } 85: 86: 87: /* 88: ============ 89: Killed 90: ============ 91: */ 92: void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) 93: { 94: if (targ->health < -999) 95: targ->health = -999; 96: 97: targ->enemy = attacker; 98: 99: if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) 100: { 101: // targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type 102: if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) 103: { 104: level.killed_monsters++; 105: if (coop->value && attacker->client) 106: attacker->client->resp.score++; 107: // medics won't heal monsters that they kill themselves 108: if (strcmp(attacker->classname, "monster_medic") == 0) 109: targ->owner = attacker; 110: } 111: } 112: 113: if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE) 114: { // doors, triggers, etc 115: targ->die (targ, inflictor, attacker, damage, point); 116: return; 117: } 118: 119: if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) 120: { 121: targ->touch = NULL; 122: monster_death_use (targ); 123: } 124: 125: targ->die (targ, inflictor, attacker, damage, point); 126: } 127: 128: 129: /* 130: ================ 131: SpawnDamage 132: ================ 133: */ 134: void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage) 135: { 136: if (damage > 255) 137: damage = 255; 138: gi.WriteByte (svc_temp_entity); 139: gi.WriteByte (type); 140: // gi.WriteByte (damage); 141: gi.WritePosition (origin); 142: gi.WriteDir (normal); 143: gi.multicast (origin, MULTICAST_PVS); 144: } 145: 146: 147: /* 148: ============ 149: T_Damage 150: 151: targ entity that is being damaged 152: inflictor entity that is causing the damage 153: attacker entity that caused the inflictor to damage targ 154: example: targ=monster, inflictor=rocket, attacker=player 155: 156: dir direction of the attack 157: point point at which the damage is being inflicted 158: normal normal vector from that point 159: damage amount of damage being inflicted 160: knockback force to be applied against targ as a result of the damage 161: 162: dflags these flags are used to control how T_Damage works 163: DAMAGE_RADIUS damage was indirect (from a nearby explosion) 164: DAMAGE_NO_ARMOR armor does not protect from this damage 165: DAMAGE_ENERGY damage is from an energy based weapon 166: DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles 167: DAMAGE_BULLET damage is from a bullet (used for ricochets) 168: DAMAGE_NO_PROTECTION kills godmode, armor, everything 169: ============ 170: */ 171: static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags) 172: { 173: gclient_t *client; 174: int save; 175: int power_armor_type; 176: int index; 177: int damagePerCell; 178: int pa_te_type; 179: int power; 180: int power_used; 181: 182: if (!damage) 183: return 0; 184: 185: client = ent->client; 186: 187: if (dflags & DAMAGE_NO_ARMOR) 188: return 0; 189: 190: if (client) 191: { 192: power_armor_type = PowerArmorType (ent); 193: if (power_armor_type != POWER_ARMOR_NONE) 194: { 195: index = ITEM_INDEX(FindItem("Cells")); 196: power = client->pers.inventory[index]; 197: } 198: } 199: else if (ent->svflags & SVF_MONSTER) 200: { 201: power_armor_type = ent->monsterinfo.power_armor_type; 202: power = ent->monsterinfo.power_armor_power; 203: } 204: else 205: return 0; 206: 207: if (power_armor_type == POWER_ARMOR_NONE) 208: return 0; 209: if (!power) 210: return 0; 211: 212: if (power_armor_type == POWER_ARMOR_SCREEN) 213: { 214: vec3_t vec; 215: float dot; 216: vec3_t forward; 217: 218: // only works if damage point is in front 219: AngleVectors (ent->s.angles, forward, NULL, NULL); 220: VectorSubtract (point, ent->s.origin, vec); 221: VectorNormalize (vec); 222: dot = DotProduct (vec, forward); 223: if (dot <= 0.3) 224: return 0; 225: 226: damagePerCell = 1; 227: pa_te_type = TE_SCREEN_SPARKS; 228: damage = damage / 3; 229: } 230: else 231: { 232: damagePerCell = 1; // power armor is weaker in CTF 233: pa_te_type = TE_SHIELD_SPARKS; 234: damage = (2 * damage) / 3; 235: } 236: 237: save = power * damagePerCell; 238: if (!save) 239: return 0; 240: if (save > damage) 241: save = damage; 242: 243: SpawnDamage (pa_te_type, point, normal, save); 244: ent->powerarmor_time = level.time + 0.2; 245: 246: power_used = save / damagePerCell; 247: 248: if (client) 249: client->pers.inventory[index] -= power_used; 250: else 251: ent->monsterinfo.power_armor_power -= power_used; 252: return save; 253: } 254: 255: static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags) 256: { 257: gclient_t *client; 258: int save; 259: int index; 260: gitem_t *armor; 261: 262: if (!damage) 263: return 0; 264: 265: client = ent->client; 266: 267: if (!client) 268: return 0; 269: 270: if (dflags & DAMAGE_NO_ARMOR) 271: return 0; 272: 273: index = ArmorIndex (ent); 274: if (!index) 275: return 0; 276: 277: armor = GetItemByIndex (index); 278: 279: if (dflags & DAMAGE_ENERGY) 280: save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage); 281: else 282: save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage); 283: if (save >= client->pers.inventory[index]) 284: save = client->pers.inventory[index]; 285: 286: if (!save) 287: return 0; 288: 289: client->pers.inventory[index] -= save; 290: SpawnDamage (te_sparks, point, normal, save); 291: 292: return save; 293: } 294: 295: void M_ReactToDamage (edict_t *targ, edict_t *attacker) 296: { 297: if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) 298: return; 299: 300: if (attacker == targ || attacker == targ->enemy) 301: return; 302: 303: // if we are a good guy monster and our attacker is a player 304: // or another good guy, do not get mad at them 305: if (targ->monsterinfo.aiflags & AI_GOOD_GUY) 306: { 307: if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) 308: return; 309: } 310: 311: // we now know that we are not both good guys 312: 313: // if attacker is a client, get mad at them because he's good and we're not 314: if (attacker->client) 315: { 316: // this can only happen in coop (both new and old enemies are clients) 317: // only switch if can't see the current enemy 318: if (targ->enemy && targ->enemy->client) 319: { 320: if (visible(targ, targ->enemy)) 321: { 322: targ->oldenemy = attacker; 323: return; 324: } 325: targ->oldenemy = targ->enemy; 326: } 327: targ->enemy = attacker; 328: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) 329: FoundTarget (targ); 330: return; 331: } 332: 333: // it's the same base (walk/swim/fly) type and a different classname and it's not a tank 334: // (they spray too much), get mad at them 335: if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) && 336: (strcmp (targ->classname, attacker->classname) != 0) && 337: (strcmp(attacker->classname, "monster_tank") != 0) && 338: (strcmp(attacker->classname, "monster_supertank") != 0) && 339: (strcmp(attacker->classname, "monster_makron") != 0) && 340: (strcmp(attacker->classname, "monster_jorg") != 0) ) 341: { 342: if (targ->enemy) 343: if (targ->enemy->client) 344: targ->oldenemy = targ->enemy; 345: targ->enemy = attacker; 346: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) 347: FoundTarget (targ); 348: } 349: else 350: // otherwise get mad at whoever they are mad at (help our buddy) 351: { 352: if (targ->enemy) 353: if (targ->enemy->client) 354: targ->oldenemy = targ->enemy; 355: targ->enemy = attacker->enemy; 356: FoundTarget (targ); 357: } 358: } 359: 360: qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker) 361: { 362: //ZOID 363: if (ctf->value && targ->client && attacker->client) 364: if (targ->client->resp.ctf_team == attacker->client->resp.ctf_team && 365: targ != attacker) 366: return true; 367: //ZOID 368: 369: //FIXME make the next line real and uncomment this block 370: // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team)) 371: return false; 372: } 373: 374: 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) 375: { 376: gclient_t *client; 377: int take; 378: int save; 379: int asave; 380: int psave; 381: int te_sparks; 382: 383: if (!targ->takedamage) 384: return; 385: 386: // friendly fire avoidance 387: // if enabled you can't hurt teammates (but you can hurt yourself) 388: // knockback still occurs 389: if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) 390: { 391: if (OnSameTeam (targ, attacker)) 392: { 393: if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) 394: damage = 0; 395: else 396: mod |= MOD_FRIENDLY_FIRE; 397: } 398: } 399: meansOfDeath = mod; 400: 401: // easy mode takes half damage 402: if (skill->value == 0 && deathmatch->value == 0 && targ->client) 403: { 404: damage *= 0.5; 405: if (!damage) 406: damage = 1; 407: } 408: 409: client = targ->client; 410: 411: if (dflags & DAMAGE_BULLET) 412: te_sparks = TE_BULLET_SPARKS; 413: else 414: te_sparks = TE_SPARKS; 415: 416: VectorNormalize(dir); 417: 418: // bonus damage for suprising a monster 419: if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) 420: damage *= 2; 421: 422: //ZOID 423: //strength tech 424: damage = CTFApplyStrength(attacker, damage); 425: //ZOID 426: 427: if (targ->flags & FL_NO_KNOCKBACK) 428: knockback = 0; 429: 430: // figure momentum add 431: if (!(dflags & DAMAGE_NO_KNOCKBACK)) 432: { 433: if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) 434: { 435: vec3_t kvel; 436: float mass; 437: 438: if (targ->mass < 50) 439: mass = 50; 440: else 441: mass = targ->mass; 442: 443: if (targ->client && attacker == targ) 444: VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... 445: else 446: VectorScale (dir, 500.0 * (float)knockback / mass, kvel); 447: 448: VectorAdd (targ->velocity, kvel, targ->velocity); 449: } 450: } 451: 452: take = damage; 453: save = 0; 454: 455: // check for godmode 456: if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) 457: { 458: take = 0; 459: save = damage; 460: SpawnDamage (te_sparks, point, normal, save); 461: } 462: 463: // check for invincibility 464: if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) 465: { 466: if (targ->pain_debounce_time < level.time) 467: { 468: gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); 469: targ->pain_debounce_time = level.time + 2; 470: } 471: take = 0; 472: save = damage; 473: } 474: 475: //ZOID 476: //team armor protect 477: if (ctf->value && targ->client && attacker->client && 478: targ->client->resp.ctf_team == attacker->client->resp.ctf_team && 479: targ != attacker && ((int)dmflags->value & DF_ARMOR_PROTECT)) { 480: psave = asave = 0; 481: } else { 482: //ZOID 483: psave = CheckPowerArmor (targ, point, normal, take, dflags); 484: take -= psave; 485: 486: asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); 487: take -= asave; 488: } 489: 490: //treat cheat/powerup savings the same as armor 491: asave += save; 492: 493: //ZOID 494: //resistance tech 495: take = CTFApplyResistance(targ, take); 496: //ZOID 497: 498: // team damage avoidance 499: if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) 500: return; 501: 502: //ZOID 503: CTFCheckHurtCarrier(targ, attacker); 504: //ZOID 505: 506: // do the damage 507: if (take) 508: { 509: if ((targ->svflags & SVF_MONSTER) || (client)) 510: SpawnDamage (TE_BLOOD, point, normal, take); 511: else 512: SpawnDamage (te_sparks, point, normal, take); 513: 514: if (!CTFMatchSetup()) 515: targ->health = targ->health - take; 516: 517: if (targ->health <= 0) 518: { 519: if ((targ->svflags & SVF_MONSTER) || (client)) 520: targ->flags |= FL_NO_KNOCKBACK; 521: Killed (targ, inflictor, attacker, take, point); 522: return; 523: } 524: } 525: 526: if (targ->svflags & SVF_MONSTER) 527: { 528: M_ReactToDamage (targ, attacker); 529: if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) 530: { 531: targ->pain (targ, attacker, knockback, take); 532: // nightmare mode monsters don't go into pain frames often 533: if (skill->value == 3) 534: targ->pain_debounce_time = level.time + 5; 535: } 536: } 537: else if (client) 538: { 539: if (!(targ->flags & FL_GODMODE) && (take) && !CTFMatchSetup()) 540: targ->pain (targ, attacker, knockback, take); 541: } 542: else if (take) 543: { 544: if (targ->pain) 545: targ->pain (targ, attacker, knockback, take); 546: } 547: 548: // add to the damage inflicted on a player this frame 549: // the total will be turned into screen blends and view angle kicks 550: // at the end of the frame 551: if (client) 552: { 553: client->damage_parmor += psave; 554: client->damage_armor += asave; 555: client->damage_blood += take; 556: client->damage_knockback += knockback; 557: VectorCopy (point, client->damage_from); 558: } 559: } 560: 561: 562: /* 563: ============ 564: T_RadiusDamage 565: ============ 566: */ 567: void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) 568: { 569: float points; 570: edict_t *ent = NULL; 571: vec3_t v; 572: vec3_t dir; 573: 574: while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) 575: { 576: if (ent == ignore) 577: continue; 578: if (!ent->takedamage) 579: continue; 580: 581: VectorAdd (ent->mins, ent->maxs, v); 582: VectorMA (ent->s.origin, 0.5, v, v); 583: VectorSubtract (inflictor->s.origin, v, v); 584: points = damage - 0.5 * VectorLength (v); 585: if (ent == attacker) 586: points = points * 0.5; 587: if (points > 0) 588: { 589: if (CanDamage (ent, inflictor)) 590: { 591: VectorSubtract (ent->s.origin, inflictor->s.origin, dir); 592: T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); 593: } 594: } 595: } 596: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.