Forum

is there a way to do location specific damage in QC?

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

Re:

Postby Cobalt » Wed Jul 15, 2015 8:19 pm

Well, Im still at it with this challenge, and I more or less dropped the idea of the Z-hit approach for now, and more or less am trying the method this guy mentioned way back.

Karatorian wrote:While I'm sure that engine support would make this all easier, I bet it could all be done with fairly generic QuakeC as well.

The basic idea goes like this. Create invisible target location entities for each player and update their positions in PlayerPostThink. (Which could get kinda complicated, depending on how accurate you wish to be. But possible.)

Then change the attack code to use these entities. By referencing a field to specify the target player and another to specify which part was hit, it should be fairly basic.

However, without using much in the way of extensions, I'm not sure how easy it'd be to make the targeting part work. I don't know exactly what traceline will and won't hit. So it might be difficult to make the entities that represent the target areas be nicely invisible and not interfere with the rest of the physics while still being targetable.
Any thoughts on this?


Essentially starting on the head only as the first challenge, the case for a Top-Headshot seems to be only viable with Spikes code previously mentioned because you can filter in a bound zone based on _x and _y where the traceline impacts
the hitbox, its already more or less happening in Traceattack () in its basic code. When you spawn a "headzone" approx at the view_ofs ('0 0 22' or '0 0 25.5' to be more precise), the distance from the top is so close its barely negligble with respect to the absmax of the hitbox, so if we had an actual imaginary ent with a small hitbox there, its not even worth performing a collisoin....unless someone else sees a reason why?

Other case is a headshot through the bottom side of the hitbox, which I dont think is possible.

That leaves cases for the sides of the hitbox, and I believe I have finally got a viable solution going that tracks range of the targ vs the attacker, and determines a "penetration" factor which will be the golden egg so to speak, and probably will need further adjustments.

I mentioned in the other Forum that Doomguy and Quakeguy both wear Helmets, however Quake pretty much totally does not have any helmet.mdl's , or at least none that I can find, for at least the purposes of making it a pickup item or something that drops off the player when hit, so in my tests I have supplied a helmarmor factor which wears down like regular armor during a headshot condition. Likewise, I made a "precise" headshot zone thats more in the center of zone1's head and depending on penetration, range and weapon type, we can factor in this zone as the "perfect headshot" condition, regardless of the helmet armor in certain cases.

Most this code is pretty much there, except for the current obstacle which is positioning the head_ent effectively when the pitch angle changes and the player is looking down or up at greater angles. Maybe someone out there can fix this? Im using MOVETYPE_FOLLOW because I have had decent results with it as long as its think it updated ever frame, though I have read someplaces setattachment in DP works better? My shortcoming here is calculating a view_ofs for the follow ent, other than that, we are nearly there.

Code: Select all

/*
============
SUB_RandomRange

Return a random number rounded off between min & max (or min if max is 0)
Just make sure max is greater than min...
============
*/
float(float rmin, float rmax) SUB_RandomRange =
{
   if (!rmax) return rmin;
   else
   {
    if (random () < 0.5)
       return floor(rmin + random()*(rmax-rmin));
    else
      return ceil(rmin + random()*(rmax-rmin));
      
   }
};

String (entity poorguy, vector org) HitLocation =
{

   // This is the main hit location routine - modified from  [ http://www.quakewiki.net/articles/hit-location-system/ ]

   local vector f, g, h;
   local float x, zdif;
   local string facing,location;
   facing = Side_touch (poorguy); //  > > SPikes modified code to determine side of the htitbox
   h = realorigin (poorguy);
   location = "UNDEFINED";

   g = org;
   g_z = 0;
   h_z = 0;
   x = vlen (g - h);
   f = ((normalize (f) * x) + org);
   zdif = (f_z - poorguy.origin_z);
   
   if (zdif <= 2)
   location = "LEG";
   else if (zdif <= 23.5)
   location = "CHEST";
   else if (facing == "TOP") // set zones for top headshot
    {
          zone1 = h_x + 6;
      zone2 = h_x - 6;   
      zone3 = h_y + 6;
      zone4 = h_y - 6;
      
   if (org_x <= zone1 && org_x >= zone2)
   if (org_y <= zone3 && org_y >= zone4)
   location = "TOPHEAD";

       if (org_x <= zone1 - 3 && org_x >= zone2 + 3)
   if (org_y <= zone3 - 3 && org_y >= zone4 + 3)
      location = "TOPHEADSHOT"; // Precise
    
    }
   else
   location = "UNDEFINED";
   

   if ((cvar ("local"))) // if we are a local dev, for debug
{
local string readout;
readout = "hit the";

readout = strcat(readout," ");
readout = strcat (readout, location);
readout = strcat(readout,"--");
readout = strcat (readout, facing);
readout = strcat (readout, " of ");
readout = strcat (readout, poorguy.netname);


readout = strcat(readout," ");

readout = strcat (readout, "\n");

centerprint(self,readout);
}
   
   return location;

};





// determines if head was penetrated by a bullet
float (vector loc, entity subj) Inbounds =
{
if (loc_x <= subj.absmin_x
 || loc_y <= subj.absmin_y
 || loc_z <= subj.absmin_z
 || loc_x >= subj.absmax_x
 || loc_y >= subj.absmax_y
 || loc_z >= subj.absmax_z)
return (0);
else
return (1);


};

// This track code not working well :)
void () SUB_TrackHead =
{
local vector ang;
if (self.aiment.ishuman == 1) // Using Frikbot
ang = self.aiment.v_angle;
else
ang = self.aiment.angles;
   
makevectors (ang);   

local vector trans, ovf;
ovf = v_forward;

trans = self.aiment.view_ofs + '0 0 1';
trans_z = trans_z + self.aiment.v_angle_x * 0.1;// + (self.aiment.view_ofs_z + '0 0 1');
trans_x = trans_x + self.aiment.v_angle_x * 0.1;
trans_y = trans_y + self.aiment.v_angle_y * 0.1;

// These 2 from Runequake's head pos code, seems to help sometimes, but for now I commented them out
//self.angles_x = self.aiment.v_angle_x - 60;
//self.angles_y = self.aiment.v_angle_y - 27;

self.view_ofs = (trans + v_forward + v_right); // relative origin
self.teleport_time = time + 1;
self.nextthink = self.aiment.nextthink;
};




entity (entity this) SpawnHeadMarker =
{
local entity dummy;
dummy = spawn ();
dummy.solid = 0;
dummy.origin = (this.origin + v_up*26 + v_right + v_forward); // Borrowed this origin code from Runequake's Vengence rune which puts a H_player.mdl over the players head and seems to mostly worlk.

dummy.angles_z = -30; // Also borrowed from rq, not sure if needed.

dummy.effects = EF_NODEPTHTEST; // Just so we can see it with chase_active running
setmodel (dummy, "progs/s_bubble.spr");
setsize (dummy, '-3 -3 -3.5', '3 3 3.5');   

dummy.think = SUB_TrackHead;
dummy.nextthink = time + 0.2;
return dummy;

};



Code: Select all
// This is placed in PutClientInServer () , just to spawn the head_ent
if (self.head_ent == world)
   {

   self.head_ent = SpawnHeadMarker (self);
   self.head_ent.classname = "Headzone_Marker";
                self.head_ent.movetype = MOVETYPE_FOLLOW;
                self.head_ent.aiment = self;
               //self.head_ent.effects = EF_NODRAW; ( set to hide the model when completed)

   // Left this commented because we dont really need to worry about its angles?
               //self.head_ent.punchangle = self.head_ent.angles; // the original angles of bmodel
       
   }



Code: Select all
void (float damage, vector dir) TraceAttack =
{
   local vector vel;
   local vector org, end;
   local float r,braindamage;
       end = trace_endpos;
   local entity oldt_ent;
   oldt_ent = trace_ent;
   local vector vel,org,vf,vr,vu;
          
   vf = v_forward;
   vr = v_right;
   vu = v_up;
   
   
   vel = normalize (((dir + (vu * crandom ())) + (vr * crandom ())));
   vel = (vel + (2 * trace_plane_normal));
   vel = (vel * 200);
   org = (trace_endpos - (dir * 4));
   if (trace_ent.classname == "grenade" || trace_ent.classname == "missile")
   {
      trace_ent.touch ();
      
   }
   
   
   if (trace_ent.takedamage)
   {
   local float range, p_force; /// range and penetration force by wep
   p_force = 18;
   range = vlen(self.origin - trace_ent.origin);
   if (self.weapon == 2)
   {
   if (range < 500) p_force = 20;   
   if (range > 800) p_force = 16;
   }
      
   if (self.weapon == 3)
   {
   if (range < 300)
                 p_force = 20;      
   else p_force = 14;   
   }
    //bprint ("range is ");
   //bprint (ftos(range));
   //bprint ("\n");
                 
                local string hotspot;
       
   if (trace_ent.flags & FL_CLIENT)
   hotspot = HitLocation (oldt_ent,end);
   
   
   if (trace_ent.flags & FL_CLIENT && self.weapon < 3 && trace_ent.waterlevel < 3)
   {
   local vector src;
   
   src = realorigin (trace_ent);
   //face = Side_touch (trace_ent);   
   
   local entity hd;
   src = trace_endpos + (dir * p_force);
   //SpawnTempDebugMarker (end, 0.25);
   
   hd = trace_ent.head_ent;
    local float hdhit;
                 hdhit = (vlen(trace_endpos - hd.origin));
   //bprint ("dist is ");
   //bprint (ftos(hdhit));
   //bprint ("\n");
   traceline (trace_endpos,src, 1, trace_ent);
   //SpawnTempDebugMarker (trace_endpos, 0.25);
       
   
   
   if (Inbounds (trace_endpos, hd) || hotspot == "TOPHEADSHOT")
   {

   setmodel (hd, "progs/brain.mdl"); // set a special model if hit for testing only
       
   //sound (self, CHAN_AUTO, "zombie/z_fall.wav", 1, ATTN_IDLE);
   if (hotspot != "TOPHEADSHOT")
   hotspot = "HEAD";
                 }
   
   
   oldt_ent.hitloc = hotspot;
   
   if (hotspot == "UNDEFINED") // For future dev
   {
   SpawnTempDebugMarker (trace_endpos, 0.25);
   //damage = 0;   
   }
   
   if (hotspot == "TOPHEAD" || hotspot == "HEAD" || hotspot == "TOPHEADSHOT")  // landed on head
   {
      
     if (oldt_ent.helmarmor > 0)
    {
   local float att,v;
   v = 1;
   
    oldt_ent.helmarmor = oldt_ent.helmarmor - damage;
    braindamage = damage / 3;
    te_spark (end, vel * -1, 1) ;
    local string ric;
    r = random ();
         if (r <= 0.3)
         {
            if (self.weapon == 2)
            ric = "weapons/ric1_.ogg";
                else
            ric = "weapons/ric1.wav";
         
                }
              else if (r <= 0.6)
            {
            
            if (self.weapon == 2)
            ric = "weapons/ric2_.ogg";
                else
            ric = "weapons/ric2.wav";
            }
            else
            {               
            if (self.weapon == 2)
            ric = "weapons/ric3_.ogg";
                else
            ric = "weapons/ric3.wav";
                        }
            
            if (oldt_ent.helmarmor >= (oldt_ent.helmeted * 0.9))
            {
            att = ATTN_NONE;
                v = SUB_RandomRange (0.25, 0.85);
            }
            else if (oldt_ent.helmarmor >= (oldt_ent.helmeted * 0.65))
            att = ATTN_NORM;
                else if (oldt_ent.helmarmor >= (oldt_ent.helmeted * 0.35))
                att = ATTN_IDLE;
                else att = ATTN_STATIC;
         
         sound7 (oldt_ent, CHAN_AUTO, ric, v, att, SUB_RandomRange (40,170),1);
                 stuffcmd (oldt_ent, " bf 0.15 0.025 0.05 0.33\n");

    } // end helm armor
    else
    {
      
    sound7 (oldt_ent, CHAN_AUTO, "weapons/spike2.wav", 1, ATTN_NONE, SUB_RandomRange (50,75),1);
    if (self.items & IT_QUAD)
    damage = 45;   
    else damage = damage * 3;
    T_Damage (oldt_ent, self, self, damage);
    }   
      
   
   if (hotspot == "TOPHEADSHOT") // pure headshot zone
    {
                  if (self.weapon == 2) // Future dev count # of ssg pellets hit head
    {
    SUB_Null (); // pellets hit this frame timer?   
      
    }
 
    }
         
   
   
    //sound (self, CHAN_AUTO, "zombie/z_fall.wav", 1, ATTN_IDLE);
   
   
   } else if (damage || !oldt_ent.helmarmor)
   SpawnBlood (org, (vel * 0.2), damage);
   

   
   }
    if (hotspot == "LEG")
   damage = damage / 2;
   
       
   

   
   
       if (braindamage) // :)
   {
   oldt_ent.hitloc = "BRAIN";
       T_Damage (oldt_ent, self, self, braindamage);
   stuffcmd (oldt_ent, " bf 0.15 0.025 0.05 0.33\n");
       sound (oldt_ent, CHAN_AUTO, "player/playerjump8_.wav", 1, ATTN_IDLE);
   }
   if (damage)
   AddMultiDamage (oldt_ent, damage);
   
   oldt_ent.hitloc = "";
   
   if (oldt_ent.solid == SOLID_BSP && oldt_ent != world)
       sound7 (oldt_ent, CHAN_AUTO, "zombie/z_fall.wav", 1, ATTN_NORM, SUB_RandomRange (125,400),1);
   
   }
        else
   {
      
      if (Calc_PointDamage (org, -1))
      SpawnBlood (org, (vel * 0.2), damage);
      else
      particle (org, (vel * 0.01), 232, 3);
      
      if (random () < 0.25 && range > 666)
      {
      local float att;
      local string ric;
      att = SUB_RandomRange (1, 3);
      
      r = SUB_RandomRange (0, 2);
              if (!r)
      ric = "weapons/ric7.wav";
      else if (r == 1)
      ric = "weapons/ric8.wav";
      else
      ric = "weapons/ric9.wav";
      
      
      
      pointsound (org, ric, r,att);
      }
   else
   {
      if (self.weapon < 3)
      {
         r = random ();
         if (r <= 0.3)
         {
            if (self.weapon == 2)
            pointsound (org, "weapons/ric1_.ogg", ceil(r), SUB_RandomRange (1, 3));
                else
            {
            if (random () < 0.5)
            pointsound (org, "weapons/ric1.wav", ceil(r), SUB_RandomRange (1, 3));
                else
            pointsound (org, "weapons/ric4.wav", ceil(r), SUB_RandomRange (1, 3));
                }
         
         }
         else
         {
            if (r <= 0.6)
            {
            
            if (self.weapon == 2)
            pointsound (org, "weapons/ric2_.ogg", random (), SUB_RandomRange (1, 3));
                else
            {
            if (random () < 0.5)
            pointsound (org, "weapons/ric2.wav", r, SUB_RandomRange (1, 3));
            else
            pointsound (org, "weapons/ric5.wav", r, SUB_RandomRange (1, 3));
            }   
            
            }
            else
            {
               
               if (self.weapon == 2)
            pointsound (org, "weapons/ric3_.ogg", random (), SUB_RandomRange (1, 3));
                else
            {
               if (random () < 0.5)
               pointsound (org, "weapons/ric3.wav", r, SUB_RandomRange (1, 3));
                   else
               pointsound (org, "weapons/ric6.wav", r, SUB_RandomRange (1, 3));
            }
            
            }
         }
      }
   }
}
   // Punch effect for ssg
   if (oldt_ent.invincible_finished < time)
   if (vlen (org - self.origin) < 200 && oldt_ent.ishuman && self.weapon == 2)
   {
   if (oldt_ent.origin_y + 5 < org_y)
       oldt_ent.punchangle_y = vlen(org - self.origin) /5;      
   if (oldt_ent.origin_y - 5 > org_y)
       oldt_ent.punchangle_y = vlen(org - self.origin) / -5;

   }
};
User avatar
Cobalt
 
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

Previous

Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest