|
|
1.1.1.3 ! 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 = 2; 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: { 1.1.1.2 root 316: targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET; 317: 1.1 root 318: // this can only happen in coop (both new and old enemies are clients) 319: // only switch if can't see the current enemy 320: if (targ->enemy && targ->enemy->client) 321: { 322: if (visible(targ, targ->enemy)) 323: { 324: targ->oldenemy = attacker; 325: return; 326: } 327: targ->oldenemy = targ->enemy; 328: } 329: targ->enemy = attacker; 330: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) 331: FoundTarget (targ); 332: return; 333: } 334: 335: // it's the same base (walk/swim/fly) type and a different classname and it's not a tank 336: // (they spray too much), get mad at them 337: if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) && 338: (strcmp (targ->classname, attacker->classname) != 0) && 339: (strcmp(attacker->classname, "monster_tank") != 0) && 340: (strcmp(attacker->classname, "monster_supertank") != 0) && 341: (strcmp(attacker->classname, "monster_makron") != 0) && 342: (strcmp(attacker->classname, "monster_jorg") != 0) ) 343: { 1.1.1.2 root 344: if (targ->enemy && targ->enemy->client) 345: targ->oldenemy = targ->enemy; 1.1 root 346: targ->enemy = attacker; 347: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) 348: FoundTarget (targ); 349: } 1.1.1.2 root 350: // if they *meant* to shoot us, then shoot back 351: else if (attacker->enemy == targ) 1.1 root 352: { 1.1.1.2 root 353: if (targ->enemy && targ->enemy->client) 354: targ->oldenemy = targ->enemy; 355: targ->enemy = attacker; 356: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) 357: FoundTarget (targ); 358: } 359: // otherwise get mad at whoever they are mad at (help our buddy) unless it is us! 360: else if (attacker->enemy && attacker->enemy != targ) 361: { 362: if (targ->enemy && targ->enemy->client) 363: targ->oldenemy = targ->enemy; 1.1 root 364: targ->enemy = attacker->enemy; 365: if (!(targ->monsterinfo.aiflags & AI_DUCKED)) 366: FoundTarget (targ); 367: } 368: } 369: 370: qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker) 371: { 372: //FIXME make the next line real and uncomment this block 373: // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team)) 374: return false; 375: } 376: 377: 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) 378: { 379: gclient_t *client; 380: int take; 381: int save; 382: int asave; 383: int psave; 384: int te_sparks; 385: 386: if (!targ->takedamage) 387: return; 388: 389: // friendly fire avoidance 390: // if enabled you can't hurt teammates (but you can hurt yourself) 391: // knockback still occurs 392: if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) 393: { 394: if (OnSameTeam (targ, attacker)) 395: { 396: if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) 397: damage = 0; 398: else 399: mod |= MOD_FRIENDLY_FIRE; 400: } 401: } 402: meansOfDeath = mod; 403: 404: // easy mode takes half damage 405: if (skill->value == 0 && deathmatch->value == 0 && targ->client) 406: { 407: damage *= 0.5; 408: if (!damage) 409: damage = 1; 410: } 411: 412: client = targ->client; 413: 414: if (dflags & DAMAGE_BULLET) 415: te_sparks = TE_BULLET_SPARKS; 416: else 417: te_sparks = TE_SPARKS; 418: 419: VectorNormalize(dir); 420: 421: // bonus damage for suprising a monster 422: if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) 423: damage *= 2; 424: 425: if (targ->flags & FL_NO_KNOCKBACK) 426: knockback = 0; 427: 428: // figure momentum add 429: if (!(dflags & DAMAGE_NO_KNOCKBACK)) 430: { 431: if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) 432: { 433: vec3_t kvel; 434: float mass; 435: 436: if (targ->mass < 50) 437: mass = 50; 438: else 439: mass = targ->mass; 440: 441: if (targ->client && attacker == targ) 442: VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... 443: else 444: VectorScale (dir, 500.0 * (float)knockback / mass, kvel); 445: 446: VectorAdd (targ->velocity, kvel, targ->velocity); 447: } 448: } 449: 450: take = damage; 451: save = 0; 452: 453: // check for godmode 454: if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) 455: { 456: take = 0; 457: save = damage; 458: SpawnDamage (te_sparks, point, normal, save); 459: } 460: 461: // check for invincibility 462: if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) 463: { 464: if (targ->pain_debounce_time < level.time) 465: { 466: gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); 467: targ->pain_debounce_time = level.time + 2; 468: } 469: take = 0; 470: save = damage; 471: } 472: 473: psave = CheckPowerArmor (targ, point, normal, take, dflags); 474: take -= psave; 475: 476: asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); 477: take -= asave; 478: 479: //treat cheat/powerup savings the same as armor 480: asave += save; 481: 482: // team damage avoidance 483: if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) 484: return; 485: 486: // do the damage 487: if (take) 488: { 489: if ((targ->svflags & SVF_MONSTER) || (client)) 490: SpawnDamage (TE_BLOOD, point, normal, take); 491: else 492: SpawnDamage (te_sparks, point, normal, take); 493: 494: 495: targ->health = targ->health - take; 496: 497: if (targ->health <= 0) 498: { 499: if ((targ->svflags & SVF_MONSTER) || (client)) 500: targ->flags |= FL_NO_KNOCKBACK; 501: Killed (targ, inflictor, attacker, take, point); 502: return; 503: } 504: } 505: 506: if (targ->svflags & SVF_MONSTER) 507: { 508: M_ReactToDamage (targ, attacker); 509: if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) 510: { 511: targ->pain (targ, attacker, knockback, take); 512: // nightmare mode monsters don't go into pain frames often 513: if (skill->value == 3) 514: targ->pain_debounce_time = level.time + 5; 515: } 516: } 517: else if (client) 518: { 519: if (!(targ->flags & FL_GODMODE) && (take)) 520: targ->pain (targ, attacker, knockback, take); 521: } 522: else if (take) 523: { 524: if (targ->pain) 525: targ->pain (targ, attacker, knockback, take); 526: } 527: 528: // add to the damage inflicted on a player this frame 529: // the total will be turned into screen blends and view angle kicks 530: // at the end of the frame 531: if (client) 532: { 533: client->damage_parmor += psave; 534: client->damage_armor += asave; 535: client->damage_blood += take; 536: client->damage_knockback += knockback; 537: VectorCopy (point, client->damage_from); 538: } 539: } 540: 541: 542: /* 543: ============ 544: T_RadiusDamage 545: ============ 546: */ 547: void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) 548: { 549: float points; 550: edict_t *ent = NULL; 551: vec3_t v; 552: vec3_t dir; 553: 554: while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) 555: { 556: if (ent == ignore) 557: continue; 558: if (!ent->takedamage) 559: continue; 560: 561: VectorAdd (ent->mins, ent->maxs, v); 562: VectorMA (ent->s.origin, 0.5, v, v); 563: VectorSubtract (inflictor->s.origin, v, v); 564: points = damage - 0.5 * VectorLength (v); 565: if (ent == attacker) 566: points = points * 0.5; 567: if (points > 0) 568: { 569: if (CanDamage (ent, inflictor)) 570: { 571: VectorSubtract (ent->s.origin, inflictor->s.origin, dir); 572: T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); 573: } 574: } 575: } 576: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.