]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/damage.qc
Merge remote-tracking branch 'origin/master' into samual/update_effects_tab
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / damage.qc
index 85e76e395cff1ccbf29afc8ebd125f8a8fe79258..bbc8f88043453a3e0338b02fccbfef6d00955ba2 100644 (file)
@@ -1,7 +1,119 @@
-void DamageEffect(vector hitorg, float dmg, float type, float specnum1);
+void DamageEffect_Think()
+{
+       // if particle distribution is enabled, slow ticrate by total number of damages
+       if(autocvar_cl_damageeffect_distribute)
+               self.nextthink = time + autocvar_cl_damageeffect_ticrate * self.owner.total_damages;
+       else
+               self.nextthink = time + autocvar_cl_damageeffect_ticrate;
+
+       if(time >= self.cnt || !self.owner || !self.owner.modelindex || !self.owner.drawmask)
+       {
+               // time is up or the player got gibbed / disconnected
+               self.owner.total_damages = max(0, self.owner.total_damages - 1);
+               remove(self);
+               return;
+       }
+       if(self.state && !self.owner.csqcmodel_isdead)
+       {
+               // if the player was dead but is now alive, it means he respawned
+               // if so, clear his damage effects, or damages from his dead body will be copied back
+               self.owner.total_damages = max(0, self.owner.total_damages - 1);
+               remove(self);
+               return;
+       }
+       self.state = self.owner.csqcmodel_isdead;
+#ifdef COMPAT_XON050_ENGINE
+       if(self.owner.isplayermodel && (self.owner.entnum == player_localentnum || self.owner.entnum == spectatee_status) && !autocvar_chase_active)
+#else
+       if(self.owner.isplayermodel && (self.owner.entnum == player_localentnum) && !autocvar_chase_active)
+#endif
+               return; // if we aren't using a third person camera, hide our own effects
+
+       // now generate the particles
+       vector org;
+       org = gettaginfo(self, 0); // origin at attached location
+       pointparticles(self.team, org, '0 0 0', 1);
+}
+
+void DamageEffect(vector hitorg, float dmg, float type, float specnum)
+{
+       // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets)
+
+       float life, nearestbone = 0;
+       string specstr, effectname;
+       entity e;
+
+       if(!autocvar_cl_damageeffect || autocvar_cl_gentle || autocvar_cl_gentle_damage)
+               return;
+       if(!self || !self.modelindex || !self.drawmask)
+               return;
+
+       // if this is a rigged mesh, the effect will show on the bone where damage was dealt
+       // we do this by choosing the skeletal bone closest to the impact, and attaching our entity to it
+       // if there's no skeleton, object origin will automatically be selected
+       FOR_EACH_TAG(self)
+       {
+               if(!tagnum)
+                       continue; // skip empty bones
+               // blacklist bones positioned outside the mesh, or the effect will be floating
+               // TODO: Do we have to do it this way? Why do these bones exist at all?
+               if(gettaginfo_name == "master" || gettaginfo_name == "knee_L" || gettaginfo_name == "knee_R" || gettaginfo_name == "leg_L" || gettaginfo_name == "leg_R")
+                       continue; // player model bone blacklist
+
+               // now choose the bone closest to impact origin
+               if(nearestbone == 0 || vlen(hitorg - gettaginfo(self, tagnum)) <= vlen(hitorg - gettaginfo(self, nearestbone)))
+                       nearestbone = tagnum;
+       }
+       gettaginfo(self, nearestbone); // set gettaginfo_name
+
+       // return if we reached our damage effect limit or damages are disabled
+       // TODO: When the limit is reached, it would be better if the oldest damage was removed instead of not adding a new one
+       if(nearestbone)
+       {
+               if(self.total_damages >= autocvar_cl_damageeffect_bones)
+                       return; // allow multiple damages on skeletal models
+       }
+       else
+       {
+               if(autocvar_cl_damageeffect < 2 || self.total_damages)
+                       return; // allow a single damage on non-skeletal models
+       }
+
+       life = bound(autocvar_cl_damageeffect_lifetime_min, dmg * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
+       specstr = species_prefix(specnum);
+       type = DEATH_WEAPONOF(type);
+       e = get_weaponinfo(type);
+
+       effectname = strcat("damage_", e.netname);
+       
+       // if damage was dealt with a bullet weapon, our effect is blood
+       // since blood is species dependent, include the species tag
+       if(type == WEP_SHOTGUN || type == WEP_UZI || type == WEP_RIFLE)
+       {
+               if(self.isplayermodel)
+               {
+                       effectname = strcat(effectname, "_", specstr);
+                       effectname = substring(effectname, 0, strlen(effectname) - 1); // remove the _ symbol at the end of the species tag
+               }
+               else
+                       return; // objects don't bleed
+       }
+
+       e = spawn();
+       setmodel(e, "null"); // necessary to attach and read origin // samual: FIXME: this is weird, is there some better way to do this?
+       setattachment(e, self, gettaginfo_name); // attach to the given bone
+       e.classname = "damage";
+       e.owner = self;
+       e.cnt = time + life;
+       e.team = particleeffectnum(effectname);
+       e.think = DamageEffect_Think;
+       e.nextthink = time;
+       self.total_damages += 1;
+}
+
 void Ent_DamageInfo(float isNew)
 {
-       float dmg, rad, edge, thisdmg, forcemul, species;
+       float dmg, rad, edge, thisdmg, forcemul, species, hitplayer = FALSE;
        vector force, thisforce;
        entity oldself;
 
@@ -34,6 +146,10 @@ void Ent_DamageInfo(float isNew)
        
        for(self = findradius(w_org, rad + MAX_DAMAGEEXTRARADIUS); self; self = self.chain)
        {
+               // attached ents suck
+               if(self.tag_entity)
+                       continue;
+
                vector nearest = NearestPointOnBox(self, w_org);
                if(rad)
                {
@@ -76,144 +192,151 @@ void Ent_DamageInfo(float isNew)
                        self.event_damage(thisdmg, w_deathtype, w_org, thisforce);
 
                DamageEffect(w_org, thisdmg, w_deathtype, species);
+
+               if(self.isplayermodel)
+                       hitplayer = TRUE; // this impact damaged a player
        }
 
        self = oldself;
        
        if(DEATH_ISVEHICLE(w_deathtype))
        {
-           traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
-           if(trace_plane_normal != '0 0 0')       
-            w_backoff = trace_plane_normal;
-        else
-            w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
-           
-           setorigin(self, w_org + w_backoff * 2); // for sound() calls
-           
-           switch(w_deathtype)
-           {            
-            case DEATH_VHCRUSH:
-                break;
-                
-            case DEATH_SBMINIGUN:
-                string _snd;
-                _snd = strcat("weapons/ric", ftos(1 + rint(random() * 2)), ".waw");
-                sound(self, CH_SHOTS, _snd, VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("spiderbot_minigun_impact"), self.origin, w_backoff * 1000, 1);
-                break;
-            case DEATH_SBROCKET:
-                sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("spiderbot_rocket_explode"), self.origin, w_backoff * 1000, 1);
-                break;
-            case DEATH_SBBLOWUP:
-                sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_MIN);
-                pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
-                break;
-                
-            case DEATH_WAKIGUN:
-                sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("wakizashi_gun_impact"), self.origin, w_backoff * 1000, 1);
-                break;
-            case DEATH_WAKIROCKET:
-                sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("wakizashi_rocket_explode"), self.origin, w_backoff * 1000, 1);
-                break;
-            case DEATH_WAKIBLOWUP:
-                sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_MIN);
-                pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
-                break;
-                
-            case DEATH_RAPTOR_CANNON:
-                sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("raptor_cannon_impact"), self.origin, w_backoff * 1000, 1);
-                break;
-            case DEATH_RAPTOR_BOMB_SPLIT:
-                float i;
-                vector ang, vel;
-                for(i = 1; i < 4; ++i)
-                {
-                    vel = normalize(w_org - (w_org + normalize(force) * 16)) + randomvec() * 128;
-                    ang = vectoangles(vel);
-                    RaptorCBShellfragToss(w_org, vel, ang + '0 0 1' * (120 * i));
-                }
-                    
-                
-                sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("raptor_bomb_spread"), self.origin, w_backoff * 1000, 1);
-                break;
-            case DEATH_RAPTOR_BOMB:
-                sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("raptor_bomb_impact"), self.origin, w_backoff * 1000, 1);
-                break;
-            case DEATH_RAPTOR_DEATH:
-                sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_MIN);
-                pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
-                break;
-           }
+               traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
+               if(trace_plane_normal != '0 0 0')
+                       w_backoff = trace_plane_normal;
+               else
+                       w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
+               
+               setorigin(self, w_org + w_backoff * 2); // for sound() calls
+               
+               switch(w_deathtype)
+               {
+                       case DEATH_VH_CRUSH:
+                               break;
+                               
+                       // spiderbot
+                       case DEATH_VH_SPID_MINIGUN:
+                               string _snd;
+                               _snd = strcat("weapons/ric", ftos(1 + rint(random() * 2)), ".waw");
+                               sound(self, CH_SHOTS, _snd, VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("spiderbot_minigun_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_SPID_ROCKET:
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("spiderbot_rocket_explode"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_SPID_DEATH:
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_MIN);
+                               pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
+                               break;
+            
+                       case DEATH_VH_WAKI_GUN:
+                               sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("wakizashi_gun_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_WAKI_ROCKET:
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("wakizashi_rocket_explode"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_WAKI_DEATH:
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_MIN);
+                               pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
+                               break;
+                               
+                       case DEATH_VH_RAPT_CANNON:
+                               sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("raptor_cannon_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_RAPT_FRAGMENT:
+                               float i;
+                               vector ang, vel;
+                               for(i = 1; i < 4; ++i)
+                               {
+                                       vel = normalize(w_org - (w_org + normalize(force) * 16)) + randomvec() * 128;
+                                       ang = vectoangles(vel);
+                                       RaptorCBShellfragToss(w_org, vel, ang + '0 0 1' * (120 * i));
+                               }
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("raptor_bomb_spread"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_RAPT_BOMB:
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("raptor_bomb_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_RAPT_DEATH:
+                               sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_MIN);
+                               pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1);
+                               break;
+                       case DEATH_VH_BUMB_GUN:
+                               sound(self, CH_SHOTS, "weapons/fireball_impact2.wav", VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("bigplasma_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+               }
        }
        
        
        if(DEATH_ISTURRET(w_deathtype))
-       {           
-           string _snd;
-           traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
-           if(trace_plane_normal != '0 0 0')       
-            w_backoff = trace_plane_normal;
-        else
-            w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
-           
-           setorigin(self, w_org + w_backoff * 2); // for sound() calls
-           
-           switch(w_deathtype)
-           {   
-             case DEATH_TURRET_EWHEEL:
-                sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_MIN);
-                pointparticles(particleeffectnum("laser_impact"), self.origin, w_backoff * 1000, 1);
-                break;
-             
-             case DEATH_TURRET_FLAC:
-                pointparticles(particleeffectnum("hagar_explode"), w_org, '0 0 0', 1);
-                _snd = strcat("weapons/hagexp", ftos(1 + rint(random() * 2)), ".waw");
-                sound(self, CH_SHOTS, _snd, VOL_BASE, ATTN_NORM);                
-                break;
-                
-             case DEATH_TURRET_MLRS:
-             case DEATH_TURRET_HK:
-             case DEATH_TURRET_WALKER_ROCKET:
-             case DEATH_TURRET_HELLION:
-                sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_MIN);
-                pointparticles(particleeffectnum("rocket_explode"), self.origin, w_backoff * 1000, 1);
-                break;
-             
-             case DEATH_TURRET_MACHINEGUN:
-             case DEATH_TURRET_WALKER_GUN:
-                _snd = strcat("weapons/ric", ftos(1 + rint(random() * 2)), ".waw");
-                sound(self, CH_SHOTS, _snd, VOL_BASE, ATTN_NORM);
-                pointparticles(particleeffectnum("machinegun_impact"), self.origin, w_backoff * 1000, 1);
-                break;
-                          
-             case DEATH_TURRET_PLASMA:
-                sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTN_MIN);
-                pointparticles(particleeffectnum("electro_impact"), self.origin, w_backoff * 1000, 1);
-                break;
-                          
-             case DEATH_TURRET_WALKER_MEELE:
-                sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTN_MIN);
-                pointparticles(particleeffectnum("TE_SPARK"), self.origin, w_backoff * 1000, 1);
-                break;
-
-             case DEATH_TURRET_PHASER:
-                break;
-                
-             case DEATH_TURRET_TESLA:
-                te_smallflash(self.origin);
-                break;
-
-        }        
+       {
+               string _snd;
+               traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
+               if(trace_plane_normal != '0 0 0')
+                       w_backoff = trace_plane_normal;
+               else
+                       w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
+               
+               setorigin(self, w_org + w_backoff * 2); // for sound() calls
+               
+               switch(w_deathtype)
+               {   
+                        case DEATH_TURRET_EWHEEL:
+                               sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_MIN);
+                               pointparticles(particleeffectnum("laser_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+                        
+                        case DEATH_TURRET_FLAC:
+                               pointparticles(particleeffectnum("hagar_explode"), w_org, '0 0 0', 1);
+                               _snd = strcat("weapons/hagexp", ftos(1 + rint(random() * 2)), ".waw");
+                               sound(self, CH_SHOTS, _snd, VOL_BASE, ATTN_NORM);
+                               break;
+                               
+                        case DEATH_TURRET_MLRS:
+                        case DEATH_TURRET_HK:
+                        case DEATH_TURRET_WALK_ROCKET:
+                        case DEATH_TURRET_HELLION:
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_MIN);
+                               pointparticles(particleeffectnum("rocket_explode"), self.origin, w_backoff * 1000, 1);
+                               break;
+                        
+                        case DEATH_TURRET_MACHINEGUN:
+                        case DEATH_TURRET_WALK_GUN:
+                               _snd = strcat("weapons/ric", ftos(1 + rint(random() * 2)), ".waw");
+                               sound(self, CH_SHOTS, _snd, VOL_BASE, ATTN_NORM);
+                               pointparticles(particleeffectnum("machinegun_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+                                                 
+                        case DEATH_TURRET_PLASMA:
+                               sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTN_MIN);
+                               pointparticles(particleeffectnum("electro_impact"), self.origin, w_backoff * 1000, 1);
+                               break;
+                                                 
+                        case DEATH_TURRET_WALK_MEELE:
+                               sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTN_MIN);
+                               pointparticles(particleeffectnum("TE_SPARK"), self.origin, w_backoff * 1000, 1);
+                               break;
+
+                        case DEATH_TURRET_PHASER:
+                               break;
+                               
+                        case DEATH_TURRET_TESLA:
+                               te_smallflash(self.origin);
+                               break;
+
+               }
        }
        
        // TODO spawn particle effects and sounds based on w_deathtype
        if(!DEATH_ISSPECIAL(w_deathtype))
+       if not(hitplayer && !rad) // don't show ground impacts for hitscan weapons if a player was hit
        {
                float hitwep;
 
@@ -237,115 +360,3 @@ void DamageInfo_Precache()
        for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                (get_weaponinfo(i)).weapon_func(WR_PRECACHE);
 }
-
-.float total_damages;
-void DamageEffect_Think()
-{
-       // if particle distribution is enabled, slow ticrate by total number of damages
-       if(autocvar_cl_damageeffect_distribute)
-               self.nextthink = time + autocvar_cl_damageeffect_ticrate * self.owner.total_damages;
-       else
-               self.nextthink = time + autocvar_cl_damageeffect_ticrate;
-
-       if(time >= self.cnt || !self.owner || !self.owner.modelindex || !self.owner.drawmask)
-       {
-               // time is up or the player got gibbed / disconnected
-               self.owner.total_damages -= 1;
-               remove(self);
-               return;
-       }
-       if(self.state && !self.owner.csqcmodel_isdead)
-       {
-               // if the player was dead but is now alive, it means he respawned
-               // if so, clear his damage effects, or damages from his dead body will be copied back
-               self.owner.total_damages -= 1;
-               remove(self);
-               return;
-       }
-       self.state = self.owner.csqcmodel_isdead;
-       if(self.owner.isplayermodel && (self.owner.entnum == player_localentnum || self.owner.entnum == spectatee_status) && !autocvar_chase_active)
-               return; // if we aren't using a third person camera, hide our own effects
-
-       // now generate the particles
-       vector org;
-       org = gettaginfo(self, 0); // origin at attached location
-       pointparticles(self.team, org, '0 0 0', 1);
-}
-
-void DamageEffect(vector hitorg, float dmg, float type, float specnum)
-{
-       // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets)
-
-       float life, skeletal;
-       string specstr, effectnum;
-       entity e;
-
-       if(autocvar_cl_gentle || autocvar_cl_gentle_damage)
-               return;
-       if(!self || !self.modelindex || !self.drawmask)
-               return;
-
-       // if this is a rigged mesh, the effect will show on the bone where damage was dealt
-       // we do this by choosing the skeletal bone closest to the impact, and attaching our entity to it
-       // if there's no skeleton, object origin will automatically be selected
-       float closest;
-       FOR_EACH_TAG(self)
-       {
-               // blacklist bones positioned outside the mesh, or the effect will be floating
-               // TODO: Do we have to do it this way? Why do these bones exist at all?
-               if(gettaginfo_name == "master" || gettaginfo_name == "knee_L" || gettaginfo_name == "knee_R" || gettaginfo_name == "leg_L" || gettaginfo_name == "leg_R")
-                       continue; // player model bone blacklist
-               if(gettaginfo_name == "")
-                       continue; // skip empty bones
-
-               // now choose the bone closest to impact origin
-               if(!closest || vlen(hitorg - gettaginfo(self, tagnum)) <= vlen(hitorg - gettaginfo(self, closest)))
-               {
-                       closest = tagnum;
-                       skeletal = TRUE; // a bone was found, so this model is rigged
-               }
-       }
-       gettaginfo(self, closest); // set gettaginfo_name
-
-       // return if we reached our damage effect limit or damages are disabled
-       if(skeletal)
-       {
-               if(autocvar_cl_damageeffect < 1 || self.total_damages >= autocvar_cl_damageeffect_bones)
-                       return; // allow multiple damages on skeletal models
-       }
-       else
-       {
-               if(autocvar_cl_damageeffect < 2 || self.total_damages)
-                       return; // allow a single damage on non-skeletal models
-       }
-
-       life = bound(autocvar_cl_damageeffect_lifetime_min, dmg * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
-       specstr = species_prefix(specnum);
-       type = DEATH_WEAPONOF(type);
-       e = get_weaponinfo(type);
-
-       effectnum = strcat("damage_", e.netname);
-       // if damage was dealt with a bullet weapon, our effect is blood
-       // since blood is species dependent, include the species tag
-       if(type == WEP_SHOTGUN || type == WEP_UZI || type == WEP_RIFLE)
-       {
-               if(self.isplayermodel)
-               {
-                       effectnum = strcat(effectnum, "_", specstr);
-                       effectnum = substring(effectnum, 0, strlen(effectnum) - 1); // remove the _ symbol at the end of the species tag
-               }
-               else
-                       return; // objects don't bleed
-       }
-
-       e = spawn();
-       setmodel(e, "models/null.md3"); // necessary to attach and read origin
-       setattachment(e, self, gettaginfo_name); // attach to the given bone
-       e.classname = "damage";
-       e.owner = self;
-       e.cnt = time + life;
-       e.team = particleeffectnum(effectnum);
-       e.think = DamageEffect_Think;
-       e.nextthink = time;
-       self.total_damages += 1;
-}