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