home *** CD-ROM | disk | FTP | other *** search
/ Gambler 19 / GAMBLERCD19.BIN / UTILS / 3D / BRONIE / DUAL_LAU.ZIP / src / g_combat.c < prev    next >
C/C++ Source or Header  |  1997-11-26  |  13KB  |  518 lines

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