Changing the Damage Formula (aka, not this shit again...)

JimTheDinosaur said:
Well, I've finally come to understand why the combatdamage hookscript only runs once: it only tallies the total damage done by all the bullets to the target on a "hit" (remember those bursts where it'd say "you missed" and the guy who you aimed at still gets blown apart? Doesn't get calculated there). What this means is that I see little point in trying my hand at doing the damage calculations until (1) it becomes clear how the damage for the extra targets gets calculated (is armor even taken into account? I'm not even sure anymore) and (2) there's a way for adjusting the damage on "hits" (I have no idea how you would count the number of bullets that hit the target on a "hit", let alone a "miss").

The to hit calculations are completely fine because the tohit hook script gets called for every single bullet, but doing damage calculations would be a waste of time in my opinion because it would necessarily not affect one of the biggest parts of combat in Fallout. I assume all the damage formulas made up to this point don't affect bursts either.
the only two users I know that are able to trace the exe successfully are Haen and Timeslip.

Timeslip in the past has hesitated to explore the combat functions too far; have you tried contacting Haen?
 
Code:
Ok, I've been held up by RL, but here is what I understand from taking a look back at my mod currently in sfall and rethinking how the damage should be calculated yet again.

A- In the portion of the exe that was explained to me and that I understand (mostly):
there's a pointer to the number of hits that have occurred
a check to see that it's not zero
and I believe when damage has been calculated it loops back to the DamageFunction, where I assume it minuses one and loops back to the damage formula if there are hits remaining to be calculated, each time the DamageFunction summing up the damage
thus I think this is how it handles bursts
but because I don't know what's in the DamageFunction, I'm not sure how it's handling the potential of multiple targets being hit by a burst

B- Here is what feels right and that I hope can be implement for the damage calculation, but I have not had a chance to try yet in game, only ran some top and bottom numbers manually for one example:
ND = net damage value
RD = random damage value produced from weapons hit damage range
RB = ranged bonus (RB=0 unless the player has Bonus Ranged Damage perk)
CM = critical hit damage multiplier (if no critical hit then CM=2, otherwise assigned value from critical hit table)
ADR = armor damage resistance value
ADT = armor damage threshold value
X = ammo dividend
Y = ammo divisor
RM = ammo resistance modifier (only value allowed to be negative or positive in the equation)
CD = combat difficulty multiplier (Easy=75, Normal=100, Hard=125) 

1- some checks, just in case because 0 or negatives in theses would screw things up:
if X<1 then X=1
if Y<1 then Y=1
if ADT<0 then ADT=0
if ADR<0 then ADR=0
if X/Y<1 then temp3=1 --- X/Y being less then 1 did not make sense because it would end up causing the ammo to do severely less damage or severely more depending on how it was applied

2- does the RD+RB exceed ADT and is the ammo type designed to reduce ADT (assumption: Y>1 means the ammo was intended to pierce armor)
temp1=(RD+RB)-(ADT/Y)
if temp1<=0 then ND=0

3- If ND>0, is the armor's ability to resist damage affected by the ammo (assumption: X>1 means the ammo was intended to increase the net damage)
temp2=(ADR+RM)/100/X

4- to incorporate CD in a more meaningful way and with the assumption there is no such thing as armor resisting damage less than 0% or greater than 100%
if temp2>=CD/100 then
temp4=(temp1*CD/100/X)
temp5=temp1-temp4
temp6=temp5*temp3
ND=(temp5+temp6)/2

if temp2>0 and temp2<CD/100 then
temp4=temp1*CD/100*temp2
temp5=temp1-temp4
temp6=temp5*temp3
ND=(temp5+temp6)/2

if temp2<=0
temp4=temp1*CD/100
temp5=temp4*temp3
ND=(temp4+temp5)/2

The last two lines in each condition here might seem odd, but it was the only way I could find to get a result with a reasonable value

5- even after the previous calculations the values still seemed a bit soft, thus I added a small bonus that would differ depending on ammo type
ND=ND+Y

6- critical hit?
ND=ND*CM/2

The divide by two here is due to CM by default being equal to 2 when the hit was not critical

7- a last ND value check, no such thing as negative damage
if ND<0 then ND=0

 
The sample results below are with the assumption that the final result is floored (values after decimal dropped) in the final result and NOT with each division (yet to be in game tested), but this assumption is relatively safe given that if each division that was 0.x resulted in 0 then even the original damage calculation would result in too many 0 value hits.

10mm Pistol with 10mm JHP Ammunition
Ammo     X/Y     2/1
Ammo     RM      25
 
        Original Formula                               New Formula                                     
        N     LA     MA     CA     PA     APA          N     LA     MA     CA     PA     APA   
ADT     0     2      4      5      12     15           0     2      4      5      12     15
ADR     0     25     30     40     40     55           0     25     30     40     40     55
--------------------------------------------------------------------------------------------
5       7     4      2      1      0      0            7     4      2      0      0      0
6       -     -      -      -      0      0            -     -      -      2      -      -
7       -     -      -      -      0      0            -     -      -      -      -      -
8       -     -      -      -      1      0            -     -      -      -      -      -
9       -     -      -      -      -      0            -     -      -      -      -      -
10      -     -      -      -      -      1            -     -      -      -      -      -
11      -     -      -      -      -      -            -     -      -      -      -      -
12      18    11     9      6      4      1            16    12     9      8      0      0
--------------------------------------------------------------------------------------------

10mm Pistol with 10mm AP Ammunition
Ammo     X/Y     1/2
Ammo     RM      -25
 
        Original Formula                               New Formula                                     
        N     LA     MA     CA     PA     APA          N     LA     MA     CA     PA     APA   
ADT     0     2      4      5      12     15           0     2      4      5      12     15
ADR     0     25     30     40     40     55           0     25     30     40     40     55
--------------------------------------------------------------------------------------------
5       3     0      0      0      0      0            7     6      4      4      0      0
6       -     1      0      -      -      -            -     -      -      -      0      0
7       -     -      0      -      -      -            -     -      -      -      2      0
8       -     -      0      -      -      -            -     -      -      -      -      2
9       -     -      0      -      -      -            -     -      -      -      -      -
10      -     -      0      -      -      -            -     -      -      -      -      -
11      -     -      1      -      -      -            -     -      -      -      -      -
12      7     4      1      0      0      0            14    13     11     10     7      5
--------------------------------------------------------------------------------------------
 
In the portion of the exe that was explained to me and that I understand (mostly):
there's a pointer to the number of hits that have occurred
a check to see that it's not zero
and I believe when damage has been calculated it loops back to the DamageFunction, where I assume it minuses one and loops back to the damage formula if there are hits remaining to be calculated, each time the DamageFunction summing up the damage
thus I think this is how it handles bursts
but because I don't know what's in the DamageFunction, I'm not sure how it's handling the potential of multiple targets being hit by a burst

Interesting, so presumably your .exe hack covers the formula in between each DamageFunction, while hs_combatdamage covers the total that's arrived at afterwards. This would seem to also mean that your method can include the ranged bonus perk (called every hit), while hs_combatdamage can include the Living Anatomy perk (called only after the total hits are tabulated).

Still leaves one thing I'm not sure about, like you also said: how about "misses" in bursts, do they call DamageFunction too, or is there some unknown process that gets called? If only this were the Codex, then I could tag/annoy all the .exe explorers into responding 8-). I guess you could try some stuff out with your new formula and put it to the test with, say, an smg loaded with 10mm JHP against power armor targets (should be 0 on miss if your formula works, right?).
 
When hit chance is calculated; and say the chance to hit is 20%, where does that value get passed to? I would assume that function determines a miss or whether the miss of the primary target ends up being a hit to an unintended.

The damage calculation code I work with gets the number of hits from a pointer, which that then sets the loop.
 
I've decided to give my own formula a chance, which hopefully does exactly what I described in this thread (single relative modifier to both DT and DR). The formula itself looks a lot like Haen's, but a bit smaller (it cuts out the difficulty mod for instance, which I never liked).

Code:
// Jim's Formula
static __declspec(naked) void DamageFunction5() {
	__asm {
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		mov edx,dword ptr ss:[esp+0x24];	// Get Critical Multiplier (passed in as argument to function)
		call GetAmmoDividend;			// Retrieve Ammo Dividend
		imul edx,eax;				// Damage Multipler = Critical Multipler * Ammo Dividend
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		call GetAmmoDivisor;			// Retrieve Ammo Divisor
		imul ebp,eax;				// Ammo Divisor = 1 * Ammo Divisor (ebp set to 1 earlier in function)
		mov ebx,dword ptr ss:[esp+0x1c];	// Get number of hits
		xor ecx,ecx;				// Set loop counter to zero
		mov dword ptr ss:[esp+0x24],edx;	// Store Damage Multiplier
		test ebx,ebx;				// Is number of hits smaller than= 0?
		jle end;		// If yes, jump beyond damage calculation loop
ajmp:							// Start of damage calculation loop
		mov edx,dword ptr ds:[esi+0x4];		// Get pointer to weapon (?)
		mov eax,dword ptr ds:[esi];		// Get pointer to critter (?)
		mov ebx,dword ptr ss:[esp+0x18];	// Get Bonus Ranged Damage
		call DamageFunctionSub1;		// Retrieve Raw Damage
		add ebx,eax; 				// Raw Damage = Raw Damage + Bonus Ranged Damage
		mov edx,dword ptr ss:[esp+0x28];	// Get armor DT
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		call GetAmmoDTMod;			// Retrieve ammo AM (Armor Modifier: adds or removes a percentage of the DT and DR)  
		imul edx,eax;				// DT modifier = armor DT * ammo AM
		mov dword ptr ss:[esp+0x30],0x64;	//sets some variable to 100
		mov eax,edx;				//(not sure if I have to do this, but Haen does it)
		sar edx,0x1f;				// makes DT modifier the dividend
		idiv dword ptr ss:[esp+0x30];		// DT modifier = DT modifier / 100
		mov edx,dword ptr ss:[esp+0x28];	// Get armor DT
		add edx,eax;				// DT = DT + DT modifier (can be negative)
		test edx,edx;				// Is DT >= 0?
		jge bjmp;				// If yes, skip the next instruction
		xor edx,edx;				// Otherwise, set DT = 0
bjmp:
		sub ebx,edx;				// Raw Damage = Raw Damage - DT
		test ebx,ebx;				// Is Raw Damage smaller than= 0?
		jle cjmp;				// If yes, skip damage calculation and go to bottom of loop
		imul ebx,dword ptr ss:[esp+0x24];	// Otherwise, Raw Damage = Raw Damage * Damage Multiplier
		test ebp,ebp;				// Is Ammo Divisor == 0?
		je djmp;				// If yes, avoid divide by zero error
		mov edx,ebx;
		mov eax,ebx;
		sar edx,0x1f;
		idiv ebp;
		mov ebx,eax;				// Otherwise, Raw Damage = Raw Damage / Ammo Divisor
djmp:
		mov edx,ebx;
		mov eax,ebx;
		sar edx,0x1f;
		sub eax,edx;
		sar eax,0x1;				// Raw Damage = Raw Damage / 2 (related to critical hit damage multiplier bonus)
		jmp fjmp;
fjmp:
		mov edx,dword ptr ss:[esp+0x2c];	// Get armor DR
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		call GetAmmoDTMod;			// Retrieve ammo AM
		imul edx,eax;				// DR modifier = armor DR * ammo AM
		mov dword ptr ss:[esp+0x30],0x64;	//sets some variable to 100
		mov eax,edx;				//(not sure if I have to do this, but Haen does it)
		sar edx,0x1f;				// makes DR modifier the dividend
		idiv dword ptr ss:[esp+0x30];		// DR modifier = DR modifier / 100
		mov edx,dword ptr ss:[esp+0x2c];	// Get armor DR
		add edx,eax;				// DR = DR + DR modifier (can be negative)
		test edx,edx;				// Is DR >= 0?
		jge gjmp;
		xor edx,edx;				// If no, set DR = 0
		jmp hjmp;
gjmp:
		cmp edx,0x64;				// Otherwise, is DR >= 100?
		jge ijmp;				// If yes, damage will be zero, so stop calculating and go to bottom of loop
hjmp:
		imul edx,ebx;				// Otherwise, Resisted Damage = DR * Raw Damage
		mov dword ptr ss:[esp+0x30],0x64;
		mov eax,edx;
		sar edx,0x1f;
		idiv dword ptr ss:[esp+0x30];		// Resisted Damage = Resisted Damage / 100
		sub ebx,eax;				// Raw Damage = Raw Damage - Resisted Damage
cjmp:
		test ebx,ebx;				// Is Raw Damage smaller than= 0?
		jle ijmp;				// If yes, don't accumulate damage
		add dword ptr ds:[edi],ebx;		// Otherwise, Accumulated Damage = Accumulated Damage + Raw Damage
ijmp:
		mov eax,dword ptr ss:[esp+0x1c];	// Get number of hits
		inc ecx;				// counter += 1
		cmp ecx,eax;				// Is counter smaller than number of hits?
		jl ajmp;				// If yes, go back to start of damage calcuation loop (calculate damage for next hit)
end:
		jmp DamageFunctionReturn;		// Otherwise, exit loop
	}
}

Could somebody maybe tell me if it looks somewhat good? I'll see if I can find some way to test compile it.
 
I pressed CTRL + F and typed 'laser'

No matches in both pages of this thread

Thus I beg you, Jim: show lasers some love, they're useless in Fallout 1 & 2
 
You can't do that through the damage formula. Either through armor values or just plain raising damage ranges (I'll probably do the latter at some point, though you can already easily do that yourself with the weapons mod).
 
Okay2.jpg
 
Just a quick comment, if you are using assembly then values will have their remainder dropped. (ex. 1.9 = 1, 0.9 = 0)

It doesn't matter which assembly language commands you use for division, the loss of the remainder remains the same.

This is why I had to develop a work around.
 
Glovz said:
Just a quick comment, if you are using assembly then values will have their remainder dropped. (ex. 1.9 = 1, 0.9 = 0)

It doesn't matter which assembly language commands you use for division, the loss of the remainder remains the same.

This is why I had to develop a work around.

Yeah, knew that, but now that I think about it this might be a problem for the DT calculation, (DR values are big enough for this not to be a problem); I guess I can just multiply both damage and DT by a hundred until the end of the calc. Thanks for the reminder!
 
Code:
// Jim's Formula
static __declspec(naked) void DamageFunction5() {
	__asm {
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		mov edx,dword ptr ss:[esp+0x24];	// Get Critical Multiplier (passed in as argument to function)
		call GetAmmoDividend;			// Retrieve Ammo Dividend
		imul edx,eax;				// Damage Multipler = Critical Multipler * Ammo Dividend
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		call GetAmmoDivisor;			// Retrieve Ammo Divisor
		imul ebp,eax;				// Ammo Divisor = 1 * Ammo Divisor (ebp set to 1 earlier in function)
		mov ebx,dword ptr ss:[esp+0x1c];	// Get number of hits
		xor ecx,ecx;				// Set loop counter to zero
		mov dword ptr ss:[esp+0x24],edx;	// Store Damage Multiplier
		test ebx,ebx;				// Is number of hits smaller than= 0?
		jle end;		// If yes, jump beyond damage calculation loop
ajmp:							// Start of damage calculation loop
		mov edx,dword ptr ds:[esi+0x4];		// Get pointer to weapon (?)
		mov eax,dword ptr ds:[esi];		// Get pointer to critter (?)
		mov ebx,dword ptr ss:[esp+0x18];	// Get Bonus Ranged Damage
		call DamageFunctionSub1;		// Retrieve Raw Damage
		add ebx,eax; 				// Raw Damage = Raw Damage + Bonus Ranged Damage
		imul ebx,0x64;				//Raw Damage = Raw Damage * 100
		mov edx,dword ptr ss:[esp+0x28];	// Get armor DT
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		call GetAmmoDTMod;			// Retrieve ammo AM (Armor Modifier: adds or removes a percentage of the DT and DR)  
		imul edx,eax;				// DT modifier = armor DT * ammo AM
		mov edx,dword ptr ss:[esp+0x28];	// Get armor DT
		imul edx,0x64;				//DT = DT*100
		add edx,eax;				// DT = DT + DT modifier (can be negative)
		test edx,edx;				// Is DT >= 0?
		jge bjmp;				// If yes, skip the next instruction
		xor edx,edx;				// Otherwise, set DT = 0
bjmp:
		sub ebx,edx;				// Raw Damage = Raw Damage - DT
		test ebx,ebx;				// Is Raw Damage smaller than= 0?
		jle cjmp;				// If yes, skip damage calculation and go to bottom of loop
		imul ebx,dword ptr ss:[esp+0x24];	// Otherwise, Raw Damage = Raw Damage * Damage Multiplier
		test ebp,ebp;				// Is Ammo Divisor == 0?
		je djmp;				// If yes, avoid divide by zero error
		mov edx,ebx;
		mov eax,ebx;
		sar edx,0x1f;
		idiv ebp;
		mov ebx,eax;				// Otherwise, Raw Damage = Raw Damage / Ammo Divisor
djmp:
		mov edx,ebx;
		mov eax,ebx;
		sar edx,0x1f;
		sub eax,edx;
		sar eax,0x1;				// Raw Damage = Raw Damage / 2 (related to critical hit damage multiplier bonus)
		jmp fjmp;
fjmp:
		mov dword ptr ss:[esp+0x30],0x64;	//sets some variable to 100
		mov eax,ebx;				//(not sure if I have to do this, but Haen does it)
		mov edx,ebx;				
		sar edx,0x1f;				// makes Raw Damage modifier the dividend
		idiv dword ptr ss:[esp+0x30];		// Raw Damage = Raw Damage / 100
		mov ebx,eax;		
		mov edx,dword ptr ss:[esp+0x2c];	// Get armor DR
		mov eax,dword ptr ds:[esi+0x8];		// Get pointer to critter's weapon
		call GetAmmoDTMod;			// Retrieve ammo AM
		imul edx,eax;				// DR modifier = armor DR * ammo AM
		mov dword ptr ss:[esp+0x30],0x64;	//sets some variable to 100
		mov eax,edx;				//(not sure if I have to do this, but Haen does it)
		sar edx,0x1f;				// makes DR modifier the dividend
		idiv dword ptr ss:[esp+0x30];		// DR modifier = DR modifier / 100
		mov edx,dword ptr ss:[esp+0x2c];	// Get armor DR
		add edx,eax;				// DR = DR + DR modifier (can be negative)
		test edx,edx;				// Is DR >= 0?
		jge gjmp;
		xor edx,edx;				// If no, set DR = 0
		jmp hjmp;
gjmp:
		cmp edx,0x64;				// Otherwise, is DR >= 100?
		jge ijmp;				// If yes, damage will be zero, so stop calculating and go to bottom of loop
hjmp:
		imul edx,ebx;				// Otherwise, Resisted Damage = DR * Raw Damage
		mov dword ptr ss:[esp+0x30],0x64;
		mov eax,edx;
		sar edx,0x1f;
		idiv dword ptr ss:[esp+0x30];		// Resisted Damage = Resisted Damage / 100
		sub ebx,eax;				// Raw Damage = Raw Damage - Resisted Damage
cjmp:
		test ebx,ebx;				// Is Raw Damage smaller than= 0?
		jle ijmp;				// If yes, don't accumulate damage
		add dword ptr ds:[edi],ebx;		// Otherwise, Accumulated Damage = Accumulated Damage + Raw Damage
ijmp:
		mov eax,dword ptr ss:[esp+0x1c];	// Get number of hits
		inc ecx;				// counter += 1
		cmp ecx,eax;				// Is counter smaller than number of hits?
		jl ajmp;				// If yes, go back to start of damage calcuation loop (calculate damage for next hit)
end:
		jmp DamageFunctionReturn;		// Otherwise, exit loop
	}
}

Ok, now DT gets multiplied by a 100 alongside with the Raw Damage, and the damage only gets divided back to normal after the DT has been substracted.
 
Back
Top