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