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