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 "Logging.h"
#include "Unarmed.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 const DWORD DisplayBonusHtHDmg2Exit = 0x47254E;
static const DWORD DisplayBonusHtHDmg2Exit = 0x47254F;
__asm {
mov ecx, eax;
call stat_level_;
add eax, 2;
push eax; // max dmg
mov edx, PERK_bonus_hth_damage;
mov eax, ecx;
call perk_level_;
shl eax, 1; // Multiply by 2
inc eax; // min dmg (1 + bonus)
call stat_level_; // get STAT_melee_dmg
push eax; // max dmg (meleeDmg)
mov edx, esp; // meleeDmg ref
push edi; // handOffset
call GetHtHDamage;
push eax; // min dmg
jmp DisplayBonusHtHDmg2Exit;
}
}
static bool bonusHtHDamageFix = false;
long DamageMod_GetHtHMinDamageBonus(TGameObj* source) {
return (bonusHtHDamageFix)
? 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);
int DisplayBonusDmg = GetConfigInt("Misc", "DisplayBonusDamage", 0);
bonusHtHDamageFix = GetConfigInt("Misc", "BonusHtHDamageFix", 1) != 0;
displayBonusDamage = GetConfigInt("Misc", "DisplayBonusDamage", 0) != 0;
if (BonusHtHDmgFix) {
bonusHtHDamageFix = true;
if (bonusHtHDamageFix) {
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[] = {
0x435C0C, // DisplayFix (ListDrvdStats_)
0x439921 // PrintFix (Save_as_ASCII_)
@@ -482,17 +496,19 @@ void DamageMod_Init() {
dlogr(" Done", DL_INIT);
}
if (DisplayBonusDmg) {
if (displayBonusDamage) {
dlog("Applying Display Bonus Damage patch.", DL_INIT);
HookCall(0x4722DD, DisplayBonusRangedDmg_hook); // display_stats_
if (BonusHtHDmgFix) {
HookCall(0x472309, DisplayBonusHtHDmg1_hook); // 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);
if (bonusHtHDamageFix) {
HookCall(0x472309, DisplayBonusHtHDmg1_hook); // MeleeWeap (display_stats_)
}
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
};
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
{
ROLL_CRITICAL_FAILURE = 0x0,
+19 -7
View File
@@ -617,6 +617,18 @@ __declspec(noinline) TGameObj* __stdcall GetItemPtrSlot(TGameObj* critter, Inven
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() {
return ptr_itemButtonItems[*ptr_itemCurrentItem].mode;
}
@@ -627,13 +639,13 @@ TGameObj* GetActiveItem() {
AttackType GetSlotHitMode(HandSlot hand) { // 0 - left, 1 - right
switch (ptr_itemButtonItems[hand].mode) {
case 1:
case 2: // called shot
return (AttackType)ptr_itemButtonItems[hand].primaryAttack;
case 3:
case 4: // called shot
return (AttackType)ptr_itemButtonItems[hand].secondaryAttack;
case 5: // reload mode
case HANDMODE_Primary:
case HANDMODE_Primary_Aimed: // called shot
return GetHandSlotPrimaryAttack(hand);
case HANDMODE_Secondary:
case HANDMODE_Secondary_Aimed: // called shot
return GetHandSlotSecondaryAttack(hand);
case HANDMODE_Reload:
return (AttackType)(ATKTYPE_LWEAPON_RELOAD + hand);
}
return ATKTYPE_PUNCH;
+4
View File
@@ -587,6 +587,10 @@ long GetItemType(TGameObj* item);
__declspec(noinline) TGameObj* __stdcall GetItemPtrSlot(TGameObj* critter, InvenType slot);
AttackType GetHandSlotPrimaryAttack(HandSlot slot);
AttackType GetHandSlotSecondaryAttack(HandSlot slot);
HandSlotMode GetHandSlotMode(HandSlot slot);
long& GetActiveItemMode();
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(void, set_focus_func, void*, func)
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_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_set_tags, long*, tags, long, num)
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);
// Adjust the maximum text width of the unarmed attack display on the inventory screen
SafeWrite8(0x472576, 150);
if (GetConfigInt("Misc", "SuperStimExploitFix", 0)) {
Translate_Get("sfall", "SuperStimExploitMsg", "You cannot use this item on someone who is not injured!", superStimMsg);
MakeCall(0x49C3D9, protinst_use_item_on_hack);
+83 -30
View File
@@ -23,6 +23,12 @@
#include "DamageMod.h"
static struct {
AttackType primaryHit;
AttackType secondaryHit;
long mode;
} slotHitData[2];
class Hits {
public:
static const long count = 20;
@@ -40,7 +46,6 @@ private:
bool isPenetrate; // compute_damage_
bool isSecondary;
// HitsData() : reqLevel(0), reqSkill(0), reqStat(), minDamage(1), maxDamage(2), bonusDamage(0), bonusCrit(0), costAP(3), isPenetrate(false), isSecondary(false) {}
HitsData() {
reqLevel = 0;
reqSkill = 0;
@@ -231,7 +236,7 @@ Hits unarmed;
static bool UnarmedReqStats(AttackType hit) {
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++) {
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)) {
return false;
}
@@ -241,29 +246,20 @@ static bool UnarmedReqStats(AttackType hit) {
return false;
}
static bool Punching(bool isPrimary) {
static AttackType GetPunchingHit(bool isPrimary) {
for (size_t i = 0; i < 6; i++) {
AttackType hit = unarmed.GetSortHit(i);
if (unarmed.Hit(hit).isSecondary == isPrimary) continue;
if (UnarmedReqStats(hit)) {
if (unarmed.Hit(hit).isSecondary) {
ptr_itemButtonItems[HANDSLOT_Left].secondaryAttack = hit;
} else {
ptr_itemButtonItems[HANDSLOT_Left].primaryAttack = hit;
}
return true;
}
if (unarmed.Hit(hit).isSecondary != isPrimary && UnarmedReqStats(hit)) return hit;
}
return false;
return ATKTYPE_PUNCH;
}
// Punch hits
static void __fastcall check_unarmed_left_slot(long skillLevel) {
unarmed.GetDudeStats(skillLevel);
if (!Punching(true)) ptr_itemButtonItems[HANDSLOT_Left].primaryAttack = ATKTYPE_PUNCH;
if (!Punching(false)) ptr_itemButtonItems[HANDSLOT_Left].secondaryAttack = ATKTYPE_PUNCH;
ptr_itemButtonItems[HANDSLOT_Left].primaryAttack = GetPunchingHit(true);
ptr_itemButtonItems[HANDSLOT_Left].secondaryAttack = GetPunchingHit(false);
}
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++) {
AttackType hit = unarmed.GetSortHit(i);
if (unarmed.Hit(hit).isSecondary == isPrimary) continue;
if (UnarmedReqStats(hit)) {
if (unarmed.Hit(hit).isSecondary) {
ptr_itemButtonItems[HANDSLOT_Right].secondaryAttack = hit;
} else {
ptr_itemButtonItems[HANDSLOT_Right].primaryAttack = hit;
}
return true;
}
if (unarmed.Hit(hit).isSecondary != isPrimary && UnarmedReqStats(hit)) return hit;
}
return false;
return ATKTYPE_KICK;
}
// Kick hits
static void check_unarmed_right_slot() {
if (!Kicking(true)) ptr_itemButtonItems[HANDSLOT_Right].primaryAttack = ATKTYPE_KICK;
if (!Kicking(false)) ptr_itemButtonItems[HANDSLOT_Right].secondaryAttack = ATKTYPE_KICK;
ptr_itemButtonItems[HANDSLOT_Right].primaryAttack = GetKickingHit(true);
ptr_itemButtonItems[HANDSLOT_Right].secondaryAttack = GetKickingHit(false);
}
static void __declspec(naked) intface_update_items_hack_kick() {
@@ -350,6 +337,69 @@ long Unarmed_GetHitAPCost(AttackType hit) {
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() {
@@ -437,6 +487,9 @@ void Unarmed_Init() {
MakeCall(0x4248B6, check_unarmed_penetrate, 5);
SafeWrite16(0x4248C1, 0x01F8); // cmp eax, 1
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() {
+3
View File
@@ -22,3 +22,6 @@ void Unarmed_Init();
//void Unarmed_Exit();
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>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>Cdecl</CallingConvention>
<ForcedIncludeFiles>stdafx.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<FunctionLevelLinking>true</FunctionLevelLinking>