MultiDamage fix

Discuss programming in the QuakeC language.
Orion
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

MultiDamage fix

Post by Orion »

You know that originally the multi damage for the shotguns only applies damage to a single entity, even if the pellets spread hitting two or more entities, and the last entity it hits gets the full damage, while the others take no damage at all.
That's not a hard fix, but for those who haven't found a way yet, here's my solution:

Code: Select all

.float multi_damage;
void() ClearMultiDamage =
{
	local entity e;
	
	e = nextent(world);
	while (e)
	{
		e.multi_damage = 0;
		e = nextent(e);
	}
};

void() ApplyMultiDamage =
{
	local entity e;
	
	e = nextent(world);
	while (e)
	{
		if (e.multi_damage > 0)
			T_Damage (e, self, self, e.multi_damage);
		e = nextent(e);
	}
};

/*
================
TraceAttack
================
*/
void(float damage, vector dir) TraceAttack =
{
	local	vector	vel, org;
	
	vel = normalize(dir + v_up*crandom() + v_right*crandom());
	vel = vel + 2*trace_plane_normal;
	vel = vel * 200;

	org = trace_endpos - dir*4;

	if (trace_ent.takedamage)
	{
		SpawnBlood (org, vel*0.2, damage);
		trace_ent.multi_damage = trace_ent.multi_damage + damage;
	}
	else
	{
		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_GUNSHOT);
		WriteCoord (MSG_BROADCAST, org_x);
		WriteCoord (MSG_BROADCAST, org_y);
		WriteCoord (MSG_BROADCAST, org_z);
	}
};
AddMultiDamage() was removed, because a single line in TraceAttack() will do the same job, as I turned multi_damage into a .float (field) instead of a global.
TraceAttack() will now add the multi damage to individual trace_ents. FireBullets() remains unchanged, so I didn't need to post it here. ClearMultiDamage() simply cycles through all entites in the world and resets their .multi_damage value to zero. ApplyMultiDamage() is called in FireBullets() after the while() loop, which also cycles through all entites, checks if there are entities with .multi_damage > 0, and applies the damage accordingly.
That worked well for me. :wink:
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Re: MultiDamage fix

Post by Seven »

Thank you very much Orion.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: MultiDamage fix

Post by Cobalt »

First off , bravo, great idea.

Second:

How did this work for anyone else? I put the code in, and at first no damage was given , but that was my mistake because I pasted wrong.
To me it looks like we are just taking the float [damage] and applying its same value to whatever other ent the trace hits? I would imagine the secondary damage would be less than the first, so would it not be better to reduce that value somewhat?

Also I noticed that you are pretty much walking through the entire entity list with the [ while (e) ] statements.
CPU resources can be saved if you use [while (e.takedamage) ] for example. Also its the same to just say [ If (e.multi_damage) ] than to check if its > 0.

After some more study, this code probably is ok for vanilla single player , but for deathmatch or teamplay it looks like it needs more tweaking because if you have multiple shotgun battles going on, we are clearing multidamage for any entity on the map every time a gun is fired, so that wont work out too well. I am going to try and add more to this and will repost later.
Orion
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

Re: MultiDamage fix

Post by Orion »

Notice that multi_damage was turned into a dot float, which is a float that belongs to an entity, so 2 entities will not take the same damage, unless if the pellets are spread half and half for both entities. The Shotgun firing works like this:

First it resets the multi_damage value of every entity (that was the only way I found, but I might try yours) to zero. Then it goes to the while loop, and shoot the desired amout of pellets. TraceAttack() will add to multi_damage the amount of damage each pellet does, for the trace_ent it hits, if damageable. If a pellet misses, the trace_ent won't receive additional multi_damage from that pellet.

Then it finishes the while loop, and applies the multi damage to every entity with multi_damage > 0, and each entity will have their own multi_damage values. So I think it should work well even on MP, but I dunno what could happen it 2 shotguns from different players are fired EXACTLY at the same frame, but this is very hard to happen. I think if 2 players shoot with the SG at a single enemy, at the very same frame, that enemy will get a multi_damage of 96, if no pellet misses. 96 because if 2 shotguns are fired at the same frame, then ApplyMultiDamage() will be called twice, doubling the damage from EVERY shotgun. This is indeed a bug. Hard to happen, but it is. I still haven't discovered a better way to handle multi damage, but this one I found somewhat acceptable atm.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: MultiDamage fix

Post by Cobalt »

True it is difficult for a conflict to occur in the same frame as you indicated. I am trying this in my mod and still having troubles. I already have modded the traceattack function to handle the fix for the deathstrings in MP, and I am doing some other stuff like checking for armor type and then using particle to simulate bits of the armor flying off if hit...with some special sounds, but clearly there is a conflict not allowing this code to work.

Last night I tried assigning the trace_ent an owner (.owner) entity of the inflictor (self) to better manage the application of and clearing of the damage, but I must be working on the wrong code or something because its had no effect. My theory in advantage to checking for the owner is that in your while loop you can put in a counter that checks for a 2nd occurence of the same owner , and then reduce the damage to that secondary entity for example. However, while starting at world in the scan, its just chance / luck if indeed that secondary entity will turn out the be the second with respect to world or the first.

Another thing I noticed in your code is that you could likely wipe out clearmultidamage totally as well. Just set it to zero in the code for the multi damage loop after the line where the damage is applied to the entity.



Orion wrote:Notice that multi_damage was turned into a dot float, which is a float that belongs to an entity, so 2 entities will not take the same damage, unless if the pellets are spread half and half for both entities. The Shotgun firing works like this:
.
Orion
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

Re: MultiDamage fix

Post by Orion »

Yeah, you're right, it is possible to completely remove ClearMultiDamage().

So the new ApplyMultiDamage() will look like this:

Code: Select all

void() ApplyMultiDamage =
{
	local entity e;
	
	e = nextent(world);
	while (e)
	{
		if (e.multi_damage)
		{
			T_Damage (e, self, self, e.multi_damage);
			e.multi_damage = 0;
		}
		e = nextent(e);
	}
};
And then you comment out ClearMultiDamage(); from FireBullets() before "while (shotcount < 0)".
And unfortunately, you have to cycle through all entites in the world because not all of them takes damage. So if I change while(e) to while(e.takedamage) it will stop on the first entity that takes no damage, and the order where entities are scanned is the same as the map's entity list afaik. There may be an entity with .takedamage = DAMAGE_NO between two with .takedamage = DAMAGE_YES/AIM.

Example:

while (e.takedamage): nextent(world) returns a player: DAMAGE_AIM, so it will look for the next one, which would be, say, a torch: DAMAGE_NO, it will stop here because (e.takedamage) == FALSE, so it stops looking for more damageable entities.
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: MultiDamage fix

Post by r00k »

I check this out and see how it works for me.
I remember something like this from the QIP days; Quake's hitscan scheme with damage_collectors.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: MultiDamage fix

Post by Cobalt »

Hmm didnt know that about the nextent and the while loop. What if we also changed:

e = nextent(e);

to be

e = nextent(e.takedamage);

?


And unfortunately, you have to cycle through all entites in the world because not all of them takes damage. So if I change while(e) to while(e.takedamage) it will stop on the first entity that takes no damage, and the order where entities are scanned is the same as the map's entity list afaik. There may be an entity with .takedamage = DAMAGE_NO between two with .takedamage = DAMAGE_YES/AIM.

Example:

while (e.takedamage): nextent(world) returns a player: DAMAGE_AIM, so it will look for the next one, which would be, say, a torch: DAMAGE_NO, it will stop here because (e.takedamage) == FALSE, so it stops looking for more damageable entities.
Orion
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

Re: MultiDamage fix

Post by Orion »

Impossible, it should be an entity, not a float. Also, your compiler will give you an error if you try that.
A possibility is using darkplaces' findfloat(), but it will only work on DP then.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: MultiDamage fix

Post by Cobalt »

Ok yea makes sense, thanks never knew about findfloat. I may give that a try someday.

Thinking some more about different stuff , and what if we spawn something like the shubs trigger hurt at the trace_ents origin for one frame and were use that to apply the shotgun damage? Also lets say we have the ssg and the wide spread is more hitting ent1 than ent2...how could we find out which ent is really taking more damage and apply it? Would tracebox come in handy?

EDIT / UPDATE:

I played around with my idea , and got it working. One thing I can tell is that spawning the entity at the org float, means if you shoot someone in the back, and they are moving away from you, the touch would not be for very long, but if they were moving into the firing, the touch would have a tendincy to last longer, which means more damage could be applied with some more code tweaking. All I did was remove the entity on the first touch, and I reduced the damage somewhat just to get it looking more normal because at first too much damage was being inflicted. Also, there is no need to call multidamage in the firebullets function now.Heres what I cameup with:

Code: Select all



.float multi_damage;

.entity owner  // No need to redeclare if this is already in your code

void() ApplyMultiDamage =
{


T_Damage (other, self.owner, self.owner, (other.multi_damage * random ()));
remove (self);
return;

};

/*
================
TraceAttack
================
*/
void(float damage, vector dir) TraceAttack =
{
   local   vector   vel, org;
   
   vel = normalize(dir + v_up*crandom() + v_right*crandom());
   vel = vel + 2*trace_plane_normal;
   vel = vel * 200;

   org = trace_endpos - dir*4;

   
    if (trace_ent.health)
 {
 trace_ent.multi_damage = damage;
 local entity bullets;
 bullets = spawn();
 bullets.owner = self;
 setorigin (bullets, org);
 setsize (bullets, '-16 -16 -20', '16 16 16'); // tried this setsize just for tests sake...could be changed maybe made smaller or bigger 
 bullets.solid = SOLID_TRIGGER;
 bullets.touch = ApplyMultiDamage;


SpawnBlood (org, vel*0.2, damage);
 }
   else
   {
      WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
      WriteByte (MSG_BROADCAST, TE_GUNSHOT);
      WriteCoord (MSG_BROADCAST, org_x);
      WriteCoord (MSG_BROADCAST, org_y);
      WriteCoord (MSG_BROADCAST, org_z);
   }
};
Another thing maybe could be measuring the distance of the traceline to proportionally reduce damage on greater distances, and also gradually introduce a faling z_origin on the end_pos of the trace , becsause the stock shotguns were pretty much infinite sniper shotguns really .....


Orion wrote:Impossible, it should be an entity, not a float. Also, your compiler will give you an error if you try that.
A possibility is using darkplaces' findfloat(), but it will only work on DP then.
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: MultiDamage fix

Post by r00k »

EDIT: seems to work like a champ!

READ below(updated just as u were typing)...
need the nextent to catch triggers/doors behind opponents. ;)
Last edited by r00k on Tue May 01, 2012 3:59 am, edited 2 times in total.
Orion
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

Re: MultiDamage fix

Post by Orion »

Yeah, but what about shootable doors, buttons, monsters? These will take no shotgun damage with that code. :P
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: MultiDamage fix

Post by Cobalt »

Oops, yea you are right. Is that because the traceline passes through takedamage entities?
Orion wrote:Yeah, but what about shootable doors, buttons, monsters? These will take no shotgun damage with that code. :P
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: MultiDamage fix

Post by r00k »

I had posted a snippet

Code: Select all

e = find(world, classname, "player");
thats What he was replying about.. I was trying to skip the entire entity list and just scan for players.
For as much use the shotgun gets in DM games, I'm surprised no one fixed this sooner!
Nice job Orion!
\
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: MultiDamage fix

Post by Cobalt »

Hmm. Im gonna play around with my idea some more. I dont know why its not supplying damage from the ent to any of the triggers, can someone tell me why?

Again, kudos to the original poster for tackling this problem. I just like to take opportunities like this to push the envelope some more and see what else as far as improvements could be done because my mod is a Darkplaces only type mod.

One thing I noticed today was one of my bots shot another bot while jumping in mid air. I posted in another thread here a while ago about improved air-death scenes....not much interest seemed present, but I still plan to put some code in there that checks if the target is on the ground. (problem is if its touching a wall, its condidered onground via FL_ONGROUND) If we are on ground, (we may also benefit by doing a check for checkbottom() or droptofloor(), however droptofloor still actually drops the entity you are referencing to the floor, so I think the only way around that is to spawn another ent at the same origin and then check for droptofloor) we run the stock QC that picks a random death scene. If we are in the air, we could merely apply some force to the target via calculations from the associated code mentioned here, and this is where my spawned entity could come in handy as the ' source of the force ' so to speak. I mentioned how we need to compare vector forces because if the ent is heading into the bullets the ent will be effected proportionately by that force. The animation frames for the player actually have some frames that show the player angled back, but if you notice, you can hit the player in the back and it still may learch opposite the direction of the firing. This could be fixed somewhat in QC by merely changing the angle of the model, however I am guessing the engine still exclusively handles the 'pain frames', but I could be wrong.

Some interesting new effects become possible if this is done right. Consider shooting an ent in mid air with the ssg and see how far its pushed back with a proportional roll angle applied to the model, and the regular deathscenes either eliminated or randomly framed in with new code.

Alot of this idea would probably be a great addition for the GYRO mod, however I cant locate the author, I believe he is QuakeMatt? Any coders interested in working with me to make this a new GYRO feature let me know.
Post Reply