mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Backported Unarmed module and UnarmedFile option from 4.x
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
; This file lets you configure the requirements and effects of unarmed attacks
|
||||
|
||||
; Strong Punch
|
||||
[8]
|
||||
ReqLevel=1 ; required player level
|
||||
SkillLevel=55 ; required Unarmed skill level
|
||||
|
||||
; Required stats
|
||||
Stat0=0 ; ST
|
||||
Stat1=0 ; PE
|
||||
Stat2=0 ; EN
|
||||
Stat3=0 ; CH
|
||||
Stat4=0 ; IN
|
||||
Stat5=6 ; AG
|
||||
Stat6=0 ; LK
|
||||
|
||||
MinDamage=1
|
||||
MaxDamage=2
|
||||
BonusDamage=3 ; bonus damage added to the minimum and maximum damage
|
||||
BonusCrit=0 ; value of the critical hit chance
|
||||
APCost= ; you can leave the value empty or omit the parameter to use the engine default value
|
||||
|
||||
Penetrate=0 ; set to 1 to make the attack reduce target's armor DT (the formula is determined by the engine or a hook script)
|
||||
Secondary=0 ; set to 1 to assign the attack to the secondary mode (be careful when setting this parameter)
|
||||
|
||||
; Hammer Punch
|
||||
[9]
|
||||
|
||||
; Haymaker
|
||||
[10]
|
||||
|
||||
; Jab
|
||||
[11]
|
||||
|
||||
; Palm Strike
|
||||
[12]
|
||||
|
||||
; Piercing Strike
|
||||
[13]
|
||||
|
||||
; Strong Kick
|
||||
[14]
|
||||
|
||||
; Snap Kick
|
||||
[15]
|
||||
|
||||
; Power Kick
|
||||
[16]
|
||||
|
||||
; Hip Kick
|
||||
[17]
|
||||
|
||||
; Hook Kick
|
||||
[18]
|
||||
|
||||
; Piercing Kick
|
||||
[19]
|
||||
|
||||
; Default attacks (any requirements are ignored)
|
||||
; Punch
|
||||
[4]
|
||||
|
||||
; Kick
|
||||
[5]
|
||||
+5
-2
@@ -790,8 +790,11 @@ CreditsAtBottom=0
|
||||
;Point to an ini file containing elevator data
|
||||
;ElevatorsFile=sfall\Elevators.ini
|
||||
|
||||
;Allows you to change some engine parameters for the game mechanics
|
||||
;See the Tweaks.ini in the modders pack for an example file
|
||||
;Allows you to change the requirements and effects of unarmed attacks
|
||||
;See the Unarmed.ini in the modders pack for an example file
|
||||
;UnarmedFile=sfall\Unarmed.ini
|
||||
|
||||
;To change some engine parameters for the game mechanics, uncomment the next line
|
||||
;TweaksFile=sfall\Tweaks.ini
|
||||
|
||||
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
+3
-3
@@ -113,7 +113,7 @@ isFloat:
|
||||
jmp NegateFixHack_Back;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static void __declspec(naked) compute_attack_hack() {
|
||||
static const DWORD UnarmedAttacksFixEnd = 0x423A0D;
|
||||
__asm {
|
||||
@@ -150,7 +150,7 @@ end:
|
||||
jmp UnarmedAttacksFixEnd;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
static void __declspec(naked) SharpShooterFix() {
|
||||
__asm {
|
||||
call stat_level_ // Perception
|
||||
@@ -3161,7 +3161,7 @@ void BugFixes_Init()
|
||||
|
||||
//if (GetConfigInt("Misc", "SpecialUnarmedAttacksFix", 1)) {
|
||||
dlog("Applying Special Unarmed Attacks fix.", DL_FIX);
|
||||
MakeJump(0x42394D, compute_attack_hack);
|
||||
//MakeJump(0x42394D, compute_attack_hack); - implementation moved to Unarmed module
|
||||
dlogr(" Done", DL_FIX);
|
||||
//}
|
||||
|
||||
|
||||
+15
-3
@@ -21,6 +21,8 @@
|
||||
#include "FalloutEngine.h"
|
||||
#include "Logging.h"
|
||||
|
||||
#include "ReplacementFuncs.h"
|
||||
|
||||
static int formula;
|
||||
|
||||
static const DWORD DamageFunctionReturn = 0x424A63;
|
||||
@@ -362,7 +364,7 @@ static __declspec(naked) void CommonDmgRngDispFix_hook() {
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static __declspec(naked) void HtHDamageFix1a_hack() {
|
||||
__asm {
|
||||
cmp ecx, dword ptr ds:[FO_VAR_obj_dude]; // Is the critter == PC?
|
||||
@@ -378,7 +380,7 @@ fix:
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
static __declspec(naked) void HtHDamageFix1b_hook() {
|
||||
__asm {
|
||||
call stat_level_; // Get Total_Melee_Damage
|
||||
@@ -436,6 +438,14 @@ static void __declspec(naked) DisplayBonusHtHDmg2_hack() {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
: 0;
|
||||
}
|
||||
|
||||
void DamageMod_Init() {
|
||||
if (formula = GetConfigInt("Misc", "DamageFormula", 0)) {
|
||||
switch (formula) {
|
||||
@@ -451,7 +461,9 @@ void DamageMod_Init() {
|
||||
|
||||
int BonusHtHDmgFix = GetConfigInt("Misc", "BonusHtHDamageFix", 1);
|
||||
int DisplayBonusDmg = GetConfigInt("Misc", "DisplayBonusDamage", 0);
|
||||
|
||||
if (BonusHtHDmgFix) {
|
||||
bonusHtHDamageFix = true;
|
||||
dlog("Applying Bonus HtH Damage Perk fix.", DL_INIT);
|
||||
if (DisplayBonusDmg == 0) { // Subtract damage from perk bonus (vanilla displaying)
|
||||
const DWORD meleeDmgDispPrtAddr[] = {
|
||||
@@ -465,7 +477,7 @@ void DamageMod_Init() {
|
||||
};
|
||||
HookCalls(CommonDmgRngDispFix_hook, commonDmgRngDispAddr);
|
||||
}
|
||||
MakeCall(0x478492, HtHDamageFix1a_hack); // Unarmed (item_w_damage_)
|
||||
//MakeCall(0x478492, HtHDamageFix1a_hack); // Unarmed (item_w_damage_)
|
||||
HookCall(0x47854C, HtHDamageFix1b_hook); // MeleeWeap (item_w_damage_)
|
||||
dlogr(" Done", DL_INIT);
|
||||
}
|
||||
|
||||
@@ -19,3 +19,5 @@
|
||||
#pragma once
|
||||
|
||||
void DamageMod_Init();
|
||||
|
||||
long DamageMod_GetHtHMinDamageBonus(TGameObj* source);
|
||||
|
||||
+12
-11
@@ -625,24 +625,25 @@ TGameObj* GetActiveItem() {
|
||||
return ptr_itemButtonItems[*ptr_itemCurrentItem].item;
|
||||
}
|
||||
|
||||
long GetCurrentAttackMode() {
|
||||
long hitMode = -1;
|
||||
if (*ptr_interfaceWindow != -1) {
|
||||
long activeHand = *ptr_itemCurrentItem; // 0 - left, 1 - right
|
||||
switch (ptr_itemButtonItems[activeHand].mode) {
|
||||
AttackType GetSlotHitMode(HandSlot hand) { // 0 - left, 1 - right
|
||||
switch (ptr_itemButtonItems[hand].mode) {
|
||||
case 1:
|
||||
case 2: // called shot
|
||||
hitMode = ptr_itemButtonItems[activeHand].primaryAttack;
|
||||
break;
|
||||
return (AttackType)ptr_itemButtonItems[hand].primaryAttack;
|
||||
case 3:
|
||||
case 4: // called shot
|
||||
hitMode = ptr_itemButtonItems[activeHand].secondaryAttack;
|
||||
break;
|
||||
return (AttackType)ptr_itemButtonItems[hand].secondaryAttack;
|
||||
case 5: // reload mode
|
||||
hitMode = ATKTYPE_LWEAPON_RELOAD + activeHand;
|
||||
return (AttackType)(ATKTYPE_LWEAPON_RELOAD + hand);
|
||||
}
|
||||
return ATKTYPE_PUNCH;
|
||||
}
|
||||
|
||||
long GetCurrentAttackMode() {
|
||||
if (*ptr_interfaceWindow != -1) {
|
||||
return GetSlotHitMode((HandSlot)*ptr_itemCurrentItem);
|
||||
}
|
||||
return hitMode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
AttackSubType GetWeaponType(DWORD weaponFlag) {
|
||||
|
||||
@@ -591,6 +591,9 @@ long& GetActiveItemMode();
|
||||
|
||||
TGameObj* GetActiveItem();
|
||||
|
||||
// Hand: 0 - left, 1 - right
|
||||
AttackType GetSlotHitMode(HandSlot hand);
|
||||
|
||||
long GetCurrentAttackMode();
|
||||
|
||||
AttackSubType GetWeaponType(DWORD weaponFlag);
|
||||
|
||||
@@ -281,6 +281,7 @@ FUNC(interpret_, 0x46CCA4)
|
||||
FUNC(intface_disable_, 0x45EAFC)
|
||||
FUNC(intface_enable_, 0x45EA64)
|
||||
FUNC(intface_get_attack_, 0x45EF6C)
|
||||
FUNC(intface_get_item_states_, 0x45F4B4)
|
||||
FUNC(intface_hide_, 0x45E9E0)
|
||||
FUNC(intface_is_hidden_, 0x45EA5C)
|
||||
FUNC(intface_is_item_right_hand_, 0x45F7FC)
|
||||
|
||||
+19
-20
@@ -263,7 +263,7 @@ long __stdcall CalcApCostHook_Invoke(TGameObj* source, long hitMode, long isCall
|
||||
? CalcApCostHook_Script(source, hitMode, isCalled, cost, weapon)
|
||||
: cost;
|
||||
}
|
||||
|
||||
/*
|
||||
static void __declspec(naked) CalcApCostHook() {
|
||||
__asm {
|
||||
HookBegin;
|
||||
@@ -290,7 +290,7 @@ static void __declspec(naked) CalcApCostHook() {
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
// this is for using non-weapon items, always 2 AP in vanilla
|
||||
static void __declspec(naked) CalcApCostHook2() {
|
||||
__asm {
|
||||
@@ -619,7 +619,6 @@ defaultHandler:
|
||||
jmp protinst_use_item_;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static void RemoveInvenObjHook_Script(TGameObj* source, TGameObj* item, long count, long rmType) {
|
||||
BeginHook();
|
||||
@@ -639,7 +638,6 @@ void RemoveInvenObjHook_Invoke(TGameObj* source, TGameObj* item, long count, lon
|
||||
if (HookHasScript(HOOK_REMOVEINVENOBJ)) RemoveInvenObjHook_Script(source, item, count, rmType);
|
||||
}
|
||||
*/
|
||||
|
||||
static long rmObjType = -1;
|
||||
|
||||
void __stdcall SetRemoveObjectType(long rmType) {
|
||||
@@ -914,9 +912,10 @@ static void __declspec(naked) ItemDamageHook() {
|
||||
}
|
||||
argCount = 6;
|
||||
|
||||
if (args[2] == 0) { // weapon arg
|
||||
args[4] += 8; // type arg
|
||||
}
|
||||
// tweak for 0x4784AA (obsolete)
|
||||
//if (args[2] == 0) { // weapon arg
|
||||
// args[4] += 8; // type arg
|
||||
//}
|
||||
|
||||
RunHookScript(HOOK_ITEMDAMAGE);
|
||||
|
||||
@@ -1872,19 +1871,19 @@ void HookScripts_Init() {
|
||||
MakeCall(0x423893, AfterHitRollHook);
|
||||
|
||||
// HOOK_CALCAPCOST
|
||||
const DWORD calcApCostHkAddr[] = {
|
||||
0x42307A,
|
||||
0x42669F,
|
||||
0x42687B,
|
||||
0x42A625,
|
||||
0x42A655,
|
||||
0x42A686,
|
||||
0x42AE32,
|
||||
0x42AE71,
|
||||
0x460048,
|
||||
0x47807B
|
||||
};
|
||||
HookCalls(CalcApCostHook, calcApCostHkAddr);
|
||||
//const DWORD calcApCostHkAddr[] = {
|
||||
// 0x42307A,
|
||||
// 0x42669F,
|
||||
// 0x42687B,
|
||||
// 0x42A625,
|
||||
// 0x42A655,
|
||||
// 0x42A686,
|
||||
// 0x42AE32,
|
||||
// 0x42AE71,
|
||||
// 0x460048,
|
||||
// 0x47807B
|
||||
//};
|
||||
//HookCalls(CalcApCostHook, calcApCostHkAddr);
|
||||
MakeCall(0x478083, CalcApCostHook2);
|
||||
|
||||
// HOOK_DEATHANIM1, HOOK_DEATHANIM2
|
||||
|
||||
+11
-9
@@ -1015,7 +1015,7 @@ static void PerkAndTraitSetup() {
|
||||
HookCall(0x4248F9, BlockedTrait); // compute_damage_
|
||||
break;
|
||||
case TRAIT_fast_shot:
|
||||
HookCall(0x478C8A, BlockedTrait); // item_w_mp_cost_
|
||||
//HookCall(0x478C8A, BlockedTrait); // item_w_mp_cost_ (obsolete)
|
||||
HookCall(0x478E70, BlockedTrait); // item_w_called_shot_
|
||||
break;
|
||||
case TRAIT_bloody_mess:
|
||||
@@ -1063,7 +1063,7 @@ dlgExit:
|
||||
jmp perks_dialog_Ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static void __declspec(naked) item_w_mp_cost_hook() {
|
||||
__asm {
|
||||
call item_w_range_;
|
||||
@@ -1078,7 +1078,7 @@ checkType:
|
||||
jmp item_w_subtype_; // eax - item
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
// Haenlomal's tweak
|
||||
static void __declspec(naked) item_w_called_shot_hack() {
|
||||
static const DWORD FastShotTraitFix_End = 0x478E7F;
|
||||
@@ -1115,20 +1115,22 @@ static void FastShotTraitFix() {
|
||||
goto fix;
|
||||
case 2: {
|
||||
dlog("Applying Fast Shot trait patch (Alternative behavior).", DL_INIT);
|
||||
SafeWrite16(0x478C9F, 0x9090);
|
||||
const DWORD fastShotFixF1[] = {0x478BB8, 0x478BC7, 0x478BD6, 0x478BEA, 0x478BF9, 0x478C08, 0x478C2F};
|
||||
HookCalls((void*)0x478C7D, fastShotFixF1);
|
||||
/* Implemented in sfall item_w_mp_cost function */
|
||||
//SafeWrite16(0x478C9F, 0x9090); // item_w_mp_cost_
|
||||
//const DWORD fastShotFixF1[] = {0x478BB8, 0x478BC7, 0x478BD6, 0x478BEA, 0x478BF9, 0x478C08, 0x478C2F};
|
||||
//HookCalls((void*)0x478C7D, fastShotFixF1); // jmp 0x478C7D
|
||||
goto done;
|
||||
}
|
||||
case 3:
|
||||
dlog("Applying Fast Shot trait patch (Fallout 1 behavior).", DL_INIT);
|
||||
HookCall(0x478C97, (void*)item_hit_with_);
|
||||
SafeWrite16(0x478C9E, CODETYPE_JumpZ << 8); // ignore all unarmed attacks (cmp eax, 0; jz)
|
||||
/* Implemented in sfall item_w_mp_cost function */
|
||||
//HookCall(0x478C97, (void*)item_hit_with_);
|
||||
//SafeWrite16(0x478C9E, CODETYPE_JumpZ << 8); // ignore all unarmed attacks (cmp eax, 0; jz)
|
||||
goto done;
|
||||
default:
|
||||
dlog("Applying Fast Shot trait fix.", DL_INIT);
|
||||
fix:
|
||||
HookCall(0x478C97, item_w_mp_cost_hook);
|
||||
//HookCall(0x478C97, item_w_mp_cost_hook); - Fix implemented in sfall item_w_mp_cost function
|
||||
done:
|
||||
dlogr(" Done", DL_INIT);
|
||||
}
|
||||
|
||||
+97
-34
@@ -26,6 +26,7 @@
|
||||
#include "HookScripts.h"
|
||||
#include "PartyControl.h"
|
||||
#include "Perks.h"
|
||||
#include "Unarmed.h"
|
||||
|
||||
#include "ReplacementFuncs.h"
|
||||
|
||||
@@ -295,7 +296,8 @@ static void __declspec(naked) adjust_fid_hack() {
|
||||
|
||||
//////////////////////////////////// ITEMS /////////////////////////////////////
|
||||
|
||||
static const int reloadCostAP = 2; // engine default reload AP cost
|
||||
static const int reloadAPCost = 2; // engine default reload AP cost
|
||||
static const int unarmedAPCost = 3; // engine default use item AP cost
|
||||
|
||||
static std::tr1::array<long, 3> healingItemPids = {PID_STIMPAK, PID_SUPER_STIMPAK, PID_HEALING_POWDER};
|
||||
|
||||
@@ -321,7 +323,7 @@ bool __fastcall sfgame_IsHealingItem(TGameObj* item) {
|
||||
}
|
||||
|
||||
bool __stdcall sfgame_UseDrugItemFunc(TGameObj* source, TGameObj* item) {
|
||||
bool result = (sfgame_item_d_take_drug(source, item) == -1);
|
||||
bool result = (sfgame_item_d_take_drug(source, item) == -1); // HOOK_USEOBJON
|
||||
if (result) {
|
||||
fo_item_add_force(source, item, 1);
|
||||
} else {
|
||||
@@ -370,7 +372,7 @@ long __stdcall sfgame_item_weapon_range(TGameObj* source, TGameObj* weapon, long
|
||||
long type = GetWeaponType(flagExt);
|
||||
|
||||
if (type == ATKSUBTYPE_THROWING) {
|
||||
long heaveHoMod = fo_perk_level(source, PERK_heave_ho);
|
||||
long heaveHoMod = sfgame_perk_level(source, PERK_heave_ho);
|
||||
long stRange = fo_stat_level(source, STAT_st);
|
||||
|
||||
if (perkHeaveHoModTweak) {
|
||||
@@ -393,7 +395,34 @@ long __stdcall sfgame_item_weapon_range(TGameObj* source, TGameObj* weapon, long
|
||||
// return sfgame_item_weapon_range(source, fo_item_hit_with(source, hitMode), hitMode);
|
||||
//}
|
||||
|
||||
static bool fastShotTweak = false;
|
||||
static int fastShotTweak;
|
||||
|
||||
static long item_w_mp_cost_sub(TGameObj* source, TGameObj* item, long hitMode, long isCalled, long cost) {
|
||||
if (isCalled) cost++;
|
||||
if (cost < 0) cost = 0;
|
||||
|
||||
long type = fo_item_w_subtype(item, hitMode);
|
||||
|
||||
if (source->protoId == PID_Player && DudeHasTrait(TRAIT_fast_shot)) {
|
||||
// Alternative behaviors of the Fast Shot trait
|
||||
if (item && fastShotTweak > 2) { // Fallout 1 behavior (allowed for all weapons)
|
||||
cost--;
|
||||
} else if (fastShotTweak == 2) { // Alternative behavior (allowed for all attacks)
|
||||
cost--;
|
||||
} else if (fastShotTweak < 2 && type > ATKSUBTYPE_MELEE && fo_item_w_range(source, hitMode) >= 2) { // Fallout 2 behavior (with Haenlomal's fix)
|
||||
cost--;
|
||||
}
|
||||
}
|
||||
if ((type == ATKSUBTYPE_MELEE || type == ATKSUBTYPE_UNARMED) && sfgame_perk_level(source, PERK_bonus_hth_attacks)) {
|
||||
cost--;
|
||||
}
|
||||
if (type == ATKSUBTYPE_GUNS && sfgame_perk_level(source, PERK_bonus_rate_of_fire)) {
|
||||
cost--;
|
||||
}
|
||||
if (cost < 1) cost = 1;
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
// Implementation of item_w_primary_mp_cost_ and item_w_secondary_mp_cost_ engine functions in a single function with the HOOK_CALCAPCOST hook
|
||||
long __fastcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, long hitMode, long isCalled) {
|
||||
@@ -411,40 +440,18 @@ long __fastcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, l
|
||||
case ATKTYPE_LWEAPON_RELOAD:
|
||||
case ATKTYPE_RWEAPON_RELOAD:
|
||||
if (weapon && weapon->protoId != PID_SOLAR_SCORCHER) { // Solar Scorcher has no reload AP cost
|
||||
cost = reloadCostAP;
|
||||
cost = reloadAPCost;
|
||||
if (GetProto(weapon->protoId)->item.weapon.perk == PERK_weapon_fast_reload) {
|
||||
cost--;
|
||||
}
|
||||
}
|
||||
goto endReload;
|
||||
}
|
||||
if (hitMode < ATKTYPE_LWEAPON_RELOAD) {
|
||||
if (isCalled) cost++;
|
||||
if (cost < 0) cost = 0;
|
||||
|
||||
long type = fo_item_w_subtype(weapon, hitMode);
|
||||
cost = item_w_mp_cost_sub(source, weapon, hitMode, isCalled, cost);
|
||||
|
||||
if (source->protoId == PID_Player && DudeHasTrait(TRAIT_fast_shot)) {
|
||||
if (fastShotTweak || // Fallout 1 behavior and Alternative behavior (allowed for all weapons)
|
||||
(fo_item_w_range(source, hitMode) >= 2 && type > ATKSUBTYPE_MELEE)) // Fallout 2 behavior (with fix) and Haenlomal's tweak
|
||||
{
|
||||
cost--;
|
||||
}
|
||||
}
|
||||
if ((type == ATKSUBTYPE_MELEE || type == ATKSUBTYPE_UNARMED) && fo_perk_level(source, PERK_bonus_hth_attacks)) {
|
||||
cost--;
|
||||
}
|
||||
if (type == ATKSUBTYPE_GUNS && fo_perk_level(source, PERK_bonus_rate_of_fire)) {
|
||||
cost--;
|
||||
}
|
||||
if (cost < 1) cost = 1;
|
||||
}
|
||||
return CalcApCostHook_Invoke(source, hitMode, isCalled, cost, weapon);
|
||||
}
|
||||
|
||||
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
|
||||
long __stdcall sfgame_item_w_mp_cost(TGameObj* source, long hitMode, long isCalled) {
|
||||
long cost = fo_item_w_mp_cost(source, hitMode, isCalled);
|
||||
return CalcApCostHook_Invoke(source, hitMode, isCalled, cost, nullptr);
|
||||
endReload:
|
||||
return CalcApCostHook_Invoke(source, hitMode, isCalled, cost, weapon); // return cost
|
||||
}
|
||||
|
||||
static void __declspec(naked) ai_search_inven_weap_hook() {
|
||||
@@ -458,6 +465,54 @@ static void __declspec(naked) ai_search_inven_weap_hook() {
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
|
||||
long __fastcall sfgame_item_w_mp_cost(TGameObj* source, AttackType hitMode, long isCalled) {
|
||||
TGameObj* handItem = nullptr;
|
||||
|
||||
switch (hitMode) {
|
||||
case ATKTYPE_LWEAPON_PRIMARY:
|
||||
case ATKTYPE_LWEAPON_SECONDARY:
|
||||
case ATKTYPE_LWEAPON_RELOAD:
|
||||
handItem = fo_inven_left_hand(source);
|
||||
break;
|
||||
case ATKTYPE_RWEAPON_PRIMARY:
|
||||
case ATKTYPE_RWEAPON_SECONDARY:
|
||||
case ATKTYPE_RWEAPON_RELOAD:
|
||||
handItem = fo_inven_right_hand(source);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (handItem) {
|
||||
return sfgame_item_weapon_mp_cost(source, handItem, hitMode, isCalled);
|
||||
}
|
||||
|
||||
// unarmed hits
|
||||
long cost = unarmedAPCost;
|
||||
if (hitMode == ATKTYPE_PUNCH || hitMode == ATKTYPE_KICK || hitMode >= ATKTYPE_STRONGPUNCH) {
|
||||
cost = Unarmed_GetHitAPCost(hitMode);
|
||||
}
|
||||
|
||||
// return cost
|
||||
return CalcApCostHook_Invoke(
|
||||
source,
|
||||
hitMode,
|
||||
isCalled,
|
||||
item_w_mp_cost_sub(source, nullptr, hitMode, isCalled, cost),
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
static void __declspec(naked) item_w_mp_cost_hack() {
|
||||
__asm {
|
||||
push ebx; // isCalled
|
||||
mov ecx, eax; // source
|
||||
call sfgame_item_w_mp_cost;
|
||||
pop ecx;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////// OBJECTS ////////////////////////////////////
|
||||
|
||||
// Implementation of is_within_perception_ engine function with the HOOK_WITHINPERCEPTION hook
|
||||
@@ -677,6 +732,12 @@ int __stdcall sfgame_trait_level(DWORD traitID) {
|
||||
return DudeHasTrait(traitID);
|
||||
}
|
||||
|
||||
// Wrapper of perk_level_ function, for quickly skipping other critters
|
||||
int __stdcall sfgame_perk_level(TGameObj* source, DWORD perkID) {
|
||||
if (source != *ptr_obj_dude) return 0;
|
||||
return fo_perk_level(source, perkID);
|
||||
}
|
||||
|
||||
static int DudeGetBaseStat(DWORD statID) {
|
||||
return fo_stat_get_base_direct(*ptr_obj_dude, statID);
|
||||
}
|
||||
@@ -911,11 +972,13 @@ void InitReplacementHacks() {
|
||||
// Replace adjust_fid_ function
|
||||
MakeJump(adjust_fid_, adjust_fid_hack); // 0x4716E8
|
||||
|
||||
// Replace the item_w_primary_mp_cost_ function with the sfall implementation
|
||||
// Replace the item_w_primary_mp_cost_ function with the sfall implementation in ai_search_inven_weap_
|
||||
HookCall(0x429A08, ai_search_inven_weap_hook);
|
||||
|
||||
int fastShotFix = GetConfigInt("Misc", "FastShotFix", 0);
|
||||
fastShotTweak = (fastShotFix > 0 && fastShotFix <= 3);
|
||||
// Replace the item_w_mp_cost_ function with the sfall implementation
|
||||
MakeJump(item_w_mp_cost_ + 1, item_w_mp_cost_hack); // 0x478B25
|
||||
|
||||
fastShotTweak = GetConfigInt("Misc", "FastShotFix", 0);
|
||||
|
||||
// Replace the srcCopy_ function with a pure MMX implementation
|
||||
MakeJump(buf_to_buf_, fo_buf_to_buf); // 0x4D36D4
|
||||
|
||||
@@ -60,8 +60,8 @@ long __stdcall sfgame_item_weapon_range(TGameObj* source, TGameObj* weapon, long
|
||||
long __fastcall sfgame_item_weapon_mp_cost(TGameObj* source, TGameObj* weapon, long hitMode, long isCalled);
|
||||
|
||||
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
|
||||
// Note: Use the generic item_mp_cost function which has a hook call
|
||||
long __stdcall sfgame_item_w_mp_cost(TGameObj* source, long hitMode, long isCalled);
|
||||
// Note: Can use the generic item_mp_cost_ function which has a hook call
|
||||
long __fastcall sfgame_item_w_mp_cost(TGameObj* source, AttackType hitMode, long isCalled);
|
||||
|
||||
// Implementation of is_within_perception_ engine function with the HOOK_WITHINPERCEPTION hook
|
||||
long __stdcall sfgame_is_within_perception(TGameObj* watcher, TGameObj* target, long hookType);
|
||||
@@ -75,6 +75,8 @@ int __stdcall sfgame_trait_adjust_skill(DWORD skillID);
|
||||
|
||||
int __stdcall sfgame_trait_level(DWORD traitID);
|
||||
|
||||
int __stdcall sfgame_perk_level(TGameObj* source, DWORD perkID);
|
||||
|
||||
int __stdcall sfgame_trait_adjust_stat(DWORD statID);
|
||||
|
||||
long __fastcall sfgame_tile_num_beyond(long sourceTile, long targetTile, long maxRange);
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* sfall
|
||||
* Copyright (C) 2008-2021 The sfall team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "main.h"
|
||||
#include "FalloutEngine.h"
|
||||
|
||||
#include "DamageMod.h"
|
||||
|
||||
class Hits {
|
||||
public:
|
||||
static const long count = 20;
|
||||
|
||||
private:
|
||||
struct HitsData {
|
||||
long reqLevel; // intface_update_items_
|
||||
long reqSkill;
|
||||
long reqStat[7];
|
||||
long minDamage; // default for all
|
||||
long maxDamage; // default for all (maxDamage + STAT_melee_dmg)
|
||||
long bonusDamage; // item_w_damage_
|
||||
long bonusCrit; // compute_attack_
|
||||
long apCost; // item_w_mp_cost_
|
||||
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;
|
||||
std::memset(reqStat, 0, sizeof(reqStat));
|
||||
minDamage = 1;
|
||||
maxDamage = 2;
|
||||
bonusDamage = 0;
|
||||
bonusCrit = 0;
|
||||
apCost = 3;
|
||||
isPenetrate = false;
|
||||
isSecondary = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct SortHits {
|
||||
BYTE level;
|
||||
BYTE hit; // index in Hits class
|
||||
};
|
||||
|
||||
// sorted in descending order of the required stats in group hits
|
||||
SortHits sortHits[Hits::count - ATKTYPE_STRONGPUNCH];
|
||||
|
||||
HitsData hit[Hits::count];
|
||||
|
||||
long skillLevel; // SKILL_UNARMED_COMBAT
|
||||
long psStat[STAT_base_count];
|
||||
|
||||
public:
|
||||
class Types{
|
||||
public: enum : long {
|
||||
punch = ATKTYPE_PUNCH,
|
||||
kick = ATKTYPE_KICK,
|
||||
|
||||
strong_punch = ATKTYPE_STRONGPUNCH,
|
||||
hammer_punch,
|
||||
haymaker,
|
||||
jab,
|
||||
palm_strike,
|
||||
piercing_strike,
|
||||
|
||||
strong_kick,
|
||||
snap_kick,
|
||||
power_kick,
|
||||
hip_kick,
|
||||
hook_kick,
|
||||
piercing_kick
|
||||
};
|
||||
};
|
||||
|
||||
Hits() {
|
||||
// punch primary 1
|
||||
hit[Types::strong_punch].reqLevel = 1;
|
||||
hit[Types::strong_punch].reqSkill = 55;
|
||||
hit[Types::strong_punch].reqStat[STAT_ag] = 6;
|
||||
hit[Types::strong_punch].bonusDamage = 3;
|
||||
// primary 2
|
||||
hit[Types::hammer_punch].reqLevel = 6;
|
||||
hit[Types::hammer_punch].reqSkill = 75;
|
||||
hit[Types::hammer_punch].reqStat[STAT_st] = 5;
|
||||
hit[Types::hammer_punch].reqStat[STAT_ag] = 6;
|
||||
hit[Types::hammer_punch].bonusDamage = 5;
|
||||
hit[Types::hammer_punch].bonusCrit = 5;
|
||||
// primary 3
|
||||
hit[Types::haymaker].reqLevel = 9;
|
||||
hit[Types::haymaker].reqSkill = 100;
|
||||
hit[Types::haymaker].reqStat[STAT_st] = 5;
|
||||
hit[Types::haymaker].reqStat[STAT_ag] = 7;
|
||||
hit[Types::haymaker].bonusDamage = 7;
|
||||
hit[Types::haymaker].bonusCrit = 15;
|
||||
|
||||
// punch secondary 1
|
||||
hit[Types::jab].reqLevel = 5;
|
||||
hit[Types::jab].reqSkill = 75;
|
||||
hit[Types::jab].reqStat[STAT_st] = 5;
|
||||
hit[Types::jab].reqStat[STAT_ag] = 7;
|
||||
hit[Types::jab].bonusDamage = 3;
|
||||
hit[Types::jab].bonusCrit = 10;
|
||||
hit[Types::jab].isSecondary = true;
|
||||
// secondary 2
|
||||
hit[Types::palm_strike].reqLevel = 12;
|
||||
hit[Types::palm_strike].reqSkill = 115;
|
||||
hit[Types::palm_strike].reqStat[STAT_st] = 5;
|
||||
hit[Types::palm_strike].reqStat[STAT_ag] = 7;
|
||||
hit[Types::palm_strike].bonusDamage = 7;
|
||||
hit[Types::palm_strike].bonusCrit = 20;
|
||||
hit[Types::palm_strike].apCost = 6;
|
||||
hit[Types::palm_strike].isPenetrate = true;
|
||||
hit[Types::palm_strike].isSecondary = true;
|
||||
// secondary 3
|
||||
hit[Types::piercing_strike].reqLevel = 16;
|
||||
hit[Types::piercing_strike].reqSkill = 130;
|
||||
hit[Types::piercing_strike].reqStat[STAT_st] = 5;
|
||||
hit[Types::piercing_strike].reqStat[STAT_ag] = 7;
|
||||
hit[Types::piercing_strike].bonusDamage = 10;
|
||||
hit[Types::piercing_strike].bonusCrit = 40;
|
||||
hit[Types::piercing_strike].apCost = 8;
|
||||
hit[Types::piercing_strike].isPenetrate = true;
|
||||
hit[Types::piercing_strike].isSecondary = true;
|
||||
|
||||
// kick primary 1
|
||||
hit[Types::strong_kick].reqLevel = 1;
|
||||
hit[Types::strong_kick].reqSkill = 40;
|
||||
hit[Types::strong_kick].reqStat[STAT_ag] = 6;
|
||||
hit[Types::strong_kick].bonusDamage = 5;
|
||||
hit[Types::strong_kick].apCost = 4;
|
||||
// primary 2
|
||||
hit[Types::snap_kick].reqLevel = 6;
|
||||
hit[Types::snap_kick].reqSkill = 60;
|
||||
hit[Types::snap_kick].reqStat[STAT_ag] = 6;
|
||||
hit[Types::snap_kick].bonusDamage = 7;
|
||||
hit[Types::snap_kick].apCost = 4;
|
||||
// primary 3
|
||||
hit[Types::power_kick].reqLevel = 9;
|
||||
hit[Types::power_kick].reqSkill = 80;
|
||||
hit[Types::power_kick].reqStat[STAT_st] = 6;
|
||||
hit[Types::power_kick].reqStat[STAT_ag] = 6;
|
||||
hit[Types::power_kick].bonusDamage = 9;
|
||||
hit[Types::power_kick].bonusCrit = 5;
|
||||
hit[Types::power_kick].apCost = 4;
|
||||
|
||||
// kick secondary 1
|
||||
hit[Types::hip_kick].reqLevel = 6;
|
||||
hit[Types::hip_kick].reqSkill = 60;
|
||||
hit[Types::hip_kick].reqStat[STAT_st] = 6;
|
||||
hit[Types::hip_kick].reqStat[STAT_ag] = 7;
|
||||
hit[Types::hip_kick].bonusDamage = 7;
|
||||
hit[Types::hip_kick].apCost = 7;
|
||||
hit[Types::hip_kick].isSecondary = true;
|
||||
// secondary 2
|
||||
hit[Types::hook_kick].reqLevel = 12;
|
||||
hit[Types::hook_kick].reqSkill = 100;
|
||||
hit[Types::hook_kick].reqStat[STAT_st] = 6;
|
||||
hit[Types::hook_kick].reqStat[STAT_ag] = 7;
|
||||
hit[Types::hook_kick].bonusDamage = 9;
|
||||
hit[Types::hook_kick].bonusCrit = 10;
|
||||
hit[Types::hook_kick].apCost = 7;
|
||||
hit[Types::hook_kick].isPenetrate = true;
|
||||
hit[Types::hook_kick].isSecondary = true;
|
||||
// secondary 3
|
||||
hit[Types::piercing_kick].reqLevel = 15;
|
||||
hit[Types::piercing_kick].reqSkill = 125;
|
||||
hit[Types::piercing_kick].reqStat[STAT_st] = 6;
|
||||
hit[Types::piercing_kick].reqStat[STAT_ag] = 8;
|
||||
hit[Types::piercing_kick].bonusDamage = 12;
|
||||
hit[Types::piercing_kick].bonusCrit = 50;
|
||||
hit[Types::piercing_kick].apCost = 9;
|
||||
hit[Types::piercing_kick].isPenetrate = true;
|
||||
hit[Types::piercing_kick].isSecondary = true;
|
||||
}
|
||||
|
||||
HitsData& Hit(AttackType i) { return hit[i]; }
|
||||
|
||||
// Get hit by index
|
||||
HitsData& Hit(size_t index) { return hit[index]; }
|
||||
|
||||
AttackType GetSortHit(size_t index) { return (AttackType)sortHits[index].hit; }
|
||||
|
||||
void Sort() {
|
||||
for (char i = ATKTYPE_STRONGPUNCH, j = 0; i < Hits::count; i++, j++) {
|
||||
sortHits[j].level = (char)hit[i].reqLevel;
|
||||
sortHits[j].hit = i;
|
||||
}
|
||||
|
||||
auto greater_comp = [&](const SortHits &a, const SortHits &b) -> bool {
|
||||
if (a.level == b.level) {
|
||||
return hit[a.hit].reqSkill > hit[b.hit].reqSkill;
|
||||
}
|
||||
return a.level > b.level;
|
||||
};
|
||||
|
||||
std::sort(&sortHits[0], &sortHits[6], greater_comp);
|
||||
std::sort(&sortHits[6], &sortHits[12], greater_comp);
|
||||
}
|
||||
|
||||
void GetDudeStats(long sLevel) {
|
||||
skillLevel = sLevel;
|
||||
for (size_t stat = 0; stat < STAT_base_count; stat++) {
|
||||
psStat[stat] = fo_stat_level(*ptr_obj_dude, stat);
|
||||
}
|
||||
}
|
||||
|
||||
long SkillLevel() { return skillLevel; }
|
||||
long DudeStat(long stat) { return psStat[stat]; }
|
||||
};
|
||||
|
||||
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] > unarmed.DudeStat(stat)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool Punching(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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
static void __declspec(naked) intface_update_items_hack_punch() {
|
||||
__asm {
|
||||
push 0x45F1D7;
|
||||
jmp check_unarmed_left_slot;
|
||||
}
|
||||
}
|
||||
|
||||
static bool Kicking(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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
static void __declspec(naked) intface_update_items_hack_kick() {
|
||||
__asm {
|
||||
push 0x45F380;
|
||||
jmp check_unarmed_right_slot;
|
||||
}
|
||||
}
|
||||
|
||||
static long __fastcall get_unarmed_crit_chance(long &chanceOut, AttackType hit) {
|
||||
chanceOut = unarmed.Hit(hit).bonusCrit;
|
||||
return (chanceOut > 0)
|
||||
? 0x4239F4 //
|
||||
: 0x423A0D; // skip random roll
|
||||
}
|
||||
|
||||
static void __declspec(naked) compute_attack_hack() {
|
||||
__asm {
|
||||
sub esp, 4;
|
||||
mov ecx, esp; // chanceOut ref
|
||||
call get_unarmed_crit_chance; // edx - hit
|
||||
pop ecx; // chance
|
||||
jmp eax;
|
||||
}
|
||||
}
|
||||
|
||||
static long __fastcall get_unarmed_damage(TGameObj* source, AttackType hit, long &minOut, long &maxOut) {
|
||||
minOut = unarmed.Hit(hit).minDamage + DamageMod_GetHtHMinDamageBonus(source);
|
||||
maxOut = unarmed.Hit(hit).maxDamage + fo_stat_level(source, STAT_melee_dmg);
|
||||
return unarmed.Hit(hit).bonusDamage;
|
||||
}
|
||||
|
||||
static void __declspec(naked) item_w_damage_hack() {
|
||||
static DWORD item_w_damage_hack_ret = 0x478553;
|
||||
__asm {
|
||||
lea eax, [esp + 4]; // min_DMG
|
||||
lea edx, [esp + 0]; // max_DMG
|
||||
push ecx;
|
||||
push edx; // maxOut ref
|
||||
push eax; // minOut ref
|
||||
mov edx, esi; // hit
|
||||
call get_unarmed_damage;
|
||||
mov ebx, eax; // bonus
|
||||
pop ecx;
|
||||
jmp item_w_damage_hack_ret;
|
||||
}
|
||||
}
|
||||
|
||||
static long __fastcall check_unarmed_penetrate(AttackType hit) {
|
||||
return static_cast<long>(unarmed.Hit(hit).isPenetrate);
|
||||
}
|
||||
|
||||
long Unarmed_GetHitAPCost(AttackType hit) {
|
||||
return unarmed.Hit(hit).apCost;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void __declspec(naked) statPCAddExperienceCheckPMs_hook() {
|
||||
__asm {
|
||||
call intface_update_hit_points_;
|
||||
sub esp, 8;
|
||||
lea edx, [esp + 4]; // modeR
|
||||
mov eax, esp; // modeL
|
||||
call intface_get_item_states_;
|
||||
pop edx;
|
||||
pop ebx;
|
||||
xor eax, eax;
|
||||
jmp intface_update_items_;
|
||||
}
|
||||
}
|
||||
|
||||
void Unarmed_Init() {
|
||||
// Update unarmed attack after leveling up
|
||||
HookCall(0x4AFC15, statPCAddExperienceCheckPMs_hook);
|
||||
|
||||
unarmed = Hits();
|
||||
|
||||
std::string unarmedFile = GetConfigString("Misc", "UnarmedFile", "", MAX_PATH);
|
||||
if (!unarmedFile.empty()) {
|
||||
const char* file = unarmedFile.insert(0, ".\\").c_str();
|
||||
if (GetFileAttributes(file) != INVALID_FILE_ATTRIBUTES) { // check if file exists
|
||||
char stat[6] = "Stat0";
|
||||
char sHit[4] = "0";
|
||||
for (size_t i = 0; i < Hits::count; _itoa(++i, sHit, 10)) {
|
||||
if (i < ATKTYPE_STRONGPUNCH && i != ATKTYPE_PUNCH && i != ATKTYPE_KICK) continue;
|
||||
|
||||
auto& hit = unarmed.Hit(i);
|
||||
|
||||
int val = IniGetInt(sHit, "ReqLevel", -1, file);
|
||||
if (val >= 0) hit.reqLevel = val;
|
||||
|
||||
val = IniGetInt(sHit, "SkillLevel", -1, file);
|
||||
if (val >= 0) hit.reqSkill = val;
|
||||
|
||||
val = IniGetInt(sHit, "MinDamage", -1, file);
|
||||
if (val >= 0) hit.minDamage = val;
|
||||
|
||||
val = IniGetInt(sHit, "MaxDamage", -1, file);
|
||||
if (val >= 0) hit.maxDamage = val;
|
||||
|
||||
val = IniGetInt(sHit, "BonusDamage", -1, file);
|
||||
if (val >= 0) hit.bonusDamage = val;
|
||||
|
||||
val = IniGetInt(sHit, "BonusCrit", -1, file);
|
||||
if (val >= 0) hit.bonusCrit = val;
|
||||
|
||||
val = IniGetInt(sHit, "APCost", -1, file);
|
||||
if (val >= 0) hit.apCost = val;
|
||||
|
||||
val = IniGetInt(sHit, "Penetrate", -1, file);
|
||||
if (val >= 0) hit.isPenetrate = (val != 0);
|
||||
|
||||
val = IniGetInt(sHit, "Secondary", -1, file);
|
||||
if (val >= 0) hit.isSecondary = (val != 0);
|
||||
|
||||
for (size_t s = 0; s < STAT_base_count; _itoa(++s, &stat[4], 10)) {
|
||||
val = IniGetInt(sHit, stat, -1, file);
|
||||
if (val >= 0) hit.reqStat[s] = val;
|
||||
}
|
||||
stat[4] = '0';
|
||||
}
|
||||
|
||||
// Only if a custom file is used, otherwise use the engine function code
|
||||
MakeJump(0x45F0DF, intface_update_items_hack_punch);
|
||||
MakeJump(0x45F278, intface_update_items_hack_kick);
|
||||
}
|
||||
}
|
||||
unarmed.Sort();
|
||||
|
||||
// Get critical chance hack
|
||||
MakeJump(0x42394D, compute_attack_hack);
|
||||
SafeWrite16(0x423A03, 0xC839); // cmp eax, 50 -> cmp eax, ecx
|
||||
SafeWrite8(0x423A05, CODETYPE_Nop);
|
||||
|
||||
// Get damage hack
|
||||
MakeJump(0x478492, item_w_damage_hack);
|
||||
|
||||
// compute_damage_ penetrate hack
|
||||
SafeWrite8(0x4248B4, 0x4E); // mov ecx, [hit]
|
||||
MakeCall(0x4248B6, check_unarmed_penetrate, 5);
|
||||
SafeWrite16(0x4248C1, 0x01F8); // cmp eax, 1
|
||||
SafeWrite8(0x4248C8, CODETYPE_JumpShort);
|
||||
}
|
||||
|
||||
//void Unarmed_Exit() {
|
||||
//}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* sfall
|
||||
* Copyright (C) 2008-2021 The sfall team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void Unarmed_Init();
|
||||
//void Unarmed_Exit();
|
||||
|
||||
long Unarmed_GetHitAPCost(AttackType hit);
|
||||
+3
-1
@@ -124,7 +124,7 @@
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<ForcedIncludeFiles>stdafx.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
@@ -285,6 +285,7 @@
|
||||
<ClInclude Include="Tiles.h" />
|
||||
<ClInclude Include="SpeedPatch.h" />
|
||||
<ClInclude Include="Translate.h" />
|
||||
<ClInclude Include="Unarmed.h" />
|
||||
<ClInclude Include="Utils.h" />
|
||||
<ClInclude Include="version.h" />
|
||||
<ClInclude Include="Logging.h" />
|
||||
@@ -355,6 +356,7 @@
|
||||
<ClCompile Include="Logging.cpp" />
|
||||
<ClCompile Include="ExtraSaveSlots.cpp" />
|
||||
<ClCompile Include="Translate.cpp" />
|
||||
<ClCompile Include="Unarmed.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
<ClCompile Include="Worldmap.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -265,6 +265,9 @@
|
||||
<ClInclude Include="EngineTweaks.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Unarmed.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Console.cpp">
|
||||
@@ -445,6 +448,9 @@
|
||||
<ClCompile Include="EngineTweaks.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Unarmed.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="exports.def" />
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
#include "TalkingHeads.h"
|
||||
#include "Tiles.h"
|
||||
#include "Translate.h"
|
||||
#include "Unarmed.h"
|
||||
#include "version.h"
|
||||
#include "Worldmap.h"
|
||||
|
||||
@@ -127,6 +128,9 @@ static void InitModules() {
|
||||
dlogr("Running Elevators_Init().", DL_INIT);
|
||||
Elevators_Init();
|
||||
|
||||
dlogr("Running Unarmed_Init().", DL_INIT);
|
||||
Unarmed_Init();
|
||||
|
||||
dlogr("Running Animations_Init().", DL_INIT);
|
||||
Animations_Init();
|
||||
|
||||
@@ -244,6 +248,7 @@ static void __stdcall OnExit() {
|
||||
Graphics_Exit();
|
||||
//EngineTweaks_Exit();
|
||||
Books_Exit();
|
||||
//Unarmed_Exit();
|
||||
//Animations_Exit();
|
||||
BarBoxes_Exit();
|
||||
//Message_Exit();
|
||||
|
||||
Reference in New Issue
Block a user