Added the display of actual damage for unarmed attacks in inven

This commit is contained in:
NovaRain
2021-09-25 20:19:48 +08:00
parent 334957d458
commit 759ef35432
9 changed files with 168 additions and 63 deletions
+40 -24
View File
@@ -20,6 +20,7 @@
#include "FalloutEngine.h" #include "FalloutEngine.h"
#include "Logging.h" #include "Logging.h"
#include "Unarmed.h"
#include "ReplacementFuncs.h" #include "ReplacementFuncs.h"
@@ -422,24 +423,37 @@ static void __declspec(naked) DisplayBonusHtHDmg1_hook() {
} }
} }
static bool bonusHtHDamageFix = false;
static bool displayBonusDamage = false;
static long __fastcall GetHtHDamage(TGameObj* source, long &meleeDmg, long handOffset) {
long min, max;
AttackType hit = Unarmed_GetStoredHitMode((handOffset == 0) ? HANDSLOT_Left : HANDSLOT_Right);
long bonus = Unarmed_GetDamage(hit, min, max);
meleeDmg += max + bonus;
long perkBonus = sfgame_perk_level(source, PERK_bonus_hth_damage) << 1;
if (!displayBonusDamage) meleeDmg -= perkBonus;
if (bonusHtHDamageFix && displayBonusDamage) min += perkBonus;
return min + bonus;
}
static void __declspec(naked) DisplayBonusHtHDmg2_hack() { static void __declspec(naked) DisplayBonusHtHDmg2_hack() {
static const DWORD DisplayBonusHtHDmg2Exit = 0x47254E; static const DWORD DisplayBonusHtHDmg2Exit = 0x47254F;
__asm { __asm {
mov ecx, eax; mov ecx, eax;
call stat_level_; call stat_level_; // get STAT_melee_dmg
add eax, 2; push eax; // max dmg (meleeDmg)
push eax; // max dmg mov edx, esp; // meleeDmg ref
mov edx, PERK_bonus_hth_damage; push edi; // handOffset
mov eax, ecx; call GetHtHDamage;
call perk_level_; push eax; // min dmg
shl eax, 1; // Multiply by 2
inc eax; // min dmg (1 + bonus)
jmp DisplayBonusHtHDmg2Exit; jmp DisplayBonusHtHDmg2Exit;
} }
} }
static bool bonusHtHDamageFix = false;
long DamageMod_GetHtHMinDamageBonus(TGameObj* source) { long DamageMod_GetHtHMinDamageBonus(TGameObj* source) {
return (bonusHtHDamageFix) return (bonusHtHDamageFix)
? sfgame_perk_level(source, PERK_bonus_hth_damage) << 1 // Rank_of_Bonus_HtH_Damage_perk *= 2 ? sfgame_perk_level(source, PERK_bonus_hth_damage) << 1 // Rank_of_Bonus_HtH_Damage_perk *= 2
@@ -459,13 +473,13 @@ void DamageMod_Init() {
} }
} }
int BonusHtHDmgFix = GetConfigInt("Misc", "BonusHtHDamageFix", 1); bonusHtHDamageFix = GetConfigInt("Misc", "BonusHtHDamageFix", 1) != 0;
int DisplayBonusDmg = GetConfigInt("Misc", "DisplayBonusDamage", 0); displayBonusDamage = GetConfigInt("Misc", "DisplayBonusDamage", 0) != 0;
if (BonusHtHDmgFix) { if (bonusHtHDamageFix) {
bonusHtHDamageFix = true;
dlog("Applying Bonus HtH Damage Perk fix.", DL_INIT); dlog("Applying Bonus HtH Damage Perk fix.", DL_INIT);
if (DisplayBonusDmg == 0) { // Subtract damage from perk bonus (vanilla displaying) // Subtract damage from perk bonus (vanilla displaying)
if (!displayBonusDamage) {
const DWORD meleeDmgDispPrtAddr[] = { const DWORD meleeDmgDispPrtAddr[] = {
0x435C0C, // DisplayFix (ListDrvdStats_) 0x435C0C, // DisplayFix (ListDrvdStats_)
0x439921 // PrintFix (Save_as_ASCII_) 0x439921 // PrintFix (Save_as_ASCII_)
@@ -482,17 +496,19 @@ void DamageMod_Init() {
dlogr(" Done", DL_INIT); dlogr(" Done", DL_INIT);
} }
if (DisplayBonusDmg) { if (displayBonusDamage) {
dlog("Applying Display Bonus Damage patch.", DL_INIT); dlog("Applying Display Bonus Damage patch.", DL_INIT);
HookCall(0x4722DD, DisplayBonusRangedDmg_hook); // display_stats_ HookCall(0x4722DD, DisplayBonusRangedDmg_hook); // display_stats_
if (BonusHtHDmgFix) { if (bonusHtHDamageFix) {
HookCall(0x472309, DisplayBonusHtHDmg1_hook); // display_stats_ HookCall(0x472309, DisplayBonusHtHDmg1_hook); // MeleeWeap (display_stats_)
MakeJump(0x472546, DisplayBonusHtHDmg2_hack); // display_stats_
SafeWrite32(0x472558, 0x509EDC); // fmt: '%s %d-%d'
SafeWrite8(0x472552, 0x98 + 4);
SafeWrite8(0x47255F, 0x0C + 4);
SafeWrite8(0x472568, 0x10 + 4);
} }
dlogr(" Done", DL_INIT); dlogr(" Done", DL_INIT);
} }
// Display actual damage values for unarmed attacks (display_stats_ hacks)
MakeJump(0x472546, DisplayBonusHtHDmg2_hack);
SafeWrite32(0x472558, 0x509EDC); // fmt: '%s %d-%d'
SafeWrite8(0x472552, 0x98 + 4);
SafeWrite8(0x47255F, 0x0C + 4);
SafeWrite8(0x472568, 0x10 + 4);
} }
+11
View File
@@ -739,6 +739,17 @@ enum HandSlot : unsigned long
HANDSLOT_Right = 1 HANDSLOT_Right = 1
}; };
enum HandSlotMode : long
{
HANDMODE_Unset = 0,
HANDMODE_Primary = 1,
HANDMODE_Primary_Aimed = 2,
HANDMODE_Secondary = 3,
HANDMODE_Secondary_Aimed = 4,
HANDMODE_Reload = 5,
HANDMODE_UnkMode = 6
};
enum RollResult enum RollResult
{ {
ROLL_CRITICAL_FAILURE = 0x0, ROLL_CRITICAL_FAILURE = 0x0,
+19 -7
View File
@@ -617,6 +617,18 @@ __declspec(noinline) TGameObj* __stdcall GetItemPtrSlot(TGameObj* critter, Inven
return itemPtr; return itemPtr;
} }
AttackType GetHandSlotPrimaryAttack(HandSlot slot) {
return (AttackType)ptr_itemButtonItems[slot].primaryAttack;
}
AttackType GetHandSlotSecondaryAttack(HandSlot slot) {
return (AttackType)ptr_itemButtonItems[slot].secondaryAttack;
}
HandSlotMode GetHandSlotMode(HandSlot slot) {
return (HandSlotMode)ptr_itemButtonItems[slot].mode;
}
long& GetActiveItemMode() { long& GetActiveItemMode() {
return ptr_itemButtonItems[*ptr_itemCurrentItem].mode; return ptr_itemButtonItems[*ptr_itemCurrentItem].mode;
} }
@@ -627,13 +639,13 @@ TGameObj* GetActiveItem() {
AttackType GetSlotHitMode(HandSlot hand) { // 0 - left, 1 - right AttackType GetSlotHitMode(HandSlot hand) { // 0 - left, 1 - right
switch (ptr_itemButtonItems[hand].mode) { switch (ptr_itemButtonItems[hand].mode) {
case 1: case HANDMODE_Primary:
case 2: // called shot case HANDMODE_Primary_Aimed: // called shot
return (AttackType)ptr_itemButtonItems[hand].primaryAttack; return GetHandSlotPrimaryAttack(hand);
case 3: case HANDMODE_Secondary:
case 4: // called shot case HANDMODE_Secondary_Aimed: // called shot
return (AttackType)ptr_itemButtonItems[hand].secondaryAttack; return GetHandSlotSecondaryAttack(hand);
case 5: // reload mode case HANDMODE_Reload:
return (AttackType)(ATKTYPE_LWEAPON_RELOAD + hand); return (AttackType)(ATKTYPE_LWEAPON_RELOAD + hand);
} }
return ATKTYPE_PUNCH; return ATKTYPE_PUNCH;
+4
View File
@@ -587,6 +587,10 @@ long GetItemType(TGameObj* item);
__declspec(noinline) TGameObj* __stdcall GetItemPtrSlot(TGameObj* critter, InvenType slot); __declspec(noinline) TGameObj* __stdcall GetItemPtrSlot(TGameObj* critter, InvenType slot);
AttackType GetHandSlotPrimaryAttack(HandSlot slot);
AttackType GetHandSlotSecondaryAttack(HandSlot slot);
HandSlotMode GetHandSlotMode(HandSlot slot);
long& GetActiveItemMode(); long& GetActiveItemMode();
TGameObj* GetActiveItem(); TGameObj* GetActiveItem();
+3
View File
@@ -203,8 +203,11 @@ WRAP_WATCOM_FUNC2(long, scr_ptr, long, scriptId, TScript**, scriptPtr) // Return
WRAP_WATCOM_FUNC1(long, scr_remove, long, scriptID) WRAP_WATCOM_FUNC1(long, scr_remove, long, scriptID)
WRAP_WATCOM_FUNC1(void, set_focus_func, void*, func) WRAP_WATCOM_FUNC1(void, set_focus_func, void*, func)
WRAP_WATCOM_FUNC1(long, skill_is_tagged, long, skill) WRAP_WATCOM_FUNC1(long, skill_is_tagged, long, skill)
WRAP_WATCOM_FUNC2(long, skill_level, TGameObj*, critter, long, statID)
WRAP_WATCOM_FUNC2(long, stat_get_base, TGameObj*, critter, long, statID) WRAP_WATCOM_FUNC2(long, stat_get_base, TGameObj*, critter, long, statID)
WRAP_WATCOM_FUNC2(long, stat_get_base_direct, TGameObj*, critter, long, statID) WRAP_WATCOM_FUNC2(long, stat_get_base_direct, TGameObj*, critter, long, statID)
WRAP_WATCOM_FUNC2(long, stat_get_bonus, TGameObj*, critter, long, statID)
WRAP_WATCOM_FUNC3(long, stat_set_bonus, TGameObj*, critter, long, statID, long, amount)
WRAP_WATCOM_FUNC2(void, skill_get_tags, long*, tags, long, num) WRAP_WATCOM_FUNC2(void, skill_get_tags, long*, tags, long, num)
WRAP_WATCOM_FUNC2(void, skill_set_tags, long*, tags, long, num) WRAP_WATCOM_FUNC2(void, skill_set_tags, long*, tags, long, num)
WRAP_WATCOM_FUNC2(long, stat_level, TGameObj*, critter, long, statId) WRAP_WATCOM_FUNC2(long, stat_level, TGameObj*, critter, long, statId)
+4 -1
View File
@@ -669,9 +669,12 @@ void Inventory_Init() {
} }
} }
// Adjust the max text width of the total weight display on the inventory screen // Adjust the maximum text width of the total weight display on the inventory screen
SafeWrite32(0x472632, widthWeight); SafeWrite32(0x472632, widthWeight);
// Adjust the maximum text width of the unarmed attack display on the inventory screen
SafeWrite8(0x472576, 150);
if (GetConfigInt("Misc", "SuperStimExploitFix", 0)) { if (GetConfigInt("Misc", "SuperStimExploitFix", 0)) {
Translate_Get("sfall", "SuperStimExploitMsg", "You cannot use this item on someone who is not injured!", superStimMsg); Translate_Get("sfall", "SuperStimExploitMsg", "You cannot use this item on someone who is not injured!", superStimMsg);
MakeCall(0x49C3D9, protinst_use_item_on_hack); MakeCall(0x49C3D9, protinst_use_item_on_hack);
+83 -30
View File
@@ -23,6 +23,12 @@
#include "DamageMod.h" #include "DamageMod.h"
static struct {
AttackType primaryHit;
AttackType secondaryHit;
long mode;
} slotHitData[2];
class Hits { class Hits {
public: public:
static const long count = 20; static const long count = 20;
@@ -40,7 +46,6 @@ private:
bool isPenetrate; // compute_damage_ bool isPenetrate; // compute_damage_
bool isSecondary; bool isSecondary;
// HitsData() : reqLevel(0), reqSkill(0), reqStat(), minDamage(1), maxDamage(2), bonusDamage(0), bonusCrit(0), costAP(3), isPenetrate(false), isSecondary(false) {}
HitsData() { HitsData() {
reqLevel = 0; reqLevel = 0;
reqSkill = 0; reqSkill = 0;
@@ -231,7 +236,7 @@ Hits unarmed;
static bool UnarmedReqStats(AttackType hit) { static bool UnarmedReqStats(AttackType hit) {
if (unarmed.SkillLevel() >= unarmed.Hit(hit).reqSkill && (long)*ptr_Level_pc >= unarmed.Hit(hit).reqLevel) { if (unarmed.SkillLevel() >= unarmed.Hit(hit).reqSkill && (long)*ptr_Level_pc >= unarmed.Hit(hit).reqLevel) {
for (size_t stat = 0; stat < STAT_base_count; stat++) { for (size_t stat = 0; stat < STAT_base_count; stat++) {
if (unarmed.Hit(hit).reqStat[stat] == 0) continue; if (unarmed.Hit(hit).reqStat[stat] <= 0) continue;
if (unarmed.Hit(hit).reqStat[stat] > unarmed.DudeStat(stat)) { if (unarmed.Hit(hit).reqStat[stat] > unarmed.DudeStat(stat)) {
return false; return false;
} }
@@ -241,29 +246,20 @@ static bool UnarmedReqStats(AttackType hit) {
return false; return false;
} }
static bool Punching(bool isPrimary) { static AttackType GetPunchingHit(bool isPrimary) {
for (size_t i = 0; i < 6; i++) { for (size_t i = 0; i < 6; i++) {
AttackType hit = unarmed.GetSortHit(i); AttackType hit = unarmed.GetSortHit(i);
if (unarmed.Hit(hit).isSecondary == isPrimary) continue; if (unarmed.Hit(hit).isSecondary != isPrimary && UnarmedReqStats(hit)) return hit;
if (UnarmedReqStats(hit)) {
if (unarmed.Hit(hit).isSecondary) {
ptr_itemButtonItems[HANDSLOT_Left].secondaryAttack = hit;
} else {
ptr_itemButtonItems[HANDSLOT_Left].primaryAttack = hit;
}
return true;
}
} }
return false; return ATKTYPE_PUNCH;
} }
// Punch hits // Punch hits
static void __fastcall check_unarmed_left_slot(long skillLevel) { static void __fastcall check_unarmed_left_slot(long skillLevel) {
unarmed.GetDudeStats(skillLevel); unarmed.GetDudeStats(skillLevel);
if (!Punching(true)) ptr_itemButtonItems[HANDSLOT_Left].primaryAttack = ATKTYPE_PUNCH; ptr_itemButtonItems[HANDSLOT_Left].primaryAttack = GetPunchingHit(true);
if (!Punching(false)) ptr_itemButtonItems[HANDSLOT_Left].secondaryAttack = ATKTYPE_PUNCH; ptr_itemButtonItems[HANDSLOT_Left].secondaryAttack = GetPunchingHit(false);
} }
static void __declspec(naked) intface_update_items_hack_punch() { static void __declspec(naked) intface_update_items_hack_punch() {
@@ -273,27 +269,18 @@ static void __declspec(naked) intface_update_items_hack_punch() {
} }
} }
static bool Kicking(bool isPrimary) { static AttackType GetKickingHit(bool isPrimary) {
for (size_t i = 6; i < 12; i++) { for (size_t i = 6; i < 12; i++) {
AttackType hit = unarmed.GetSortHit(i); AttackType hit = unarmed.GetSortHit(i);
if (unarmed.Hit(hit).isSecondary == isPrimary) continue; if (unarmed.Hit(hit).isSecondary != isPrimary && UnarmedReqStats(hit)) return hit;
if (UnarmedReqStats(hit)) {
if (unarmed.Hit(hit).isSecondary) {
ptr_itemButtonItems[HANDSLOT_Right].secondaryAttack = hit;
} else {
ptr_itemButtonItems[HANDSLOT_Right].primaryAttack = hit;
}
return true;
}
} }
return false; return ATKTYPE_KICK;
} }
// Kick hits // Kick hits
static void check_unarmed_right_slot() { static void check_unarmed_right_slot() {
if (!Kicking(true)) ptr_itemButtonItems[HANDSLOT_Right].primaryAttack = ATKTYPE_KICK; ptr_itemButtonItems[HANDSLOT_Right].primaryAttack = GetKickingHit(true);
if (!Kicking(false)) ptr_itemButtonItems[HANDSLOT_Right].secondaryAttack = ATKTYPE_KICK; ptr_itemButtonItems[HANDSLOT_Right].secondaryAttack = GetKickingHit(false);
} }
static void __declspec(naked) intface_update_items_hack_kick() { static void __declspec(naked) intface_update_items_hack_kick() {
@@ -350,6 +337,69 @@ long Unarmed_GetHitAPCost(AttackType hit) {
return unarmed.Hit(hit).apCost; return unarmed.Hit(hit).apCost;
} }
long Unarmed_GetDamage(AttackType hit, long &minOut, long &maxOut) {
minOut = unarmed.Hit(hit).minDamage;
maxOut = unarmed.Hit(hit).maxDamage;
return unarmed.Hit(hit).bonusDamage;
}
////////////////////////////////////////////////////////////////////////////////
static AttackType GetPunchingHit() {
unarmed.GetDudeStats(fo_skill_level(*ptr_obj_dude, SKILL_UNARMED_COMBAT));
return GetPunchingHit(true);
}
static AttackType GetKickingHit() {
//unarmed.GetDudeStats(fo_skill_level(*ptr_obj_dude, SKILL_UNARMED_COMBAT));
return GetKickingHit(true);
}
static void SlotsStoreCurrentHitMode() {
slotHitData[HANDSLOT_Left].primaryHit = GetHandSlotPrimaryAttack(HANDSLOT_Left);
slotHitData[HANDSLOT_Left].secondaryHit = GetHandSlotSecondaryAttack(HANDSLOT_Left);
slotHitData[HANDSLOT_Left].mode = GetHandSlotMode(HANDSLOT_Left);
slotHitData[HANDSLOT_Right].primaryHit = GetHandSlotPrimaryAttack(HANDSLOT_Right);
slotHitData[HANDSLOT_Right].secondaryHit = GetHandSlotSecondaryAttack(HANDSLOT_Right);
slotHitData[HANDSLOT_Right].mode = GetHandSlotMode(HANDSLOT_Right);
}
AttackType Unarmed_GetStoredHitMode(HandSlot slot) {
AttackType hit;
switch (slotHitData[slot].mode) {
case HANDMODE_Primary:
case HANDMODE_Primary_Aimed: // called shot
hit = slotHitData[slot].primaryHit;
break;
case HANDMODE_Secondary:
case HANDMODE_Secondary_Aimed: // called shot
hit = slotHitData[slot].secondaryHit;
break;
}
if (hit < ATKTYPE_STRONGPUNCH && hit != ATKTYPE_PUNCH && hit != ATKTYPE_KICK) {
hit = (slot == HANDSLOT_Left) ? GetPunchingHit() : GetKickingHit(); // get Primary
if (slot == HANDSLOT_Left) {
slotHitData[HANDSLOT_Left].primaryHit = hit;
slotHitData[HANDSLOT_Left].mode = HANDMODE_Primary;
} else {
slotHitData[HANDSLOT_Right].primaryHit = hit;
slotHitData[HANDSLOT_Right].mode = HANDMODE_Primary;
}
}
return hit;
}
static void __declspec(naked) handle_inventory_hook() {
__asm {
call SlotsStoreCurrentHitMode;
jmp display_stats_;
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static void __declspec(naked) statPCAddExperienceCheckPMs_hook() { static void __declspec(naked) statPCAddExperienceCheckPMs_hook() {
@@ -437,6 +487,9 @@ void Unarmed_Init() {
MakeCall(0x4248B6, check_unarmed_penetrate, 5); MakeCall(0x4248B6, check_unarmed_penetrate, 5);
SafeWrite16(0x4248C1, 0x01F8); // cmp eax, 1 SafeWrite16(0x4248C1, 0x01F8); // cmp eax, 1
SafeWrite8(0x4248C8, CODETYPE_JumpShort); SafeWrite8(0x4248C8, CODETYPE_JumpShort);
// Store the current values of unarmed attack modes when opening the player's inventory
HookCall(0x46E8D4, handle_inventory_hook);
} }
//void Unarmed_Exit() { //void Unarmed_Exit() {
+3
View File
@@ -22,3 +22,6 @@ void Unarmed_Init();
//void Unarmed_Exit(); //void Unarmed_Exit();
long Unarmed_GetHitAPCost(AttackType hit); long Unarmed_GetHitAPCost(AttackType hit);
long Unarmed_GetDamage(AttackType hit, long &minOut, long &maxOut);
AttackType Unarmed_GetStoredHitMode(HandSlot slot);
+1 -1
View File
@@ -124,7 +124,7 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>Cdecl</CallingConvention> <CallingConvention>Cdecl</CallingConvention>
<ForcedIncludeFiles>stdafx.h;%(ForcedIncludeFiles)</ForcedIncludeFiles> <ForcedIncludeFiles>stdafx.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>