F2: new change to the damage calculation - hopefully a fix

Status
Not open for further replies.
How do you go about debugging this thing? I've tried in ollydbg for a while, but I can't get back to desktop when the game hits a breakpoint, and just looking through the code when it's not running isn't enough.
 
Haenlomal said:
Hi all,

First, my apologies to Glovz for de-railing his thread. If you want, maybe we get a mod to move all relevant posts to another thread?

Scond, I'll be busy most of the day with at a friend's wedding this afternoon, so I won't be able to contribute much today. I'll say, though, that I've managed to isolate the function that returns whether or not a player has a certain trait, so with this, I should be able to isolate the raw melee bonus damage from the heavy handed bonus and from the bonus Hth damage perk. The code would look something like this:

Code:
mov edx, 0x0B;   // set Melee Damage attribute argument
mov eax, ecx;     // set pointer to critter (assuming it's stored in ecx)
call 0x004aef48; // call GetCritterStat( CRITTER* eax, 0x0B)
mov ebp, eax;    // store result in ebp
mov eax, 0x06;  // Pass argument 6 (TRAIT_heavy_handed)
call 0x004B3bc8; // Retrieve Player Trait
cmp eax, 0x01;   // does player have heavy handed trait?
jne ajmp;
imul eax, 0x04;
sub ebp, eax;    // if yes, subtract 4 from melee damage
ajmp:
xor edx, edx;   // set Strength attribute argument
mov eax, ecx;     // set pointer to critter (assuming it's stored in ecx)
call 0x004aef48; // call GetCritterStat( CRITTER* eax, 0x00)
sub eax, 0x05;  // subtract 5 from strength to get melee bonus
cmp eax, 0x01; // is it less than 1?
jge bjmp;
mov eax, 0x01; // if yes, set it to 1
bjmp:
sub ebp, eax; // subtract melee bonus to get bonus from bonus hth perk

Of course, I haven't added checks to make sure the critter is the PC, or else I think that has_trait call may crash the game. But it's a start.

Cheers,

-- The Haen.
Hi Haen,

I have no problem with the offtopic discussion carrying on in this thread, but to help others follow it would be useful if a moderator or admin could move the relevant posts to another thread with you or me as the owner.

I believe I understand clearly enough what's going on in the code snippet you provided, if you post more of the code hopefully I can help with some of it.

@Magnus
Hopefully Haen can provide you with some tips; like I said earlier, I've never been really good at that myself. I used to inject changes where space allowed and test them through the game; very time consuming and very limiting.

Thank goodness Timeslip came along and allowed for a better method for adding/replacing/bypassing code in the exe.
 
Glovz said:
Hi Nevill,

First, thank you for the input - different opinions are always welcome.

Not sure I agree with your changes, something to consider is that bonus values should almost always be added after primary calculations.

Not entirely sure of what changes will be feasible at this point until Haen considers the purposed changes and/or posts the original code.

Cheers,
Glovz
Hi.

I would like to add that so far I very much approve of what you've done with damage formula. It would be fun to play F2 with these changes when RP 2.1 finally comes out. That's why I'll continue trying to convince you - because I'd like to try your fix.

If you fix it like you are currently suggesting, there will be very little randomisation in unarmed attacks damage. Basically, it will differ by 1 pt only, except for the criticals. It will make combat very predictable and bland.

From game balance viewpoint, I'd say that 2 additional pts of guaranteed damage from the perk is fine, but potential 9 extra pts (5 from STR, 4 from trait) is, in my opinion, an overkill.
 
Below are resulting ranges from using the following formulas with HtH attacks (no weapon). If the player was using a melee weapon then the result sets would scale based on the weapons range but unarmed attack bonus would NOT be applied.

Code:
Hand 1-2

player strength 4,5,6,7,8,9,10

original
raw_damage = random(min_damage_of_weapon, max_damage_of_weapon + bonus_melee_damage) 
bonus_melee_damage = max(player_strength - 5, 1) + heavy_handed_bonus + (2 * rank_of_bonus_hth_damage_perk)
raw_damage = random(1 + unarmed_attack_bonus, 2 + bonus_melee_damage + unarmed_attack_bonus). 

Glovz fix
raw_damage = random(min_damage_of_weapon, max_damage_of_weapon) + bonus_melee_damage 
bonus_melee_damage = max(player_strength - 5, 1 + (2 * rank_of_bonus_hth_damage_perk)) + heavy_handed_bonus 
raw_damage = random(1 + unarmed_attack_bonus, 2 + unarmed_attack_bonus) + bonus_melee_damage 

Nevill fix
raw_damage = random(min_damage_of_weapon, max_damage_of_weapon + bonus_melee_damage) + (2 * rank_of_bonus_hth_damage_perk) 
bonus_melee_damage = max(player_strength - 5, 1) + heavy_handed_bonus 
raw_damage = random(1 + unarmed_attack_bonus, 2 + bonus_melee_damage + unarmed_attack_bonus) + (2 * rank_of_bonus_hth_damage_perk).  


Original
Hand, no heavy handed, no bonus HtH, no unarmed attack bonus

ST     Range
4      1-3
5      1-3
6      1-3
7      1-4
8      1-5
9      1-6
10     1-7


Hand, heavy handed, no bonus HtH, no unarmed attack bonus

ST     Range
4      1-7
5      1-7
6      1-7
7      1-8
8      1-9
9      1-10
10     1-11


Hand, heavy handed, one level bonus HtH, no unarmed attack bonus

ST     Range
4      1-9
5      1-9
6      1-9
7      1-10
8      1-11
9      1-12
10     1-13


Hand, heavy handed, one level bonus HtH, unarmed attack bonus (2?)

ST     Range
4      3-11
5      3-11
6      3-11
7      3-12
8      3-13
9      3-14
10     3-15


Glovz' Fix
Hand, no heavy handed, no bonus HtH, no unarmed attack bonus

ST     Range
4      2-3
5      2-3
6      2-3
7      3-4
8      4-5
9      5-6
10     6-7


Hand, heavy handed, no bonus HtH, no unarmed attack bonus

ST     Range
4      6-7
5      6-7
6      6-7
7      7-8
8      8-9
9      9-10
10     11-12


Hand, heavy handed, one level bonus HtH, no unarmed attack bonus

ST     Range
4      7-7
5      7-7
6      7-7
7      7-8
8      8-9
9      9-10
10     11-12


Hand, heavy handed, one level bonus HtH, unarmed attack bonus (2?)

ST     Range
4      9-9
5      9-9
6      9-9
7      9-10
8      10-11
9      11-12
10     13-14


Nevill's Fix
Hand, no heavy handed, no bonus HtH, no unarmed attack bonus

ST     Range
4      1-3
5      1-3
6      1-3
7      1-4
8      1-5
9      1-6
10     1-7


Hand, heavy handed, no bonus HtH, no unarmed attack bonus

ST     Range
4      1-7
5      1-7
6      1-7
7      1-8
8      1-9
9      1-10
10     1-11


Hand, heavy handed, one level bonus HtH, no unarmed attack bonus

ST     Range
4      3-9
5      3-9
6      3-9
7      3-10
8      3-11
9      3-12
10     3-13


Hand, heavy handed, one level bonus HtH, unarmed attack bonus (2?)

ST     Range
4      5-11
5      5-11
6      5-11
7      5-12
8      5-13
9      5-14
10     5-15
Also remember once through the formulas above, the raw damage value picked randomly from the range is then put through the full damage calculation.
Code:
        Original Formula                               Fixed 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
--------------------------------------------------------------------------------------------
1       1     0      0      0      0      0            2     0      0      0      0      0
2       2     0      0      0      0      0            3     1      0      0      0      0
3       3     1      0      0      0      0            4     2      0      0      0      0
4       4     2      0      0      0      0            5     3      0      0      0      0
5       5     3      1      0      0      0            6     4      1      0      0      0
6       6     3      1      1      0      0            7     5      2      0      0      0
7       7     4      2      1      0      0            8     6      3      1      0      0
8       8     5      3      2      0      0            9     7      4      2      0      0
9       9     5      4      2      0      0            10    8      5      3      0      0
10      10    6      4      3      0      0            11    9      6      4      0      0
11      11    7      5      4      0      0            12    10     7      5      0      0
12      12    8      6      4      0      0            13    11     8      6      0      0
13      13    8      6      5      1      0            14    12     9      7      0      0
14      14    9      7      5      1      0            15    13     10     8      0      0
15      15    10     8      6      2      0            16    14     11     9      0      0
--------------------------------------------------------------------------------------------
 
As requested, here's the assembly code to the function that returns the raw damage dealt in an attack (DamageFunctionSub1 in sfall), courtesy of OllyDbg. For your reference, I've also included the switch tables used in the case statements found in the function.

The comments in about the switch tables and their associated conditional branching belong to OllyDbg. The rest of the comments are mine, from what I've been able to glean. Note that I'm uncertain of a lot of the things in there, so please take my comments with a grain of salt...or two! :) Those marked with "(?)" denotes high levels of uncertainty, more than the usual levels with which I am operating.

What we really need is someone like crazycc or Timeslip to do this sort of stuff...

Code:
004783F8   . 7C844700       DD FALLOUT2.0047847C                     ;  Switch table used at 00478475
004783FC   . 7C844700       DD FALLOUT2.0047847C
00478400   . 83844700       DD FALLOUT2.00478483
00478404   . 83844700       DD FALLOUT2.00478483
00478408   . 8A844700       DD FALLOUT2.0047848A
0047840C   . 8A844700       DD FALLOUT2.0047848A
00478410   . 7C844700       DD FALLOUT2.0047847C
00478414   . 83844700       DD FALLOUT2.00478483
00478418   . C1844700       DD FALLOUT2.004784C1                     ;  Switch table used at 004784B9
0047841C   . CB844700       DD FALLOUT2.004784CB
00478420   . D5844700       DD FALLOUT2.004784D5
00478424   . C1844700       DD FALLOUT2.004784C1
00478428   . D5844700       DD FALLOUT2.004784D5
0047842C   . E9844700       DD FALLOUT2.004784E9
00478430   . CB844700       DD FALLOUT2.004784CB
00478434   . D5844700       DD FALLOUT2.004784D5
00478438   . DF844700       DD FALLOUT2.004784DF
0047843C   . D5844700       DD FALLOUT2.004784D5
00478440   . DF844700       DD FALLOUT2.004784DF
00478444   . F0844700       DD FALLOUT2.004784F0
00478448  /$ 53             PUSH EBX
00478449  |. 51             PUSH ECX
0047844A  |. 56             PUSH ESI
0047844B  |. 57             PUSH EDI
0047844C  |. 55             PUSH EBP
0047844D  |. 83EC 10        SUB ESP,10
00478450  |. 89C1           MOV ECX,EAX  ; pointer to critter
00478452  |. 89D6           MOV ESI,EDX  ; hit_mode (see define.h hit_modes > 7 are apparently special unarmed attacks)
00478454  |. 31D2           XOR EDX,EDX
00478456  |. 31DB           XOR EBX,EBX
00478458  |. 31ED           XOR EBP,EBP
0047845A  |. 891424         MOV DWORD PTR SS:[ESP],EDX   ; set damage max = 0
0047845D  |. 895424 04      MOV DWORD PTR SS:[ESP+4],EDX ; set damage min = 0
00478461  |. 85C0           TEST EAX,EAX           ; sanity check for null critter pointer
00478463  |. 0F84 FC000000  JE FALLOUT2.00478565   ; if null pointer exit function with value of 0
00478469  |. 74 21          JE SHORT FALLOUT2.0047848C ; ??? -- This is unreacheable code
0047846B  |. 83FE 07        CMP ESI,7 ; is hit_mode a special unarmed attack?
0047846E  |. 77 1A          JA SHORT FALLOUT2.0047848A ; process unarmed attack if yes
00478470  |. 89F7           MOV EDI,ESI
00478472  |. C1E7 02        SHL EDI,2
00478475  |. 2E:FFA7 F88347>JMP DWORD PTR CS:[EDI+4783F8] ; Otherwise, detect from which slot attack was made and get item pointer to it
0047847C  |> E8 3B97FFFF    CALL FALLOUT2.00471BBC   ; weapon in left slot, get pointer to it
00478481  |. EB 05          JMP SHORT FALLOUT2.00478488
00478483  |> E8 E896FFFF    CALL FALLOUT2.00471B70   ; weapon in right slot, get pointer to it
00478488  |> 89C2           MOV EDX,EAX
0047848A  |> 89D0           MOV EAX,EDX  ; normal punch, kick, or special unarmed attack processing starts here
0047848C  |> 89C7           MOV EDI,EAX
0047848E  |. 85C0           TEST EAX,EAX        ; is left/right slot empty?
00478490  |. 75 65          JNZ SHORT FALLOUT2.004784F7 ; skip ahead if yes
00478492  |. BA 01000000    MOV EDX,1                        ; otherwise, no weapons used, so set min_unarmed_damage to 1
00478497  |. 89C8           MOV EAX,ECX
00478499  |. 895424 04      MOV DWORD PTR SS:[ESP+4],EDX     ; store min_unarmed_damage
0047849D  |. BA 0B000000    MOV EDX,0B
004784A2  |. E8 A16A0300    CALL FALLOUT2.004AEF48  ; Retrieve PC.Melee Damage Bonus
004784A7  |. 83C0 02        ADD EAX,2               ; max_unarmed_damage = bonus_melee_damage + 2
004784AA  |. 83EE 08        SUB ESI,8      ;  Switch (cases 8..13), also if hit_mode == 4, 5 (punch, kick), this will go back to 0047848A, but now with nonzero EDX value
004784AD  |. 890424         MOV DWORD PTR SS:[ESP],EAX  ; store max_unarmed_damage
004784B0  |. 83FE 0B        CMP ESI,0B
004784B3  |. 0F87 9A000000  JA FALLOUT2.00478553
004784B9  |. 2E:FF24B5 1884>JMP DWORD PTR CS:[ESI*4+478418] ; processing special unarmed attack bonus
004784C1  |> BB 03000000    MOV EBX,3                                ;  Cases 8,B of switch 004784AA
004784C6  |. E9 88000000    JMP FALLOUT2.00478553
004784CB  |> BB 05000000    MOV EBX,5                                ;  Cases 9,E of switch 004784AA
004784D0  |. E9 7E000000    JMP FALLOUT2.00478553
004784D5  |> BB 07000000    MOV EBX,7                                ;  Cases A,C,F,11 of switch 004784AA
004784DA  |. E9 74000000    JMP FALLOUT2.00478553
004784DF  |> BB 09000000    MOV EBX,9                                ;  Cases 10,12 of switch 004784AA
004784E4  |. E9 6A000000    JMP FALLOUT2.00478553
004784E9  |> BB 0A000000    MOV EBX,0A                               ;  Case D of switch 004784AA
004784EE  |. EB 63          JMP SHORT FALLOUT2.00478553
004784F0  |> BB 0C000000    MOV EBX,0C                               ;  Case 13 of switch 004784AA
004784F5  |. EB 5C          JMP SHORT FALLOUT2.00478553
004784F7  |> 89E2           MOV EDX,ESP                  ; normal weapon processing here
004784F9  |. 895424 0C      MOV DWORD PTR SS:[ESP+C],EDX
004784FD  |. 74 33          JE SHORT FALLOUT2.00478532
004784FF  |. 8D5424 08      LEA EDX,DWORD PTR SS:[ESP+8]
00478503  |. 8B40 64        MOV EAX,DWORD PTR DS:[EAX+64]
00478506  |. E8 FD9B0200    CALL FALLOUT2.004A2108
0047850B  |. 8D4424 04      LEA EAX,DWORD PTR SS:[ESP+4]
0047850F  |. 85C0           TEST EAX,EAX
00478511  |. 74 0B          JE SHORT FALLOUT2.0047851E
00478513  |. 8B4424 08      MOV EAX,DWORD PTR SS:[ESP+8]
00478517  |. 8B40 28        MOV EAX,DWORD PTR DS:[EAX+28]
0047851A  |. 894424 04      MOV DWORD PTR SS:[ESP+4],EAX
0047851E  |> 837C24 0C 00   CMP DWORD PTR SS:[ESP+C],0
00478523  |. 74 0D          JE SHORT FALLOUT2.00478532
00478525  |. 8B4424 08      MOV EAX,DWORD PTR SS:[ESP+8]
00478529  |. 8B5424 0C      MOV EDX,DWORD PTR SS:[ESP+C]
0047852D  |. 8B40 2C        MOV EAX,DWORD PTR DS:[EAX+2C]
00478530  |. 8902           MOV DWORD PTR DS:[EDX],EAX
00478532  |> 89F2           MOV EDX,ESI
00478534  |. 89F8           MOV EAX,EDI
00478536  |. E8 45FDFFFF    CALL FALLOUT2.00478280 ; Get attack_code (?)
0047853B  |. 83F8 02        CMP EAX,2 ; is it a kick... (?)
0047853E  |. 74 05          JE SHORT FALLOUT2.00478545
00478540  |. 83F8 01        CMP EAX,1 ; or a punch? (?) prevents melee bonus from being added to thrown weapons, I think...
00478543  |. 75 0E          JNZ SHORT FALLOUT2.00478553
00478545  |> BA 0B000000    MOV EDX,0B
0047854A  |. 89C8           MOV EAX,ECX
0047854C  |. E8 F7690300    CALL FALLOUT2.004AEF48  ; If yes, Retrieve PC.Melee Damage Bonus
00478551  |. 89C5           MOV EBP,EAX
00478553  |> 8B1424         MOV EDX,DWORD PTR SS:[ESP]    ;  Default case of switch 004784AA - retrieve max damage of attack
00478556  |. 8B4424 04      MOV EAX,DWORD PTR SS:[ESP+4]  ; retrieve min damage of attack
0047855A  |. 01EA           ADD EDX,EBP   ; max_damage += bonus_melee_damage
0047855C  |. 01D8           ADD EAX,EBX   ; min_damage += special unarmed bonus
0047855E  |. 01DA           ADD EDX,EBX   ; max_damage += special unarmed bonus
00478560  |. E8 5BAB0200    CALL FALLOUT2.004A30C0 ; return random number between min_dam and max_dam inclusive
00478565  |> 83C4 10        ADD ESP,10
00478568  |. 5D             POP EBP
00478569  |. 5F             POP EDI
0047856A  |. 5E             POP ESI
0047856B  |. 59             POP ECX
0047856C  |. 5B             POP EBX
0047856D  \. C3             RETN

Cheers,

-- The Haen.
 
Haenlomal said:
What we really need is someone like crazycc or Timeslip to do this sort of stuff...
Here:
Code:
int item_w_damage(sCritter* critter, int type) {
	sItem* weapon=0;
	int mindmg=0, maxdmg=0;
	if(!critter) return 0;
	switch(type) {
	case ATKTYPE_LWEP_PRIMARY: case ATKTYPE_LWEP_SECONDARY: case ATKTYPE_LWEP_RELOAD:
		weapon=inven_left_hand(critter);
		break;
	case ATKTYPE_RWEP_PRIMARY: case ATKTYPE_RWEP_SECONDARY: case ATKTYPE_RWEP_RELOAD:
		weapon=inven_right_hand(critter);
		break;
	}
	if(weapon) {
		sProto* proto;
		proto_ptr(weapon->pid, &proto);
		mindmg=proto->w_min_damage;
		maxdmg=proto->w_max_damage;
		if(item_w_subtype(weapon, type) == 1 || item_w_subtype(weapon, type)==2) {
			maxdmg+=stat_level(critter, STAT_melee_damage);
		}
	} else {
		int bonus=0;
		switch(type) {
		case ATKTYPE_STRONGPUNCH: bonus=3; break;
		case ATKTYPE_HAMMERPUNCH: bonus=5; break;
		case ATKTYPE_HAYMAKER: bonus=7; break;
		case ATKTYPE_JAB: bonus=3; break;
		case ATKTYPE_PALMSTRIKE: bonus=7; break;
		case ATKTYPE_PIERCINGSTRIKE: bonus=10; break;
		case ATKTYPE_STRONGKICK: bonus=5; break;
		case ATKTYPE_SNAPKICK: bonus=7; break;
		case ATKTYPE_POWERKICK: bonus=9; break;
		case ATKTYPE_HIPKICK: bonus=7; break;
		case ATKTYPE_HOOKKICK: bonus=9; break;
		case ATKTYPE_PIERCINGKICK: bonus=12; break;
		}
		maxdmg=stat_level(critter, STAT_melee_damage) + 2 + bonus;
		mindmg=1 + bonus;
	}
	return roll_random(mindmg, maxdmg);
}
 
@Haen
Could you post the code where the bonus melee damage is calculated?

I believe I already have the changes I want for the above code you posted, actually very minor to accomplish what I wanted.

@Timeslip
Just so I'm clear, the code you posted is the C code equivalent of the Assembly code Haen posted - correct?

Thanks,
Glovz
 
Glovz said:
@Timeslip
Just so I'm clear, the code you posted is the C code equivalent of the Assembly code Haen posted - correct?
Yup. (Well, mostly. In the original there's calls to item_hit_with and item_w_damage_min_max, which got inlined, so I left them inline too. It's why the code utterly pointlessly checks that the address of the stack is non-null. :P)
 
Timeslip said:
Glovz said:
@Timeslip
Just so I'm clear, the code you posted is the C code equivalent of the Assembly code Haen posted - correct?
Yup. (Well, mostly. In the original there's calls to item_hit_with and item_w_damage_min_max, which got inlined, so I left them inline too. It's why the code utterly pointlessly checks that the address of the stack is non-null. :P)
Cool - though I'm not sure how to edit your code for the small changes I have made so far.

Take a look:
Code:
004783F8   . 7C844700       DD FALLOUT2.0047847C                     ;  Switch table used at 00478475 
004783FC   . 7C844700       DD FALLOUT2.0047847C 
00478400   . 83844700       DD FALLOUT2.00478483 
00478404   . 83844700       DD FALLOUT2.00478483 
00478408   . 8A844700       DD FALLOUT2.0047848A 
0047840C   . 8A844700       DD FALLOUT2.0047848A 
00478410   . 7C844700       DD FALLOUT2.0047847C 
00478414   . 83844700       DD FALLOUT2.00478483 
00478418   . C1844700       DD FALLOUT2.004784C1                     ;  Switch table used at 004784B9 
0047841C   . CB844700       DD FALLOUT2.004784CB 
00478420   . D5844700       DD FALLOUT2.004784D5 
00478424   . C1844700       DD FALLOUT2.004784C1 
00478428   . D5844700       DD FALLOUT2.004784D5 
0047842C   . E9844700       DD FALLOUT2.004784E9 
00478430   . CB844700       DD FALLOUT2.004784CB 
00478434   . D5844700       DD FALLOUT2.004784D5 
00478438   . DF844700       DD FALLOUT2.004784DF 
0047843C   . D5844700       DD FALLOUT2.004784D5 
00478440   . DF844700       DD FALLOUT2.004784DF 
00478444   . F0844700       DD FALLOUT2.004784F0 
00478448  /$ 53             PUSH EBX 
00478449  |. 51             PUSH ECX 
0047844A  |. 56             PUSH ESI 
0047844B  |. 57             PUSH EDI 
0047844C  |. 55             PUSH EBP 
0047844D  |. 83EC 10        SUB ESP,10 
00478450  |. 89C1           MOV ECX,EAX  ; pointer to critter 
00478452  |. 89D6           MOV ESI,EDX  ; hit_mode (see define.h hit_modes > 7 are apparently special unarmed attacks) 
00478454  |. 31D2           XOR EDX,EDX 
00478456  |. 31DB           XOR EBX,EBX 
00478458  |. 31ED           XOR EBP,EBP 
0047845A  |. 891424         MOV DWORD PTR SS:[ESP],EDX   ; set damage max = 0 
0047845D  |. 895424 04      MOV DWORD PTR SS:[ESP+4],EDX ; set damage min = 0 
00478461  |. 85C0           TEST EAX,EAX           ; sanity check for null critter pointer 
00478463  |. 0F84 FC000000  JE FALLOUT2.00478565   ; if null pointer exit function with value of 0 
00478469  |. 74 21          JE SHORT FALLOUT2.0047848C ; ??? -- This is unreacheable code 
0047846B  |. 83FE 07        CMP ESI,7 ; is hit_mode a special unarmed attack? 
0047846E  |. 77 1A          JA SHORT FALLOUT2.0047848A ; process unarmed attack if yes 
00478470  |. 89F7           MOV EDI,ESI 
00478472  |. C1E7 02        SHL EDI,2 
00478475  |. 2E:FFA7 F88347>JMP DWORD PTR CS:[EDI+4783F8] ; Otherwise, detect from which slot attack was made and get item pointer to it 
0047847C  |> E8 3B97FFFF    CALL FALLOUT2.00471BBC   ; weapon in left slot, get pointer to it 
00478481  |. EB 05          JMP SHORT FALLOUT2.00478488 
00478483  |> E8 E896FFFF    CALL FALLOUT2.00471B70   ; weapon in right slot, get pointer to it 
00478488  |> 89C2           MOV EDX,EAX 
0047848A  |> 89D0           MOV EAX,EDX  ; normal punch, kick, or special unarmed attack processing starts here 
0047848C  |> 89C7           MOV EDI,EAX 
0047848E  |. 85C0           TEST EAX,EAX        ; is left/right slot empty? 
00478490  |. 75 65          JNZ SHORT FALLOUT2.004784F7 ; skip ahead if yes 
00478492  |. BA 01000000    MOV EDX,1                        ; otherwise, no weapons used, so set min_unarmed_damage to 1 
00478497  |. 89C8           MOV EAX,ECX 
00478499  |. 895424 04      MOV DWORD PTR SS:[ESP+4],EDX     ; store min_unarmed_damage 
0047849D  |. BA 0B000000    MOV EDX,0B 
004784A2  |. E8 A16A0300    CALL FALLOUT2.004AEF48  ; Retrieve PC.Melee Damage Bonus 


// Add this line here       ADD DWORD PTR SS:[ESP+4],EAX        ; min_unarmed_damage += bonus_melee_damage 


004784A7  |. 83C0 02        ADD EAX,2               ; max_unarmed_damage = bonus_melee_damage + 2 
004784AA  |. 83EE 08        SUB ESI,8      ;  Switch (cases 8..13), also if hit_mode == 4, 5 (punch, kick), this will go back to 0047848A, but now with nonzero EDX value 
004784AD  |. 890424         MOV DWORD PTR SS:[ESP],EAX  ; store max_unarmed_damage 
004784B0  |. 83FE 0B        CMP ESI,0B 
004784B3  |. 0F87 9A000000  JA FALLOUT2.00478553 
004784B9  |. 2E:FF24B5 1884>JMP DWORD PTR CS:[ESI*4+478418] ; processing special unarmed attack bonus 
004784C1  |> BB 03000000    MOV EBX,3                                ;  Cases 8,B of switch 004784AA 
004784C6  |. E9 88000000    JMP FALLOUT2.00478553 
004784CB  |> BB 05000000    MOV EBX,5                                ;  Cases 9,E of switch 004784AA 
004784D0  |. E9 7E000000    JMP FALLOUT2.00478553 
004784D5  |> BB 07000000    MOV EBX,7                                ;  Cases A,C,F,11 of switch 004784AA 
004784DA  |. E9 74000000    JMP FALLOUT2.00478553 
004784DF  |> BB 09000000    MOV EBX,9                                ;  Cases 10,12 of switch 004784AA 
004784E4  |. E9 6A000000    JMP FALLOUT2.00478553 
004784E9  |> BB 0A000000    MOV EBX,0A                               ;  Case D of switch 004784AA 
004784EE  |. EB 63          JMP SHORT FALLOUT2.00478553 
004784F0  |> BB 0C000000    MOV EBX,0C                               ;  Case 13 of switch 004784AA 
004784F5  |. EB 5C          JMP SHORT FALLOUT2.00478553 
004784F7  |> 89E2           MOV EDX,ESP                  ; normal weapon processing here 
004784F9  |. 895424 0C      MOV DWORD PTR SS:[ESP+C],EDX 
004784FD  |. 74 33          JE SHORT FALLOUT2.00478532 
004784FF  |. 8D5424 08      LEA EDX,DWORD PTR SS:[ESP+8] 
00478503  |. 8B40 64        MOV EAX,DWORD PTR DS:[EAX+64] 
00478506  |. E8 FD9B0200    CALL FALLOUT2.004A2108 
0047850B  |. 8D4424 04      LEA EAX,DWORD PTR SS:[ESP+4] 
0047850F  |. 85C0           TEST EAX,EAX 
00478511  |. 74 0B          JE SHORT FALLOUT2.0047851E 
00478513  |. 8B4424 08      MOV EAX,DWORD PTR SS:[ESP+8] 
00478517  |. 8B40 28        MOV EAX,DWORD PTR DS:[EAX+28] 
0047851A  |. 894424 04      MOV DWORD PTR SS:[ESP+4],EAX 
0047851E  |> 837C24 0C 00   CMP DWORD PTR SS:[ESP+C],0 
00478523  |. 74 0D          JE SHORT FALLOUT2.00478532 
00478525  |. 8B4424 08      MOV EAX,DWORD PTR SS:[ESP+8] 
00478529  |. 8B5424 0C      MOV EDX,DWORD PTR SS:[ESP+C] 
0047852D  |. 8B40 2C        MOV EAX,DWORD PTR DS:[EAX+2C] 
00478530  |. 8902           MOV DWORD PTR DS:[EDX],EAX 
00478532  |> 89F2           MOV EDX,ESI 
00478534  |. 89F8           MOV EAX,EDI 
00478536  |. E8 45FDFFFF    CALL FALLOUT2.00478280 ; Get attack_code (?) 
0047853B  |. 83F8 02        CMP EAX,2 ; is it a kick... (?) 
0047853E  |. 74 05          JE SHORT FALLOUT2.00478545 
00478540  |. 83F8 01        CMP EAX,1 ; or a punch? (?) prevents melee bonus from being added to thrown weapons, I think... 
00478543  |. 75 0E          JNZ SHORT FALLOUT2.00478553 
00478545  |> BA 0B000000    MOV EDX,0B 
0047854A  |. 89C8           MOV EAX,ECX 
0047854C  |. E8 F7690300    CALL FALLOUT2.004AEF48  ; If yes, Retrieve PC.Melee Damage Bonus 
00478551  |. 89C5           MOV EBP,EAX 
00478553  |> 8B1424         MOV EDX,DWORD PTR SS:[ESP]    ;  Default case of switch 004784AA - retrieve max damage of attack 
00478556  |. 8B4424 04      MOV EAX,DWORD PTR SS:[ESP+4]  ; retrieve min damage of attack 


// Add this line here       ADD EAX,EBP   ; min_damage += bonus_melee_damage


0047855A  |. 01EA           ADD EDX,EBP   ; max_damage += bonus_melee_damage 
0047855C  |. 01D8           ADD EAX,EBX   ; min_damage += special unarmed bonus 
0047855E  |. 01DA           ADD EDX,EBX   ; max_damage += special unarmed bonus 
00478560  |. E8 5BAB0200    CALL FALLOUT2.004A30C0 ; return random number between min_dam and max_dam inclusive 
00478565  |> 83C4 10        ADD ESP,10 
00478568  |. 5D             POP EBP 
00478569  |. 5F             POP EDI 
0047856A  |. 5E             POP ESI 
0047856B  |. 59             POP ECX 
0047856C  |. 5B             POP EBX 
0047856D  \. C3             RETN
EDIT:
Changed as per Timeslip's comment.
 
The second change is fine. The first isn't; you haven't given the arguments to stat_level. It'll just crash. :P

In any case, there's no point calling a function twice to get the same value. Just stick a 'add [esp+4], eax' in at 0x4784A7.

tbh, given how simple and self contained that function is, if you want to change it I'd rather hookscript it than make hardcoded changes to it.
 
Timeslip said:
The second change is fine. The first isn't; you haven't given the arguments to stat_level. It'll just crash. :P

In any case, there's no point calling a function twice to get the same value. Just stick a 'add [esp+4], eax' in at 0x4784A7.

tbh, given how simple and self contained that function is, if you want to change it I'd rather hookscript it than make hardcoded changes to it.
Thank you Timeslip for the correction.

If you feel you can help and make this easier, then great! But just note that I'm not great with C, but maybe Haen is. Togther hopefully we find the best solution for implementing the changes.

Thanks,
Glovz

EDIT:
There is also one other portion of code I asked Haen to post - the section that calculates the bonus melee damage. There is still one change I would like to attempt making there.
 
Glovz said:
EDIT:
There is also one other portion of code I asked Haen to post - the section that calculates the bonus melee damage. There is still one change I would like to attempt making there.
That's not another portion of the code; it's in the bit that's already posted. Or do you mean something else? :?
 
Timeslip said:
Glovz said:
EDIT:
There is also one other portion of code I asked Haen to post - the section that calculates the bonus melee damage. There is still one change I would like to attempt making there.
That's not another portion of the code; it's in the bit that's already posted. Or do you mean something else? :?
I believe I understood the code posted correctly. But please slap me if I'm wrong again. :?

This line --- CALL FALLOUT2.004AEF48 ; Retrieve PC.Melee Damage Bonus --- is a call out to a portion of the code not listed previously and I assume returns a value calculated against other criteria.

Haen described it as:
Code:
bonus_melee_damage = max(player_strength - 5, 1) + heavy_handed_bonus + (2 * rank_of_bonus_hth_damage_perk)

I want to change it to:
Code:
bonus_melee_damage = max(player_strength - 5, 1 + (2 * rank_of_bonus_hth_damage_perk)) + heavy_handed_bonus

This is the last piece of the puzzle.
 
Glovz said:
This line --- CALL FALLOUT2.004AEF48 ; Retrieve PC.Melee Damage Bonus
That's the stat_level function. Nothing special there; it just returns a stat of a given critter. If you want to change a critters unarmed damage stat, there are a heck of a lot easier ways of doing it than fiddling with the exe. (i.e. the set_critter_stat script function.)

Edit: Here's the C version. I don't have the asm at hand sorry.

Code:
int stat_level(sCritter* critter, DWORD statID) {
	if(statID>=STAT_real_max_stat) return 0;
	if(statID==STAT_current_hp) return critter_get_hits(critter);
	if(statID==STAT_current_poison) return critter_get_poison(critter);
	if(statID==STAT_current_rad) return critter_get_rads(critter);
	int stat=stat_get_base(critter, statID) + stat_get_bonus(critter, statID);
	if(statID==STAT_ac&&GetCombatState()==1&&critter!=combat_whose_turn()) {
		int apmult=1;
		if(perk_level(critter, PERK_hth_evade_perk)) {
			sCritter* inven=inven_right_hand(obj_dude());
			if(!inven||(item_get_type(inven)!=3&&item_w_anim_code(inven)==0)) {
				inven=inven_left_hand(obj_dude());
				if(!inven||(item_get_type(inven)!=3&&item_w_anim_code(inven)==0)) {
					apmult*=2;
				}
				stat+=skill_level(critter, SKILL_UNARMED_COMBAT)/12;
			}
		}
		stat+=critter->c_CurrentAP*apmult;
	}
	if(statID==STAT_pe&&critter->c_InjuredLimbs&CRITTER_EYEDAMAGE) stat-=5;
	if(statID==STAT_age) stat+=game_time()/0x12CC0300;
	if(statID==STAT_max_ap) {
		int burden=stat_level(critter, STAT_carry_amt)<item_total_weight(critter);
		if(burden<0) {
			stat-=1 + (-burden)/40;
		}
	}
	if(critter==obj_dude()) {
		switch(statID) {
		case STAT_st:
			if(perk_level(critter, PERK_gain_strength_perk)) stat++;
			if(perk_level(critter, PERK_adrenaline_rush_perk)) {
				if(stat_level(critter, STAT_max_hp)/2 > stat_level(critter, STAT_current_hp)) stat++;
			}
			break;
		case STAT_pe:
			if(perk_level(critter, PERK_gain_perception_perk)) stat++;
			break;
		case STAT_en:
			if(perk_level(critter, PERK_gain_endurance_perk)) stat++;
			break;
		case STAT_ch:
			if(perk_level(critter, PERK_gain_charisma_perk)) stat++;
			if(inven_right_hand(obj_dude())->PID==0x1b1||inven_left_hand(obj_dude())->PID==0x1b1) stat++;
			break;
		case STAT_iq:
			if(perk_level(critter, PERK_gain_intelligence_perk)) stat++;
			break;
		case STAT_ag:
			if(perk_level(critter, PERK_gain_agility_perk)) stat++;
			break;
		case STAT_lu:
			if(perk_level(critter, PERK_gain_luck_perk)) stat++;
			break;
		case STAT_rad_resist:
		case STAT_poison_resist:
			if(perk_level(critter, PERK_vault_city_inoculations_perk)) stat+=10;
			break;
		case STAT_dmg_resist:
		case STAT_dmg_resist_explosion:
			if(perk_level(critter, PERK_dermal_armor_perk)) stat+=5;
			if(perk_level(critter, PERK_dermal_enhancement_perk)) stat+=10;
			break;
		case STAT_dmg_resist_laser:
		case STAT_dmg_resist_fire:
		case STAT_dmg_resist_plasma:
			if(perk_level(critter, PERK_phoenix_armor_perk)) stat+=5;
			if(perk_level(critter, PERK_phoenix_enhancement_perk)) stat+=10;
			break;
		case STAT_max_hp:
			if(perk_level(critter, PERK_alcohol_hp_bonus1_perk)) stat+=2;
			if(perk_level(critter, PERK_alcohol_hp_bonus2_perk)) stat+=4;
			if(perk_level(critter, PERK_alcohol_hp_neg1_perk)) stat-=2;
			if(perk_level(critter, PERK_alcohol_hp_neg2_perk)) stat-=4;
			if(perk_level(critter, PERK_autodoc_hp_bonus1_perk)) stat+=2;
			if(perk_level(critter, PERK_autodoc_hp_bonus2_perk)) stat+=4;
			if(perk_level(critter, PERK_autodoc_hp_neg1_perk)) stat-=2;
			if(perk_level(critter, PERK_autodoc_hp_neg2_perk)) stat-=4;
			break;
		}
	}
	if(stat>stat_data[statID].Maximum) stat=stat_data[statID].Maximum;
	if(stat<stat_data[statID].Minimum) stat=stat_data[statID].Minimum;
	return stat;
}

Edit2: Actually bothering to read that, what you want to change isn't in there anyway. Try stat_recalc_derived at 0x4AF6FC.
 
After re-evaluating the last change I wanted to make, I find it doesn't have the affect I wanted and breaks my own rule of applying bonuses only after primarily values are calculated/selected.

With that said then I think the only change necessary is the one I made earlier today. :)

I still want to have Haen's input.

Haen did you get a chance to evaluate the code I posted from crazycc back on page 1 of this thread? crazycc claimed it added the HtH evade perk to both unarmed and melee attacks.
 
Glovz said:
After re-evaluating the last change I wanted to make, I find it doesn't have the affect I wanted and breaks my own rule of applying bonuses only after primarily values are calculated/selected.

With that said then I think the only change necessary is the one I made earlier today. :)

I still want to have Haen's input.

Haen did you get a chance to evaluate the code I posted from crazycc back on page 1 of this thread? crazycc claimed it added the HtH evade perk to both unarmed and melee attacks.

Thanks to Timeslip for all his input, especially the translation of the assembly code to something like C. I do have RecStudio (btw, is there anything better out there?), but haven't had the time to really dig into the code like Timeslip obviously had. It's also been a few years since I went from being a coder to a business major, so my technical skills are more than a trifle rusty. :oops:

Time to answer a few overdue questions:

@Glovz:

I've looked at crazycc's changes. It seems like they do what he claims it does. I don't think it would collide with anything we're doing, since it doesn't touch anything to do with melee damage.

@Magnus:

Since I only have one monitor myself, I also run into the same problem as you did with the not returning to desktop thing. I do something similar to Glovz: I inject assembly code into the damage function and force it to return values I want to isolate -- most of the time, I make the damage function return the values I want instead of the true damage.

That said, I can usually tell what's going on just by looking at the code. For one thing, I can read at a rate of 5000 words per minute, and this translates directly to me being able to rapidly assimilate code while still maintaining their relative position and importance to each other. I also know that enum values for most weapons, armors, dmg_type, atk_type and anim_codes by heart. So, seeing what the function is and looking at its boundary checking conditions, I can usually make fairly good guesses as to what's going on, and I inject code to verify my claims. If I turn out to be wrong, it's usually not too difficult to work out from that point on what's really happening.

I would much rather have a dual monitor setup -- would make my work much much easier -- but like Glovz said, it's also a very time consuming process for me.

@Nevill and Glovz (re: tweaked damage formulas)

To get a grasp of the issue, I've looked through what the Fallout 2 manual says about Melee Damage, Heavy Handed Trait, Bonus HtH Damage perk, and Bonus Ranged Damage perk, along with the in-game descriptions of each of them. I've also thought about how HtH combat works in real life based on my own personal point-of-view as a martial artist, effective ways to model said behaviour. Here's my opinion on all of it.

The manual was pretty clear that Melee Damage is not really a bonus damage at all, but an increase of potential damage. Therefore, it only increases the maximum damage level. In this, I think I am in agreement: it doesn't matter how strong or accomplished a person is. Sometimes, a blow dealt by the fists or staff just doesn't seem to do much damage because of the way the blow is landed. Kung-fu movies notwithstanding, it's actually extremely difficult to strike with maximum strength and effectiveness at the precise point of impact every time, so you are going to experience variations in damage output. So I would leave its current implementation alone.

Similar comments can be said about the Heavy Handed Trait, though for game balance purposes, I might consider applying it to minimum damage. As it stands, you gain an average of +2 melee damage, while you no longer have access to any of the higher level critical effects. Not really a worthwhile trait to go for under any circumstances. Applying it to minimum damage makes the Trait a bit more palatable, but it still, um, sucks. :P I know Per calls it "Potentially useful" trait in his famed walkthrough, but personally, I'd classify it as a "Traits to avoid".

When it comes to Bonus HtH Damage, though, I'm starting to think that its implementation is an engine bug. Both the in-game and manual descriptions seem to imply that it works in the same fashion as Bonus Ranged Damage. As such, they should be treated the same way. I guess it's possible the bug is the other way around (i.e. Bonus HtH Damage is working as expected -- it's Bonus Ranged Damage that's being handled improperly), but it doesn't make sense that way.

So personally, I would amend the HtH damage formula to the following:

Code:
raw_HtH_damage = random(min_dam + special_unarmed_bonus + 2*rank_of_HtH_dam_perk, max_dam + melee_damage + heavy_handed_bonus + special_unarmed_bonus + 2*rank_of_HtH_dam_perk)

Cheers,

-- The Haen.
 
raw_HtH_damage = random(min_dam + special_unarmed_bonus + 2*rank_of_HtH_dam_perk, max_dam + melee_damage + heavy_handed_bonus + special_unarmed_bonus + 2*rank_of_HtH_dam_perk)
That was more or less what I was proposing. It would be nice if you defined 'melee_damage', though, to see if its really the case.
 
Nevill said:
That was more or less what I was proposing. It would be nice if you defined 'melee_damage', though, to see if its really the case.

Melee Damage has been previously defined as:

Code:
max(strength - 5, 1)

This means that Melee Damage can take on any value from 1 to 5 inclusive. You can see its effects directly if you play around a bit with the new character creation screen.

-- The Haen.
 
Haenlomal said:
I do have RecStudio (btw, is there anything better out there?)
Yes, your own brain. :P

Seriously, I'm yet to see an automatic decompiler that's useful on real-world optimized code. You're far better off doing it by hand; it takes longer, but at least the result actually stands a reasonable chance of being right.
 
To clarify things, this is what everyone would like to aim for:
Melee Damage Scale
Code:
melee_damage = max(player_strength - 5, 1)
Weapon Melee Damage
Code:
raw_damage = random(min_damage_of_melee_weapon, max_damage_of_melee_weapon + melee_damage)
Unarmed Melee Damage (equivalent to Haen's formula)
Code:
raw_damage = random(1, 2 + melee_damage + heavy_handed_bonus) + special_unarmed_bonus + (2*rank_of_HtH_dam_perk)
Does everyone agree? I think I can live with this. :D

Now how to implement this? :?
 
Status
Not open for further replies.
Back
Top