Compare commits

...
27 Commits
Author SHA1 Message Date
NovaRain 3acf051b49 Renamed all metarule functions with mf_ prefix 2020-05-21 09:42:02 +08:00
NovaRain 0137504f3f Renamed all script opcode handlers with op_ prefix 2020-05-19 10:05:27 +08:00
NovaRain ebcd505561 Minor code fixes in AI.cpp:
* Fixed NPCs being unable to select a burst attack mode if the line of
fire was blocked.

* Changed the return point of combat_ai_hook_FleeFix to run
ai_check_drugs_ first.

Changed the debug message about missing art file for critters to be
displayed in game only when in sfall debugging mode.
2020-05-19 09:50:23 +08:00
NovaRain 24cb02b7d8 Fixed ai_move_away_ engine function
Some minor fixes.
2020-05-10 10:03:04 +08:00
NovaRain 48ce358451 Backported SkipLoadingGameSettings option from 4.x 2020-05-08 10:38:32 +08:00
NovaRain c293a826bc Cosmetic changes to the code:
Use double underscore for stdcall and "asm" keyword.
2020-05-07 12:30:22 +08:00
NovaRain 0da7c7b266 Minor code edits to Combat.cpp 2020-05-07 11:20:32 +08:00
NovaRain d74df7fc78 Added checking AP cost to ai_pick_hit_mode_ engine function 2020-05-07 09:49:57 +08:00
NovaRain df816bcd47 Added a tweak for finding new targets for PMs
* before they would "forget" their current target when trying to find
new ones (only 3), now the current target is included as one of
potential targets.

Tweaked the code of MultiHexPathingFix.
2020-05-05 12:15:30 +08:00
NovaRain 068988caad Added internal implementation of engine functions with hooks:
* item_w_mp_cost_ with CALCAPCOST
* is_within_perception_ with WITHINPERCEPTION
2020-05-01 10:29:27 +08:00
NovaRain 24c9499021 Added combat rating check to "Snipe" distance preference fix
Added a fix to reduce friendly fire in burst attacks.
Updated the info of TGameObj struct.
2020-05-01 08:01:24 +08:00
NovaRain 7f3de8d19a Minor code edits/fixes 2020-04-30 10:28:31 +08:00
NovaRain 87f7fe4a54 Moved code from main.cpp to new PlayerModel/MiscPatches.cpp 2020-04-28 12:43:54 +08:00
NovaRain 01ba18100e Added a fix for AI behavior for "Snipe" distance preference
Added a check for for the weapon range and the distance to the target
when AI is choosing weapon attack modes.
2020-04-28 10:58:24 +08:00
NovaRain 8b26573073 Changed the fix for AI target selection in commit f0f00a7
* NPCs will reload the weapon in the same turn (and attack if have
enough AP left) if weapon is empty while selecting targets.

Moved some leftover code in main.cpp to KillCounter.cpp & Elevators.cpp.

Misc code edits.
2020-04-27 14:32:39 +08:00
NovaRain 1839790dfb Fixed AllowDShowSound=2 not working
Limited NumSoundBuffers to the max of 32.
Fixed replacing Body_Torso with Body_Uncalled (from commit 62f890d)
Corrected the code in the previous commit.
2020-04-23 12:42:20 +08:00
NovaRain cb1d5d359f Added a tweak to the miss message in combat
* if the main target has Flat flag set, remove the message about hitting
another object.

Reorganized the code of misc patches in main.cpp.
2020-04-22 11:40:11 +08:00
NovaRain b781e117a2 Improved the fix for placing PID_CORPSE_BLOOD obj on the lower layer
(didn't work correctly after re-entering the map.)

Fixed CorpseDeleteTime not setting the timer correctly.
2020-04-21 10:55:56 +08:00
NovaRain a7d9716b68 Moved some engine structs to FalloutStructs.h
Some code edits regarding variable/struct types.
2020-04-20 12:06:13 +08:00
NovaRain cdd2bfe338 Reverted incorrect code change of SpeedPatch.cpp in previous commit.
Some code edits to Explosions.cpp and Perks.cpp.
2020-04-19 10:21:20 +08:00
NovaRain 7decec4a2c Added use_item_on_dude macro to sfall.h
Various code edits:
* Implemented more HookCalls/MakeCalls/SafeWriteBatch in code.
* Moved return/jump address consts into their respective functions.
2020-04-18 21:25:54 +08:00
NovaRain e3af4f5754 Fixed ReloadWeaponKey for using any non-weapon item (#84) 2020-04-18 11:25:46 +08:00
NovaRain 1b61bca702 Fixed "loot_obj" script function 2020-04-18 09:03:16 +08:00
NovaRain f0f00a77ea Fixed AI target selection for no ammo or target out of range cases 2020-04-18 09:01:29 +08:00
NovaRain 58d41487fd Re-fixed automatically ending combat
Updated version number.
2020-04-18 08:59:28 +08:00
NovaRain 73ccba22bf Implemented HookCalls/MakeCalls templates
* same as SafeWriteBatch, only accept address arrays due to no support
for list initialization in VS2010.
2020-04-11 08:19:32 +08:00
NovaRain 1f8b3636f3 Implemented SafeWriteBatch template from 4.x
* only the version that accepts address array due to no support
for list initialization in VS2020.
2020-04-08 10:42:59 +08:00
91 changed files with 3250 additions and 2691 deletions
+10 -6
View File
@@ -1,5 +1,5 @@
;sfall configuration settings
;v3.8.24
;v3.8.25
[Main]
;Change to 1 if you want to use command line args to tell sfall to use another ini file.
@@ -93,7 +93,7 @@ WorldMapTerrainInfo=0
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[Sound]
;Sets the number of allowed simultaneous sound effects
;Set to 0 to leave the default unchanged (i.e. 4)
;Set to 0 to leave the default unchanged (i.e. 4). The maximum is 32
NumSoundBuffers=0
;Set to 1 to allow attaching sound files to combat float messages
@@ -106,7 +106,7 @@ AllowDShowSound=0
;Set to 1 to override the music path used by default (i.e. data\sound\music\) if not present in the cfg
;Set to 2 to overwrite all occurances of the music path
OverrideMusicDir=2
OverrideMusicDir=1
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[Input]
@@ -185,7 +185,7 @@ HighlightCorpses=1
;64 - purple
OutlineColor=16
;A key to press to reload your currently equipped weapon
;A key to press to reload your currently equipped weapon or use the active item
;Set to 0 if you don't want a reload key, or a DX scancode otherwise
ReloadWeaponKey=0
@@ -344,7 +344,7 @@ SingleCore=1
OverrideArtCacheSize=0
;Prevents you from saving in combat except at the start of your turn to avoid a few bugs
;Note that even with this option enabled, it is still not advisable to save in combat at all.
;Note that even with this option enabled, it is still not advisable to save in combat at all (obsolete information)
;Set to 2 to block all saving in combat
SaveInCombatFix=1
@@ -394,7 +394,7 @@ CorpseDeleteTime=6
;Set a number of milliseconds to idle each input loop
;Set to -1 to disable
;Set to 0 to idle only if other processes are waiting for processor time (WinXP/2000: if processes have equal priority)
;Set to 1 (or some higher number if needed, the maximum is 127) to prevent 100% CPU use
;Set to 1 (or some higher number if needed) to prevent 100% CPU use. The maximum is 127
ProcessorIdle=-1
;Set to 1 if using the hero appearance mod
@@ -694,6 +694,10 @@ DisplaySwiftLearnerExp=1
;Set to 1 to display party member's current level/AC/addict flag on the combat control panel
PartyMemberExtraInfo=0
;Set to 1 to skip loading all game settings except the game/combat difficulty from saved games
;Set to 2 to also skip loading the game/combat difficulty settings
SkipLoadingGameSettings=0
;Set to 1 to prevent the inventory/loot/automap interfaces from being placed on top of other script-created windows
InterfaceDontMoveOnTop=0
+6
View File
@@ -238,6 +238,12 @@
#define ADD_PERK_MODE_PERK (2) // add to the player's perks
#define ADD_PERK_MODE_REMOVE (4) // remove from the list of selectable perks (after added to the player)
// instantly apply the item to dude_obj (w/o animation)
#define use_item_on_dude(item) set_self(dude_obj); \
set_self(dude_obj); \
use_obj_on_obj(item, dude_obj); \
set_self(0)
// sfall_funcX macros
#define add_global_timer_event(time, fixedParam) sfall_func2("add_g_timer_event", time, fixedParam)
#define add_iface_tag sfall_func0("add_iface_tag")
+189 -15
View File
@@ -21,6 +21,8 @@
#include "main.h"
#include "FalloutEngine.h"
#include "HookScripts.h"
#include "AI.h"
typedef std::tr1::unordered_map<TGameObj*, TGameObj*>::const_iterator iter;
@@ -28,6 +30,24 @@ typedef std::tr1::unordered_map<TGameObj*, TGameObj*>::const_iterator iter;
static std::tr1::unordered_map<TGameObj*, TGameObj*> targets;
static std::tr1::unordered_map<TGameObj*, TGameObj*> sources;
TGameObj* __stdcall sf_check_critters_in_lof(TGameObj* object, DWORD checkTile, DWORD team) {
if (object && object->pid >> 24 == OBJ_TYPE_CRITTER && object->teamNum != team) { // not friendly fire
if (object->tile == checkTile) return nullptr;
TGameObj* obj = nullptr; // continue checking the line of fire from object to checkTile
MakeStraightPathFunc(object, object->tile, checkTile, 0, (DWORD*)&obj, 32, (void*)obj_shoot_blocking_at_);
if (!sf_check_critters_in_lof(obj, checkTile, team)) return nullptr;
}
return object;
}
// Returns the friendly critter that is in the line of fire
TGameObj* __stdcall AI_CheckFriendlyFire(TGameObj* target, TGameObj* attacker) {
TGameObj* object = nullptr;
MakeStraightPathFunc(attacker, attacker->tile, target->tile, 0, (DWORD*)&object, 32, (void*)obj_shoot_blocking_at_);
object = sf_check_critters_in_lof(object, target->tile, attacker->teamNum);
return (!object || ((object->artFid >> 24) & 0x0F) == OBJ_TYPE_CRITTER) ? object : nullptr; // 0 if there are no friendly critters
}
static void __declspec(naked) ai_try_attack_hook_FleeFix() {
__asm {
or byte ptr [esi + 0x3C], 8; // set new 'ReTarget' flag
@@ -36,7 +56,7 @@ static void __declspec(naked) ai_try_attack_hook_FleeFix() {
}
static void __declspec(naked) combat_ai_hook_FleeFix() {
static const DWORD combat_ai_hook_flee_Ret = 0x42B22F;
static const DWORD combat_ai_hook_flee_Ret = 0x42B206;
__asm {
test byte ptr [ebp], 8; // 'ReTarget' flag (critter.combat_state)
jnz reTarget;
@@ -148,7 +168,23 @@ skip:
}
}
static DWORD RetryCombatMinAP;
static void __declspec(naked) ai_danger_source_hack_pm_newfind() {
__asm {
mov ecx, [ebp + 0x18]; // source combat_data.who_hit_me
test ecx, ecx;
jnz hasTarget;
retn;
hasTarget:
test [ecx + 0x44], DAM_DEAD;
jz isNotDead;
xor ecx, ecx;
isNotDead:
mov dword ptr [ebp + 0x18], 0; // combat_data.who_hit_me (engine code)
retn;
}
}
static long RetryCombatMinAP;
static void __declspec(naked) RetryCombatHook() {
static DWORD RetryCombatLastAP = 0;
@@ -185,7 +221,7 @@ static long __fastcall sf_ai_weapon_reload(TGameObj* weapon, TGameObj* ammo, TGa
while (ammo) {
result = ItemWReload(weapon, ammo);
if (result != 0) break; // 1 - reload done, -1 - can't reload
if (result != 0) return result; // 1 - reload done, -1 - can't reload
if (!proto) {
proto = GetProtoPtr(weapon->pid);
@@ -205,7 +241,7 @@ static long __fastcall sf_ai_weapon_reload(TGameObj* weapon, TGameObj* ammo, TGa
}
}
}
if (!result && _ammo != ammo) {
if (_ammo != ammo) {
ObjDestroy(ammo);
return 1; // notifies the engine that the ammo has already been destroyed
}
@@ -227,6 +263,112 @@ skip:
}
}
static long __fastcall CheckWeaponRangeAndApCost(TGameObj* source, TGameObj* target) {
long weaponRange = ItemWRange(source, ATKTYPE_RWEAPON_SECONDARY);
long targetDist = ObjDist(source, target);
if (targetDist > weaponRange) return 0; // don't use secondary mode
return (source->critterAP_itemAmmoPid >= sf_item_w_mp_cost(source, ATKTYPE_RWEAPON_SECONDARY, 0)); // 1 - allow secondary mode
}
static void __declspec(naked) ai_pick_hit_mode_hook() {
__asm {
call caiHasWeapPrefType_;
test eax, eax;
jnz evaluation;
retn;
evaluation:
mov edx, edi;
mov ecx, esi;
jmp CheckWeaponRangeAndApCost;
}
}
static void __declspec(naked) ai_danger_source_hook() {
__asm {
call combat_check_bad_shot_;
cmp dword ptr [esp + 56], 0x42B235 + 5; // called from combat_ai_
je fix;
retn;
fix: // check result
cmp eax, 1; // exception: 1 - no ammo
setg al; // set 0 for result OK
retn;
}
}
static void __declspec(naked) cai_perform_distance_prefs_hack() {
__asm {
mov ebx, eax; // current distance to target
mov ecx, esi;
push 0; // no called shot
mov edx, ATKTYPE_RWEAPON_PRIMARY;
call sf_item_w_mp_cost;
mov edx, [esi + 0x40];
sub edx, eax; // ap - cost = free AP's
jle moveAway; // <= 0
lea edx, [edx + ebx - 1];
cmp edx, 5; // minimum threshold distance
jge skipMove; // distance >= 5?
// check combat rating
mov eax, esi;
call combatai_rating_;
mov edx, eax; // source rating
mov eax, edi;
call combatai_rating_;
cmp eax, edx; // target vs source rating
jl skipMove; // target rating is low
moveAway:
mov ebx, 10; // move away max distance
retn;
skipMove:
xor ebx, ebx; // skip moving away at the beginning of the turn
retn;
}
}
static void __declspec(naked) ai_move_away_hook() {
static const DWORD ai_move_away_hook_Ret = 0x4289DA;
__asm {
test ebx, ebx;
jl fix; // distance arg < 0
jmp ai_cap_;
fix:
neg ebx;
mov eax, [esi + 0x40]; // Current Action Points
cmp ebx, eax;
cmovg ebx, eax; // if (distance > ap) dist = ap
add esp, 4;
jmp ai_move_away_hook_Ret;
}
}
////////////////////////////////////////////////////////////////////////////////
static long __fastcall RollFriendlyFire(TGameObj* target, TGameObj* attacker) {
if (AI_CheckFriendlyFire(target, attacker)) {
long dice = RollRandom(1, 10);
return (StatLevel(attacker, STAT_iq) >= dice); // 1 - is friendly
}
return 0;
}
static void __declspec(naked) combat_safety_invalidate_weapon_func_hook_check() {
static const DWORD safety_invalidate_weapon_burst_friendly = 0x4216C9;
__asm {
pushadc;
mov ecx, esi; // target
call RollFriendlyFire;
test eax, eax;
jnz friendly;
popadc;
jmp combat_ctd_init_;
friendly:
lea esp, [esp + 8 + 3 * 4];
jmp safety_invalidate_weapon_burst_friendly; // "Friendly was in the way!"
}
}
////////////////////////////////////////////////////////////////////////////////
static void __fastcall CombatAttackHook(TGameObj* source, TGameObj* target) {
@@ -249,8 +391,11 @@ static void __declspec(naked) combat_attack_hook() {
}
void AIInit() {
HookCall(0x426A95, combat_attack_hook); // combat_attack_this_
HookCall(0x42A796, combat_attack_hook); // ai_attack_
const DWORD combatAttackAddr[] = {
0x426A95, // combat_attack_this_
0x42A796 // ai_attack_
};
HookCalls(combat_attack_hook, combatAttackAddr);
RetryCombatMinAP = GetConfigInt("Misc", "NPCsTryToSpendExtraAP", 0);
if (RetryCombatMinAP > 0) {
@@ -264,12 +409,31 @@ void AIInit() {
0x42AF15, // cai_attempt_w_reload_
0x42A970, 0x42AA56, // ai_try_attack_
};
for (int i = 0; i < sizeof(itemWReloadAddr) / 4; i++) {
HookCall(itemWReloadAddr[i], item_w_reload_hook);
}
HookCalls(item_w_reload_hook, itemWReloadAddr);
// Adds a check for the weapon range and the AP cost when AI is choosing weapon attack modes
HookCall(0x429F6D, ai_pick_hit_mode_hook);
/////////////////////// Combat AI behavior fixes ///////////////////////
// Fix to reduce friendly fire in burst attacks
// Adds a check/roll for friendly critters in the line of fire when AI uses burst attacks
HookCall(0x421666, combat_safety_invalidate_weapon_func_hook_check);
// Fix for duplicate critters being added to the list of potential targets for AI
MakeCall(0x428E75, ai_find_attackers_hack_target2, 2);
MakeCall(0x428EB5, ai_find_attackers_hack_target3);
MakeCall(0x428EE5, ai_find_attackers_hack_target4, 1);
#ifndef NDEBUG
if (iniGetInt("Debugging", "AIBugFixes", 1, ddrawIniDef) == 0) return;
#endif
// Tweak for finding new targets for party members
// Save the current target in the "target1" variable and find other potential targets
MakeCall(0x429074, ai_danger_source_hack_pm_newfind);
SafeWrite16(0x429074 + 5, 0x47EB); // jmp 0x4290C2
// Fix to allow fleeing NPC to use drugs
MakeCall(0x42B1DC, combat_ai_hack);
// Fix for AI not checking minimum hp properly for using stimpaks (prevents premature fleeing)
@@ -277,15 +441,25 @@ void AIInit() {
// Fix for NPC stuck in fleeing mode when the hit chance of a target was too low
HookCall(0x42B1E3, combat_ai_hook_FleeFix);
HookCall(0x42ABA8, ai_try_attack_hook_FleeFix);
HookCall(0x42ACE5, ai_try_attack_hook_FleeFix);
const DWORD aiTryAttackFleeAddr[] = {0x42ABA8, 0x42ACE5};
HookCalls(ai_try_attack_hook_FleeFix, aiTryAttackFleeAddr);
// Disable fleeing when NPC cannot move closer to target
BlockCall(0x42ADF6); // ai_try_attack_
// Fix for duplicate critters being added to the list of potential targets for AI
MakeCall(0x428E75, ai_find_attackers_hack_target2, 2);
MakeCall(0x428EB5, ai_find_attackers_hack_target3);
MakeCall(0x428EE5, ai_find_attackers_hack_target4, 1);
// Fix AI target selection for combat_check_bad_shot_ function returning a no_ammo result
const DWORD aiDangerSrcBadShotAddr[] = {0x42903A, 0x42918A};
HookCalls(ai_danger_source_hook, aiDangerSrcBadShotAddr);
// Fix AI behavior for "Snipe" distance preference
// The attacker will try to shoot the target instead of always running away from it at the beginning of the turn
MakeCall(0x42B086, cai_perform_distance_prefs_hack);
// Fix for ai_move_away_ engine function not working correctly in cases when needing to move a distance away from the target
// now the function also takes the distance argument in a negative value for moving away at a distance
HookCall(0x4289A7, ai_move_away_hook);
// also patch combat_safety_invalidate_weapon_func_ for returning out_range argument in a negative value
SafeWrite8(0x421628, 0xD0); // sub edx, eax > sub eax, edx
SafeWrite16(0x42162A, 0xFF40); // lea eax, [edx+1] > lea eax, [eax-1]
}
TGameObj* __stdcall AIGetLastAttacker(TGameObj* target) {
+4
View File
@@ -21,6 +21,10 @@
#include "FalloutEngine.h"
void AIInit();
TGameObj* __stdcall sf_check_critters_in_lof(TGameObj* object, DWORD checkTile, DWORD team);
TGameObj* __stdcall AI_CheckFriendlyFire(TGameObj* target, TGameObj* attacker);
void __stdcall AICombatStart();
void __stdcall AICombatEnd();
+25 -73
View File
@@ -188,9 +188,9 @@ static DWORD __fastcall CheckSetSad(BYTE openFlag, DWORD valueMul) {
return result;
}
static const DWORD object_move_back0 = 0x417611;
static const DWORD object_move_back1 = 0x417616;
static void __declspec(naked) object_move_hack() {
static const DWORD object_move_back0 = 0x417611;
static const DWORD object_move_back1 = 0x417616;
__asm {
mov ecx, ds:[ecx + 0x3C]; // openFlag
mov edx, [esp + 0x4C - 0x20]; // valueMul
@@ -224,8 +224,6 @@ skip:
void ApplyAnimationsAtOncePatches(signed char aniMax) {
if (aniMax <= 32) return;
int i;
//allocate memory to store larger animation struct arrays
anim_set = new BYTE[animRecordSize * (aniMax + 1)];
sad = new BYTE[sadSize * (aniMax + 1)];
@@ -237,19 +235,13 @@ void ApplyAnimationsAtOncePatches(signed char aniMax) {
SafeWrite8(0x413C07, aniMax - 12);
//PC movement animation limit checks (old 24) aniMax-8 -- +8 reserved for other critical animations?.
for (i = 0; i < sizeof(animPCMove) / 4; i++) {
SafeWrite8(animPCMove[i], aniMax - 8);
}
SafeWriteBatch<BYTE>(aniMax - 8, animPCMove);
//Max animation limit checks (old 32) aniMax
for (i = 0; i < sizeof(animMaxCheck) / 4; i++) {
SafeWrite8(animMaxCheck[i], aniMax);
}
SafeWriteBatch<BYTE>(aniMax, animMaxCheck);
//Max animations checks - animation struct size * max num of animations (old 2656*32=84992)
for (i = 0; i < sizeof(animMaxSizeCheck) / 4; i++) {
SafeWrite32(animMaxSizeCheck[i], animRecordSize * aniMax);
}
SafeWriteBatch<DWORD>(animRecordSize * aniMax, animMaxSizeCheck);
//divert old animation structure list pointers to newly allocated memory
@@ -259,47 +251,31 @@ void ApplyAnimationsAtOncePatches(signed char aniMax) {
SafeWrite32(0x413A9E, animSetAddr);
//old addr 0x54C1C0
for (i = 0; i < sizeof(fake_anim_set_C) / 4; i++) {
SafeWrite32(fake_anim_set_C[i], 12 + animSetAddr);
}
SafeWriteBatch<DWORD>(12 + animSetAddr, fake_anim_set_C);
//old addr 0x54CC14
for (i = 0; i < sizeof(anim_set_0) / 4; i++) {
SafeWrite32(anim_set_0[i], animRecordSize + animSetAddr);
}
SafeWriteBatch<DWORD>(animRecordSize + animSetAddr, anim_set_0);
//old addr 0x54CC18
for (i = 0; i < sizeof(anim_set_4) / 4; i++) {
SafeWrite32(anim_set_4[i], animRecordSize + 4 + animSetAddr);
}
SafeWriteBatch<DWORD>(animRecordSize + 4 + animSetAddr, anim_set_4);
//old addr 0x54CC1C
for (i = 0; i < sizeof(anim_set_8) / 4; i++) {
SafeWrite32(anim_set_8[i], animRecordSize + 8 + animSetAddr);
}
SafeWriteBatch<DWORD>(animRecordSize + 8 + animSetAddr, anim_set_8);
//old addr 0x54CC20
for (i = 0; i < sizeof(anim_set_C) / 4; i++) {
SafeWrite32(anim_set_C[i], animRecordSize + 12 + animSetAddr);
}
SafeWriteBatch<DWORD>(animRecordSize + 12 + animSetAddr, anim_set_C);
//old addr 0x54CC24
for (i = 0; i < sizeof(anim_set_10) / 4; i++) {
SafeWrite32(anim_set_10[i], animRecordSize + 16 + animSetAddr);
}
SafeWriteBatch<DWORD>(animRecordSize + 16 + animSetAddr, anim_set_10);
//old addr 0x54CC28
for (i = 0; i < sizeof(anim_set_14) / 4; i++) {
SafeWrite32(anim_set_14[i], animRecordSize + 20 + animSetAddr);
}
SafeWriteBatch<DWORD>(animRecordSize + 20 + animSetAddr, anim_set_14);
//old addr 0x54CC38
SafeWrite32(0x413F29, animRecordSize + 36 + animSetAddr);
//old addr 0x54CC3C
for (i = 0; i < sizeof(anim_set_28) / 4; i++) {
SafeWrite32(anim_set_28[i], animRecordSize + 40 + animSetAddr);
}
SafeWriteBatch<DWORD>(animRecordSize + 40 + animSetAddr, anim_set_28);
//old addr 0x54CC48
SafeWrite32(0x415C35, animRecordSize + 52 + animSetAddr);
@@ -307,67 +283,43 @@ void ApplyAnimationsAtOncePatches(signed char aniMax) {
//struct array 2///////////////////
//old addr 0x530014
for (i = 0; i < sizeof(sad_0) / 4; i++) {
SafeWrite32(sad_0[i], sadAddr);
}
SafeWriteBatch<DWORD>(sadAddr, sad_0);
//old addr 0x530018
for (i = 0; i < sizeof(sad_4) / 4; i++) {
SafeWrite32(sad_4[i], 4 + sadAddr);
}
SafeWriteBatch<DWORD>(4 + sadAddr, sad_4);
//old addr 0x53001C
for (i = 0; i < sizeof(sad_8) / 4; i++) {
SafeWrite32(sad_8[i], 8 + sadAddr);
}
SafeWriteBatch<DWORD>(8 + sadAddr, sad_8);
//old addr 0x530020
for (i = 0; i < sizeof(sad_C) / 4; i++) {
SafeWrite32(sad_C[i], 12 + sadAddr);
}
SafeWriteBatch<DWORD>(12 + sadAddr, sad_C);
//old addr 0x530024
for (i = 0; i < sizeof(sad_10) / 4; i++) {
SafeWrite32(sad_10[i], 16 + sadAddr);
}
SafeWriteBatch<DWORD>(16 + sadAddr, sad_10);
//old addr 0x530028
for (i = 0; i < sizeof(sad_14) / 4; i++) {
SafeWrite32(sad_14[i], 20 + sadAddr);
}
SafeWriteBatch<DWORD>(20 + sadAddr, sad_14);
//old addr 0x53002C
for (i = 0; i < sizeof(sad_18) / 4; i++) {
SafeWrite32(sad_18[i], 24 + sadAddr);
}
SafeWriteBatch<DWORD>(24 + sadAddr, sad_18);
//old addr 0x530030
for (i = 0; i < sizeof(sad_1C) / 4; i++) {
SafeWrite32(sad_1C[i], 28 + sadAddr);
}
SafeWriteBatch<DWORD>(28 + sadAddr, sad_1C);
//old addr 0x530034
for (i = 0; i < sizeof(sad_20) / 4; i++) {
SafeWrite32(sad_20[i], 32 + sadAddr);
}
SafeWriteBatch<DWORD>(32 + sadAddr, sad_20);
//old addr 0x530038
for (i = 0; i < sizeof(sad_24) / 4; i++) {
SafeWrite32(sad_24[i], 36 + sadAddr);
}
SafeWriteBatch<DWORD>(36 + sadAddr, sad_24);
//old addr 0x53003A
SafeWrite32(0x416903, 38 + sadAddr);
//old addr 0x53003B
for (i = 0; i < sizeof(sad_27) / 4; i++) {
SafeWrite32(sad_27[i], 39 + sadAddr);
}
SafeWriteBatch<DWORD>(39 + sadAddr, sad_27);
//old addr 0x53003C
for (i = 0; i < sizeof(sad_28) / 4; i++) {
SafeWrite32(sad_28[i], 40 + sadAddr);
}
SafeWriteBatch<DWORD>(40 + sadAddr, sad_28);
}
void AnimationsInit() {
+17 -17
View File
@@ -319,7 +319,7 @@ void GetArrays(int* _arrays) {
_arrays[pos++] = itr->second.isAssoc() ? 1 : 0;
_arrays[pos++] = itr->second.val.size();
_arrays[pos++] = itr->second.flags;
itr++;
++itr;
}
}
@@ -373,7 +373,7 @@ void DESetArray(int id, const DWORD* types, const char* data) {
TODO: move somewhere else
*/
const char* _stdcall GetSfallTypeName(DWORD dataType) {
const char* __stdcall GetSfallTypeName(DWORD dataType) {
switch (dataType) {
case DATATYPE_NONE:
return "(none)";
@@ -388,7 +388,7 @@ const char* _stdcall GetSfallTypeName(DWORD dataType) {
}
}
DWORD _stdcall getSfallTypeByScriptType(DWORD varType) {
DWORD __stdcall getSfallTypeByScriptType(DWORD varType) {
switch (varType & 0xFFFF) {
case VAR_TYPE_STR2:
case VAR_TYPE_STR:
@@ -401,7 +401,7 @@ DWORD _stdcall getSfallTypeByScriptType(DWORD varType) {
}
}
DWORD _stdcall getScriptTypeBySfallType(DWORD dataType) {
DWORD __stdcall getScriptTypeBySfallType(DWORD dataType) {
switch (dataType) {
case DATATYPE_STR:
return VAR_TYPE_STR;
@@ -413,7 +413,7 @@ DWORD _stdcall getScriptTypeBySfallType(DWORD dataType) {
}
}
DWORD _stdcall CreateArray(int len, DWORD flags) {
DWORD __stdcall CreateArray(int len, DWORD flags) {
sArrayVar var;
var.flags = (flags & 0xFFFFFFFE); // reset 1 bit
if (len < 0) {
@@ -437,13 +437,13 @@ DWORD _stdcall CreateArray(int len, DWORD flags) {
return nextArrayID++;
}
DWORD _stdcall TempArray(DWORD len, DWORD flags) {
DWORD __stdcall TempArray(DWORD len, DWORD flags) {
DWORD id = CreateArray(len, flags);
temporaryArrays.insert(id);
return id;
}
void _stdcall FreeArray(DWORD id) {
void __stdcall FreeArray(DWORD id) {
array_itr it = arrays.find(id);
if (it != arrays.end()) {
if (it->second.key.intVal) savedArrays.erase(it->second.key);
@@ -461,7 +461,7 @@ void DeleteAllTempArrays() {
}
}
DWORD _stdcall GetArrayKey(DWORD id, int index, DWORD* resultType) {
DWORD __stdcall GetArrayKey(DWORD id, int index, DWORD* resultType) {
*resultType = VAR_TYPE_INT;
if (arrays.find(id) == arrays.end() || index < -1 || index > arrays[id].size()) {
return 0;
@@ -491,7 +491,7 @@ DWORD _stdcall GetArrayKey(DWORD id, int index, DWORD* resultType) {
}
}
DWORD _stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType) {
DWORD __stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType) {
*resultType = VAR_TYPE_INT;
if (arrays.find(id) == arrays.end()) {
return 0;
@@ -526,7 +526,7 @@ DWORD _stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType) {
return 0;
}
void _stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valType, DWORD allowUnset) {
void __stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valType, DWORD allowUnset) {
keyType = getSfallTypeByScriptType(keyType);
valType = getSfallTypeByScriptType(valType);
if (arrays.find(id) == arrays.end()) {
@@ -579,7 +579,7 @@ void _stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valT
}
}
int _stdcall LenArray(DWORD id) {
int __stdcall LenArray(DWORD id) {
if (arrays.find(id) == arrays.end()) return -1;
return arrays[id].size();
}
@@ -640,7 +640,7 @@ static void MapSort(sArrayVar& arr, int type) {
}
}
void _stdcall ResizeArray(DWORD id, int newlen) {
void __stdcall ResizeArray(DWORD id, int newlen) {
if (newlen == -1 || arrays.find(id) == arrays.end()) return;
sArrayVar &arr = arrays[id];
@@ -686,11 +686,11 @@ errorResize:
DebugPrintf("\nOPCODE ERROR: resize_array() - array sorting error.");
}
void _stdcall FixArray(DWORD id) {
void __stdcall FixArray(DWORD id) {
temporaryArrays.erase(id);
}
int _stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType) {
int __stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType) {
*resultType = VAR_TYPE_INT;
datatype = getSfallTypeByScriptType(datatype);
if (arrays.find(id) == arrays.end()) {
@@ -714,7 +714,7 @@ int _stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType) {
return -1;
}
DWORD _stdcall LoadArray(DWORD key, DWORD keyType) {
DWORD __stdcall LoadArray(DWORD key, DWORD keyType) {
int dataType = getSfallTypeByScriptType(keyType);
if (dataType != DATATYPE_INT || key != 0) { // returns arrayId by it's key (ignoring int(0) because it is used to "unsave" array)
sArrayElement keyEl = sArrayElement(key, dataType);
@@ -741,7 +741,7 @@ DWORD _stdcall LoadArray(DWORD key, DWORD keyType) {
return 0; // not found
}
void _stdcall SaveArray(DWORD key, DWORD keyType, DWORD id) {
void __stdcall SaveArray(DWORD key, DWORD keyType, DWORD id) {
array_itr it, itArray = arrays.find(id); // arrayId => arrayVar
int dataType = getSfallTypeByScriptType(keyType);
if (itArray != arrays.end()) {
@@ -779,7 +779,7 @@ void _stdcall SaveArray(DWORD key, DWORD keyType, DWORD id) {
Should always return 0!
*/
long _stdcall StackArray(DWORD key, DWORD keyType, DWORD val, DWORD valType) {
long __stdcall StackArray(DWORD key, DWORD keyType, DWORD val, DWORD valType) {
DWORD id = stackArrayId;
if (id == 0 || arrays.find(id) == arrays.end()) {
return 0;
+16 -16
View File
@@ -153,17 +153,17 @@ void GetArrays(int* arrays);
void DEGetArray(int id, DWORD* types, char* data);
void DESetArray(int id, const DWORD* types, const char* data);
const char* _stdcall GetSfallTypeName(DWORD dataType);
DWORD _stdcall getSfallTypeByScriptType(DWORD varType);
DWORD _stdcall getScriptTypeBySfallType(DWORD dataType);
const char* __stdcall GetSfallTypeName(DWORD dataType);
DWORD __stdcall getSfallTypeByScriptType(DWORD varType);
DWORD __stdcall getScriptTypeBySfallType(DWORD dataType);
// creates new normal (persistent) array. len == -1 specifies associative array (map)
DWORD _stdcall CreateArray(int len, DWORD flags);
DWORD __stdcall CreateArray(int len, DWORD flags);
// same as CreateArray, but creates temporary array instead (will die at the end of the frame)
DWORD _stdcall TempArray(DWORD len, DWORD flags);
DWORD __stdcall TempArray(DWORD len, DWORD flags);
// destroys array
void _stdcall FreeArray(DWORD id);
void __stdcall FreeArray(DWORD id);
// destroy all temp arrays
void DeleteAllTempArrays();
@@ -171,31 +171,31 @@ void DeleteAllTempArrays();
/*
op_get_array_key can be used to iterate over all keys in associative array
*/
DWORD _stdcall GetArrayKey(DWORD id, int index, DWORD* resultType);
DWORD __stdcall GetArrayKey(DWORD id, int index, DWORD* resultType);
// get array element by index (list) or key (map)
DWORD _stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType);
DWORD __stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType);
// set array element by index or key
void _stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valType, DWORD allowUnset);
void __stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valType, DWORD allowUnset);
// number of elements in list or pairs in map
int _stdcall LenArray(DWORD id);
int __stdcall LenArray(DWORD id);
// change array size (only works with list)
void _stdcall ResizeArray(DWORD id, int newlen);
void __stdcall ResizeArray(DWORD id, int newlen);
// make temporary array persistent
void _stdcall FixArray(DWORD id);
void __stdcall FixArray(DWORD id);
// searches for a given element in array and returns it's index (for list) or key (for map) or int(-1) if not found
int _stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType);
int __stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType);
// get saved array by it's key
DWORD _stdcall LoadArray(DWORD key, DWORD keyType);
DWORD __stdcall LoadArray(DWORD key, DWORD keyType);
// make array saved into the savegame with associated key
void _stdcall SaveArray(DWORD key, DWORD keyType, DWORD id);
void __stdcall SaveArray(DWORD key, DWORD keyType, DWORD id);
// special function that powers array expressions
long _stdcall StackArray(DWORD key, DWORD keyType, DWORD val, DWORD valType);
long __stdcall StackArray(DWORD key, DWORD keyType, DWORD val, DWORD valType);
+7 -11
View File
@@ -55,9 +55,9 @@ static const DWORD bboxSlotAddr[] = {
0x4616F7, 0x46170F, 0x461736, 0x4616B1, 0x46151D, 0x4615B3
};
static const DWORD DisplayBoxesRet1 = 0x4615A8;
static const DWORD DisplayBoxesRet2 = 0x4615BE;
static void __declspec(naked) DisplayBoxesHack() {
static const DWORD DisplayBoxesRet1 = 0x4615A8;
static const DWORD DisplayBoxesRet2 = 0x4615BE;
__asm {
mov edx, [boxText];
xor ebx, ebx;
@@ -109,8 +109,8 @@ skip:
}
}
static const DWORD SetIndexBoxRet = 0x4612E8;
static void __declspec(naked) BarBoxesIndexHack() {
static const DWORD SetIndexBoxRet = 0x4612E8;
__asm {
mov eax, ds:[0x4612E2]; // fontnum
mov ecx, setBoxIndex; // start index
@@ -119,9 +119,9 @@ static void __declspec(naked) BarBoxesIndexHack() {
}
}
static const DWORD SizeLoopBoxRet = 0x461477;
static const DWORD ExitLoopBoxRet = 0x461498;
static void __declspec(naked) BarBoxesSizeHack() {
static const DWORD SizeLoopBoxRet = 0x461477;
static const DWORD ExitLoopBoxRet = 0x461498;
__asm {
cmp edx, sizeBox;
jz exitLoop;
@@ -201,9 +201,7 @@ void __stdcall BarBoxes_SetText(int box, const char* text, DWORD color) {
}
static void SetEngine(int count) {
for (int i = 0; i < sizeof(bboxMemAddr) / 4; i++) {
SafeWrite32(bboxMemAddr[i], (DWORD)boxes + 8); //.mem
}
SafeWriteBatch<DWORD>((DWORD)boxes + 8, bboxMemAddr); //.mem
SafeWrite32(0x4612FE, (DWORD)boxes + 4); //.colour
SafeWrite32(0x46133C, (DWORD)boxes); //.msg
@@ -297,9 +295,7 @@ void BarBoxesInit() {
totalBoxCount = boxCount = initCount;
MakeCall(0x4615FA, refresh_box_bar_win_hack);
for (int i = 0; i < sizeof(bboxSlotAddr) / 4; i++) {
SafeWrite32(bboxSlotAddr[i], (DWORD)newBoxSlot); // _bboxslot
}
SafeWriteBatch<DWORD>((DWORD)newBoxSlot, bboxSlotAddr); // _bboxslot
ifaceWidth = iniGetInt("IFACE", "IFACE_BAR_WIDTH", 640, ".\\f2_res.ini");
if (ifaceWidth < 640) ifaceWidth = 640;
+1 -1
View File
@@ -42,8 +42,8 @@ static sBook* __fastcall FindBook(DWORD pid) {
return 0;
}
static const DWORD obj_use_book_hook_back = 0x49BA5A;
static void __declspec(naked) obj_use_book_hook() {
static const DWORD obj_use_book_hook_back = 0x49BA5A;
__asm {
mov edi, -1;
mov ecx, eax;
+65 -50
View File
@@ -33,7 +33,7 @@ void DrugsSaveFix(HANDLE file) {
DWORD sizeWrite, count = drugsPid.size();
WriteFile(file, &count, 4, &sizeWrite, 0);
if (!count) return;
for (std::list<int>::iterator it = drugsPid.begin(); it != drugsPid.end(); it++) {
for (std::list<int>::iterator it = drugsPid.begin(); it != drugsPid.end(); ++it) {
int pid = *it;
WriteFile(file, &pid, 4, &sizeWrite, 0);
}
@@ -915,36 +915,38 @@ end:
static void __declspec(naked) MultiHexCombatMoveFix() {
static const DWORD ai_move_steps_closer_move_object_ret = 0x42A192;
__asm {
mov edx, [esp + 4]; // source's destination tilenum
cmp [edi + 0x4], edx; // target's tilenum
je checkObj;
retn; // tilenums are not equal, always move to tile
checkObj:
test [edi + 0x25], 0x08; // is target multihex?
jnz multiHex;
test [esi + 0x25], 0x08; // is source multihex?
jz moveTile;
jnz multiHex;
retn; // move to tile
multiHex:
mov edx, [esp + 4]; // source's destination tilenum
cmp [edi + 0x4], edx; // target's tilenum
jnz moveTile;
add esp, 4;
jmp ai_move_steps_closer_move_object_ret;
moveTile:
retn;
jmp ai_move_steps_closer_move_object_ret; // move to object
}
}
static void __declspec(naked) MultiHexCombatRunFix() {
static const DWORD ai_move_steps_closer_run_object_ret = 0x42A169;
__asm {
mov edx, [esp + 4]; // source's destination tilenum
cmp [edi + 0x4], edx; // target's tilenum
je checkObj;
retn; // tilenums are not equal, always run to tile
checkObj:
test [edi + 0x25], 0x08; // is target multihex?
jnz multiHex;
test [esi + 0x25], 0x08; // is source multihex?
jz runTile;
jnz multiHex;
retn; // run to tile
multiHex:
mov edx, [esp + 4]; // source's destination tilenum
cmp [edi + 0x4], edx; // target's tilenum
jnz runTile;
add esp, 4;
jmp ai_move_steps_closer_run_object_ret;
runTile:
retn;
jmp ai_move_steps_closer_run_object_ret; // run to object
}
}
@@ -1434,13 +1436,17 @@ end:
static void __declspec(naked) combat_display_hack() {
__asm {
mov ebx, 0x42536B;
je end; // This is a critter
cmp dword ptr [ecx+0x78], -1; // Does the target have a script?
jne end; // Yes
mov ebx, 0x425413;
test [esi + 0x24], 0x8; // ctd.mainTarget (ObjectFlag Flat)
jnz end; // Main target is flat (engine jump)
cmp eax, OBJ_TYPE_CRITTER << 24; // Is this a critter?
je end; // Yes (engine no jump)
cmp dword ptr [ecx + 0x78], -1; // Does the target have a script?
// jne hasScript; // Yes (engine no jump)
lahf;
xor ah, 0x40; // invert ZF (01000000b)
sahf; // if there is a script, do not jump (ZF is set)
end:
jmp ebx;
retn;
}
}
@@ -1488,7 +1494,7 @@ static void __declspec(naked) ResetPlayer_hook() {
static void __declspec(naked) obj_move_to_tile_hack() {
static const DWORD obj_move_to_tile_Ret = 0x48A74E;
__asm {
cmp ds:[_loadingGame], 0; // prevents leaving the map after reloading a saved game if the player died
cmp ds:[_loadingGame], 0; // prevents leaving the map after loading a saved game if the player died
jnz skip; // on the world map from radiation (or in some cases on another map)
cmp dword ptr ds:[_map_state], 0; // map number, -1 exit to worldmap
jz mapLeave;
@@ -1517,7 +1523,7 @@ end:
}
}
static void __declspec(naked) ai_move_steps_closer_hook() {
static void __declspec(naked) ai_combat_turn_run_hook() {
__asm {
call combat_turn_run_;
movzx dx, word ptr [esi + 0x44]; // combat_data.results
@@ -1563,7 +1569,7 @@ static void __stdcall AppendText(const char* text, const char* desc) {
}
tempBuffer[len++] = ' ';
tempBuffer[len] = 0;
currDescLen = len;
currDescLen = len;
} else if (currDescLen == 0) {
tempBuffer[0] = 0;
}
@@ -1687,7 +1693,7 @@ skip:
static void __declspec(naked) op_obj_can_see_obj_hack() {
__asm {
mov eax, [esp + 0x2C - 0x28 + 4]; // source
mov ecx, [eax + 4]; // source.tile
mov ecx, [eax + 0x4]; // source.tile
cmp ecx, -1;
jz end;
mov eax, [eax + 0x28]; // source.elev
@@ -2372,10 +2378,12 @@ end:
static void __declspec(naked) combat_should_end_hack() {
static const DWORD combat_should_end_break = 0x422D00;
__asm { // ecx = dude.team_num
cmp ecx, [ebp + 0x50]; // npc who_hit_me.team_num
je break;
test byte ptr [edx], 1; // npc combat_data.combat_state
jnz break;
cmp ecx, [ebp + 0x50]; // npc->combat_data.who_hit_me.team_num (engine code)
je break; // attacker is in the player's team
test [ebp + 0x44], DAM_DEAD; // npc->combat_data.who_hit_me.damageFlags
jz break; // target is still alive
test byte ptr [edx], 1; // npc->combat_data.combat_state
jnz break; // npc is in combat
retn; // check next critter
break:
add esp, 4;
@@ -2637,7 +2645,7 @@ checkTiles:
void BugFixesInit()
{
#ifndef NDEBUG
if (iniGetInt("Debugging", "BugFixes", 1, ddrawIniDef) == 0) return;
if (iniGetInt("Debugging", "BugFixes", 1, ddrawIniDef) == 0) return;
#endif
// Fix vanilla negate operator for float values
@@ -2688,8 +2696,8 @@ void BugFixesInit()
// http://fforum.kochegarov.com/index.php?showtopic=29288&view=findpost&p=332242
//if (GetConfigInt("Misc", "TooManyItemsBugFix", 1)) {
dlog("Applying preventive patch for \"Too Many Items\" bug.", DL_INIT);
HookCall(0x4A596A, scr_write_ScriptNode_hook);
HookCall(0x4A59C1, scr_write_ScriptNode_hook);
const DWORD writeScriptNodeAddr[] = {0x4A596A, 0x4A59C1};
HookCalls(scr_write_ScriptNode_hook, writeScriptNodeAddr);
dlogr(" Done", DL_INIT);
//}
@@ -2862,8 +2870,8 @@ void BugFixesInit()
//if (GetConfigInt("Misc", "MultiHexPathingFix", 1)) {
dlog("Applying MultiHex Pathing Fix.", DL_INIT);
MakeCall(0x42901F, MultiHexFix);
MakeCall(0x429170, MultiHexFix);
const DWORD multiHexFixAddr[] = {0x42901F, 0x429170};
MakeCalls(MultiHexFix, multiHexFixAddr);
// Fix for multihex critters moving too close and overlapping their targets in combat
MakeCall(0x42A14F, MultiHexCombatRunFix, 1);
MakeCall(0x42A178, MultiHexCombatMoveFix, 1);
@@ -2980,7 +2988,8 @@ void BugFixesInit()
//}
// Fix for the displayed message when the attack randomly hits a target that is not a critter and has a script attached
MakeJump(0x425365, combat_display_hack);
// Tweak: if the main target has Flat flag set, display the "You missed" message instead of the message of hitting another object
MakeCall(0x42535F, combat_display_hack, 1);
// Fix for damage_p_proc being called for misses if the target is not a critter
MakeCall(0x424CD2, apply_damage_hack);
@@ -3008,7 +3017,12 @@ void BugFixesInit()
// Fix for critters killed in combat by scripting still being able to move in their combat turn if the distance parameter
// in their AI packages is set to stay_close/charge, or NPCsTryToSpendExtraAP is enabled
HookCall(0x42A1A8, ai_move_steps_closer_hook); // old 0x42B24D
const DWORD aiCombatTurnRunAddr[] = {
0x42A1A8, // ai_move_steps_closer_ (old 0x42B24D)
0x42898D, // ai_run_away_ (potential fix)
0x428AB3 // ai_move_away_ (potential fix)
};
HookCalls(ai_combat_turn_run_hook, aiCombatTurnRunAddr);
// Fix instant death critical
dlog("Applying instant death fix.", DL_INIT);
@@ -3062,8 +3076,8 @@ void BugFixesInit()
HookCall(0x42954B, ai_best_weapon_hook);
// also change the priority multiplier for having weapon perk to 3x (the original is 5x)
if (bestWeaponPerkFix > 1) {
SafeWrite8(0x42955E, 0x55);
SafeWrite8(0x4296E7, 0x55);
const DWORD aiBestWeaponAddr[] = {0x42955E, 0x4296E7};
SafeWriteBatch<BYTE>(0x55, aiBestWeaponAddr); // lea eax, [edx * 2];
}
dlogr(" Done", DL_INIT);
}
@@ -3074,8 +3088,8 @@ void BugFixesInit()
HookCall(0x4C1042, wmSetupRandomEncounter_hook);
// Fix for being unable to sell/give items in the barter screen when the player/party member is overloaded
HookCall(0x474C73, barter_attempt_transaction_hook_weight);
HookCall(0x474CCA, barter_attempt_transaction_hook_weight);
const DWORD barterAttTransAddr[] = {0x474C73, 0x474CCA};
HookCalls(barter_attempt_transaction_hook_weight, barterAttTransAddr);
// Fix for the underline position in the inventory display window when the item name is longer than one line
MakeCall(0x472F5F, inven_obj_examine_func_hack, 1);
@@ -3115,8 +3129,8 @@ void BugFixesInit()
// Display messages about radiation for the active geiger counter
if (GetConfigInt("Misc", "ActiveGeigerMsgs", 1)) {
dlog("Applying active geiger counter messages patch.", DL_INIT);
SafeWrite8(0x42D424, 0x74); // jnz > jz
SafeWrite8(0x42D444, 0x74);
const DWORD activeGeigerAddr[] = {0x42D424, 0x42D444};
SafeWriteBatch<BYTE>(0x74, activeGeigerAddr); // jnz > jz
dlogr(" Done", DL_INIT);
}
// Display a pop-up message box about death from radiation
@@ -3171,15 +3185,13 @@ void BugFixesInit()
// Fix pickup_obj/drop_obj/use_obj functions, change them to get pointer from script.self instead of script.target
// script.target contains an incorrect pointer, which may vary depending on the situations in the game
dlog("Applying pickup_obj/drop_obj/use_obj fix.", DL_INIT);
const DWORD ScriptTargetAddr[] = {
const DWORD scriptTargetAddr[] = {
0x456554, // op_pickup_obj_
0x456600, // op_drop_obj_
0x456A6D, // op_use_obj_
0x456AA4 // op_use_obj_
};
for (int i = 0; i < sizeof(ScriptTargetAddr) / 4; i++) {
SafeWrite8(ScriptTargetAddr[i], 0x34); // script.target > script.self
}
SafeWriteBatch<BYTE>(0x34, scriptTargetAddr); // script.target > script.self
dlogr(" Done", DL_INIT);
// Fix for critters not attacking the player in combat when loading a game saved in combat mode
@@ -3189,8 +3201,8 @@ void BugFixesInit()
MakeCall(0x422E25, combat_hack_load);
// Fix for the reserved item FRM being displayed in the top-left corner when in the loot/barter screens
HookCall(0x473AC9, JesseContainerFid);
HookCall(0x475895, JesseContainerFid);
const DWORD jesseContFidAddr[] = {0x473AC9, 0x475895};
HookCalls(JesseContainerFid, jesseContFidAddr);
// Fix the return value of has_skill function for incorrect skill numbers
SafeWrite32(0x4AA56B, 0);
@@ -3216,8 +3228,11 @@ void BugFixesInit()
// Fix for the overflow of the automap tables when the number of maps in maps.txt is more than 160
HookCall(0x41C0FC, automap_pip_save_hook);
HookCall(0x499212, PrintAutoMapList); // PrintAMList_
HookCall(0x499013, PrintAutoMapList); // PrintAMelevList_
const DWORD printAutoMapAddr[] = {
0x499212, // PrintAMList_
0x499013 // PrintAMelevList_
};
HookCalls(PrintAutoMapList, printAutoMapAddr);
// Fix "out of bounds" bug when printing the automap list
HookCall(0x499240, PrintAMList_hook);
+1 -1
View File
@@ -45,8 +45,8 @@ static long __fastcall ComputeSpray(DWORD* roundsLeftOut, DWORD* roundsRightOut,
return (result % compute_spray_target_div) ? ++roundsMainTarget : roundsMainTarget; // if remainder then round up
}
static const DWORD compute_spray_rounds_back = 0x42353A;
static void __declspec(naked) compute_spray_rounds_distribution() {
static const DWORD compute_spray_rounds_back = 0x42353A;
__asm {
push ecx;
lea ecx, [esp + 8 + 4]; // roundsLeft - out
+47 -40
View File
@@ -29,7 +29,7 @@
static const DWORD bodypartAddr[] = {
0x425562, // combat_display_
0x42A68F, 0x42A739, // ai_called_shot_
0x429E82, 0x429EC2, 0x429EFF, // ai_pick_hit_mode_
// 0x429E82, 0x429EC2, 0x429EFF, // ai_pick_hit_mode_
0x423231, 0x423268, // check_ranged_miss_
0x4242D4, // attack_crit_failure_
// for combat_ctd_init_ func
@@ -38,7 +38,7 @@ static const DWORD bodypartAddr[] = {
0x421656, 0x421675, // combat_safety_invalidate_weapon_func_
0x421CC0, // combat_begin_extra_
0x4229A9, // combat_turn_
0x42330E, 0x4233AB, // shoot_along_path_
0x42330E, 0x4233A6, 0x4233AB, // shoot_along_path_
0x423E25, 0x423E2A, // compute_explosion_on_extras_
0x425F83, // combat_anim_finished_
0x42946D, // ai_best_weapon_
@@ -81,13 +81,13 @@ static std::vector<DWORD> forcedAS;
static bool combatDisabled;
static char combatBlockedMessage[128];
static void _stdcall CombatBlocked() {
static void __stdcall CombatBlocked() {
DisplayConsoleMessage(combatBlockedMessage);
}
static const DWORD BlockCombatHook1Ret1 = 0x45F6AF;
static const DWORD BlockCombatHook1Ret2 = 0x45F6D7;
static void __declspec(naked) intface_use_item_hook() {
static const DWORD BlockCombatHook1Ret1 = 0x45F6AF;
static const DWORD BlockCombatHook1Ret2 = 0x45F6D7;
__asm {
cmp combatDisabled, 0;
jne block;
@@ -118,17 +118,17 @@ void __stdcall SetBlockCombat(long toggle) {
combatDisabled = toggle != 0;
}
static DWORD __fastcall add_check_for_item_ammo_cost(register TGameObj* weapon, DWORD hitMode) {
DWORD __fastcall Combat_check_item_ammo_cost(TGameObj* weapon, DWORD hitMode) {
DWORD rounds = 1;
DWORD anim = ItemWAnimWeap(weapon, hitMode);
long anim = ItemWAnimWeap(weapon, hitMode);
if (anim == 46 || anim == 47) { // ANIM_fire_burst or ANIM_fire_continuous
rounds = ItemWRounds(weapon); // ammo in burst
}
AmmoCostHook_Script(1, weapon, rounds); // get rounds cost from hook
DWORD currAmmo = ItemWCurrAmmo(weapon);
long currAmmo = ItemWCurrAmmo(weapon);
DWORD cost = 1; // default cost
long cost = 1; // default cost
if (currAmmo > 0) {
cost = rounds / currAmmo;
if (rounds % currAmmo) cost++; // round up
@@ -142,7 +142,7 @@ static void __declspec(naked) combat_check_bad_shot_hook() {
push edx;
push ecx; // weapon
mov edx, edi; // hitMode
call add_check_for_item_ammo_cost;
call Combat_check_item_ammo_cost;
pop ecx;
pop edx;
retn;
@@ -154,20 +154,20 @@ static void __declspec(naked) ai_search_inven_weap_hook() {
__asm {
push ecx;
mov ecx, eax; // weapon
mov edx, 2; // hitMode - ATKTYPE_RWEAPON_PRIMARY
call add_check_for_item_ammo_cost; // enough ammo?
mov edx, ATKTYPE_RWEAPON_PRIMARY; // hitMode
call Combat_check_item_ammo_cost; // enough ammo?
pop ecx;
retn;
}
}
// switch weapon mode from secondary to primary if there is not enough ammo to shoot
static const DWORD ai_try_attack_search_ammo = 0x42AA1E;
static const DWORD ai_try_attack_continue = 0x42A929;
static void __declspec(naked) ai_try_attack_hook() {
static const DWORD ai_try_attack_search_ammo = 0x42AA1E;
static const DWORD ai_try_attack_continue = 0x42A929;
__asm {
mov ebx, [esp + 0x364 - 0x38]; // hit mode
cmp ebx, 3; // ATKTYPE_RWEAPON_SECONDARY
cmp ebx, ATKTYPE_RWEAPON_SECONDARY;
jne searchAmmo;
mov edx, [esp + 0x364 - 0x3C]; // weapon
mov eax, [edx + 0x3C]; // curr ammo
@@ -176,7 +176,7 @@ static void __declspec(naked) ai_try_attack_hook() {
searchAmmo:
jmp ai_try_attack_search_ammo;
tryAttack:
mov ebx, 2; // ATKTYPE_RWEAPON_PRIMARY
mov ebx, ATKTYPE_RWEAPON_PRIMARY;
mov [esp + 0x364 - 0x38], ebx; // change hit mode
jmp ai_try_attack_continue;
}
@@ -209,7 +209,7 @@ static void __declspec(naked) compute_spray_hack() {
}
static double ApplyModifiers(std::vector<KnockbackModifier>* mods, TGameObj* object, double val) {
for (DWORD i = 0; i < mods->size(); i++) {
for (size_t i = 0; i < mods->size(); i++) {
KnockbackModifier* mod = &(*mods)[i];
if (mod->id == object->ID) {
switch (mod->type) {
@@ -247,8 +247,8 @@ static void __declspec(naked) compute_damage_hack() {
}
}
static const DWORD KnockbackRetAddr = 0x4136E1;
static void __declspec(naked) compute_dmg_damage_hack() {
static const DWORD KnockbackRetAddr = 0x4136E1;
__asm {
push ecx
push esi; // Target
@@ -264,7 +264,7 @@ static void __declspec(naked) compute_dmg_damage_hack() {
}
static int __fastcall HitChanceMod(int base, TGameObj* critter) {
for (DWORD i = 0; i < hitChanceMods.size(); i++) {
for (size_t i = 0; i < hitChanceMods.size(); i++) {
if (critter->ID == hitChanceMods[i].id) {
return min(base + hitChanceMods[i].mod, hitChanceMods[i].maximum);
}
@@ -291,7 +291,7 @@ static long __fastcall CheckDisableBurst(TGameObj* critter) {
return 0;
}
static void __declspec(naked) ai_pick_hit_mode_hack() {
static void __declspec(naked) ai_pick_hit_mode_hack_noSecondary() {
__asm {
mov ebx, [eax + 0x94]; // cap->area_attack_mode
push eax;
@@ -306,7 +306,7 @@ static void __declspec(naked) ai_pick_hit_mode_hack() {
}
}
void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD mode) {
void __stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD mode) {
std::vector<KnockbackModifier>* mods;
switch (mode) {
case 0:
@@ -348,7 +348,7 @@ void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD mod
mods->push_back(mod);
}
void _stdcall KnockbackRemoveMod(TGameObj* object, DWORD mode) {
void __stdcall KnockbackRemoveMod(TGameObj* object, DWORD mode) {
std::vector<KnockbackModifier>* mods;
switch (mode) {
case 0:
@@ -363,7 +363,7 @@ void _stdcall KnockbackRemoveMod(TGameObj* object, DWORD mode) {
default:
return;
}
for (DWORD i = 0; i < mods->size(); i++) {
for (size_t i = 0; i < mods->size(); i++) {
if ((*mods)[i].id == object->ID) {
mods->erase(mods->begin() + i);
if (mode == 0) SetNewEngineID(object); // revert to engine range id
@@ -372,7 +372,7 @@ void _stdcall KnockbackRemoveMod(TGameObj* object, DWORD mode) {
}
}
void _stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod) {
void __stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod) {
if ((DWORD)critter == -1) {
baseHitChance.maximum = maximum;
baseHitChance.mod = mod;
@@ -380,7 +380,7 @@ void _stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod) {
}
if (critter->pid >> 24 != OBJ_TYPE_CRITTER) return;
long id = SetObjectUniqueID(critter);
for (DWORD i = 0; i < hitChanceMods.size(); i++) {
for (size_t i = 0; i < hitChanceMods.size(); i++) {
if (id == hitChanceMods[i].id) {
hitChanceMods[i].maximum = maximum;
hitChanceMods[i].mod = mod;
@@ -394,7 +394,7 @@ void _stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod) {
hitChanceMods.push_back(cm);
}
void _stdcall SetNoBurstMode(TGameObj* critter, bool on) {
void __stdcall SetNoBurstMode(TGameObj* critter, bool on) {
if (critter == *ptr_obj_dude || critter->pid >> 24 != OBJ_TYPE_CRITTER) return;
long id = SetObjectUniqueID(critter);
@@ -409,18 +409,18 @@ void _stdcall SetNoBurstMode(TGameObj* critter, bool on) {
static int __fastcall AimedShotTest(DWORD pid) {
if (pid) pid = ((TGameObj*)pid)->pid;
for (DWORD i = 0; i < disabledAS.size(); i++) {
for (size_t i = 0; i < disabledAS.size(); i++) {
if (disabledAS[i] == pid) return -1;
}
for (DWORD i = 0; i < forcedAS.size(); i++) {
for (size_t i = 0; i < forcedAS.size(); i++) {
if (forcedAS[i] == pid) return 1;
}
return 0;
}
static const DWORD aimedShotRet1 = 0x478EE4;
static const DWORD aimedShotRet2 = 0x478EEA;
static void __declspec(naked) item_w_called_shot_hook() {
static const DWORD aimedShotRet1 = 0x478EE4;
static const DWORD aimedShotRet2 = 0x478EEA;
__asm {
push edx;
mov ecx, edx; // item
@@ -445,23 +445,23 @@ static void HookAimedShots() {
hookedAimedShot = true;
}
void _stdcall DisableAimedShots(DWORD pid) {
void __stdcall DisableAimedShots(DWORD pid) {
if (!hookedAimedShot) HookAimedShots();
for (DWORD i = 0; i < forcedAS.size(); i++) {
for (size_t i = 0; i < forcedAS.size(); i++) {
if (forcedAS[i] == pid) forcedAS.erase(forcedAS.begin() + (i--));
}
for (DWORD i = 0; i < disabledAS.size(); i++) {
for (size_t i = 0; i < disabledAS.size(); i++) {
if (disabledAS[i] == pid) return;
}
disabledAS.push_back(pid);
}
void _stdcall ForceAimedShots(DWORD pid) {
void __stdcall ForceAimedShots(DWORD pid) {
if (!hookedAimedShot) HookAimedShots();
for (DWORD i = 0; i < disabledAS.size(); i++) {
for (size_t i = 0; i < disabledAS.size(); i++) {
if (disabledAS[i] == pid) disabledAS.erase(disabledAS.begin() + (i--));
}
for (DWORD i = 0; i < forcedAS.size(); i++) {
for (size_t i = 0; i < forcedAS.size(); i++) {
if (forcedAS[i] == pid) return;
}
forcedAS.push_back(pid);
@@ -491,6 +491,13 @@ static void BodypartHitReadConfig() {
bodypartHit.Uncalled = static_cast<long>(GetConfigInt("Misc", "BodyHit_Torso_Uncalled", 0));
}
static void __declspec(naked) ai_pick_hit_mode_hook_bodypart() {
__asm {
mov ebx, 8; // replace Body_Torso with Body_Uncalled
jmp determine_to_hit_;
}
}
static void __declspec(naked) apply_damage_hack() {
__asm {
xor edx, edx;
@@ -538,7 +545,7 @@ void CombatInit() {
BlockCall(0x424796);
// Actually disables all secondary attacks for the critter, regardless of whether the weapon has a burst attack
MakeCall(0x429E44, ai_pick_hit_mode_hack, 1); // NoBurst
MakeCall(0x429E44, ai_pick_hit_mode_hack_noSecondary, 1); // NoBurst
if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) {
MakeCall(0x4234B3, compute_spray_hack, 1);
@@ -555,7 +562,7 @@ void CombatInit() {
SafeWrite8(0x423830, 0xEB); // compute_attack_
BlockCall(0x42303F); // block Body_Torso check (combat_attack_)
SafeWrite8(0x42A713, 7); // Body_Uncalled > Body_Groin (ai_called_shot_)
for (int i = 0; i < sizeof(bodypartAddr) / 4; i++) { // replace Body_Torso with Body_Uncalled
SafeWrite8(bodypartAddr[i], 8);
}
SafeWriteBatch<BYTE>(8, bodypartAddr); // replace Body_Torso with Body_Uncalled
const DWORD pickHitBodypartAddr[] = {0x429E8C, 0x429ECC, 0x429F09};
HookCalls(ai_pick_hit_mode_hook_bodypart, pickHitBodypartAddr);
}
+11 -8
View File
@@ -37,13 +37,16 @@ struct ChanceModifier {
}
};
void __stdcall SetBlockCombat(long toggle);
void _stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod);
void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD mode);
void _stdcall KnockbackRemoveMod(TGameObj* object, DWORD mode);
void CombatInit();
void Combat_OnGameLoad();
void _stdcall SetNoBurstMode(TGameObj* critter, bool on);
void _stdcall DisableAimedShots(DWORD pid);
void _stdcall ForceAimedShots(DWORD pid);
void __stdcall SetBlockCombat(long toggle);
DWORD __fastcall Combat_check_item_ammo_cost(TGameObj* weapon, DWORD hitMode);
void __stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod);
void __stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD mode);
void __stdcall KnockbackRemoveMod(TGameObj* object, DWORD mode);
void __stdcall SetNoBurstMode(TGameObj* critter, bool on);
void __stdcall DisableAimedShots(DWORD pid);
void __stdcall ForceAimedShots(DWORD pid);
+1 -1
View File
@@ -28,8 +28,8 @@ static void __stdcall ConsoleFilePrint(const char* msg) {
consoleFile << msg << std::endl;
}
static const DWORD ConsoleHookRet = 0x431871;
static void __declspec(naked) ConsoleHook() {
static const DWORD ConsoleHookRet = 0x431871;
__asm {
pushadc;
push eax;
+2 -2
View File
@@ -152,8 +152,8 @@ morelines:
}
void CreditsInit() {
HookCall(0x480C49, ShowCreditsHook);
HookCall(0x43F881, ShowCreditsHook);
const DWORD showCreditsAddr[] = {0x480C49, 0x43F881};
HookCalls(ShowCreditsHook, showCreditsAddr);
if (GetConfigInt("Misc", "CreditsAtBottom", 0)) {
HookCall(0x42CB49, CreditsNextLineHook_Bottom);
} else {
+11 -33
View File
@@ -41,31 +41,9 @@ static const char* CritNames[] = {
"FailMessage",
};
struct CritStruct {
union {
struct {
// This is divided by 2, so a value of 3 does 1.5x damage, and 8 does 4x damage.
long damageMult;
// This is a flag bit field (DAM_*) controlling what effects the critical causes.
long effectFlags;
// This makes a check against a (SPECIAL) stat. Values of 2 (endurance), 5 (agility), and 6 (luck) are used, but other stats will probably work as well. A value of -1 indicates that no check is to be made.
long statCheck;
// Affects the outcome of the stat check, if one is made. Positive values make it easier to pass the check, and negative ones make it harder.
long statMod;
// Another bit field, using the same values as EffectFlags. If the stat check is failed, these are applied in addition to the earlier ones.
long failureEffect;
// The message to show when this critical occurs, taken from combat.msg .
long message;
// Shown instead of Message if the stat check is failed.
long failMessage;
};
long values[7];
};
};
static CritStruct* baseCritTable; // Base critical table set up via enabling OverrideCriticalTable in ddraw.ini
static CritStruct* critTable;
static CritStruct* playerCrit;
static CritInfo* baseCritTable; // Base critical table set up via enabling OverrideCriticalTable in ddraw.ini
static CritInfo* critTable;
static CritInfo* playerCrit;
static bool Inited = false;
@@ -96,7 +74,7 @@ void ResetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DWORD element
}
static int CritTableLoad() {
CritStruct* defaultTable = (CritStruct*)_crit_succ_eff;
CritInfo* defaultTable = (CritInfo*)_crit_succ_eff;
if (mode == 1) {
dlogr("Setting up critical hit table using CriticalOverrides.ini (old fmt)", DL_CRITICALS);
if (GetFileAttributes(critTableFile.c_str()) == INVALID_FILE_ATTRIBUTES) return 1;
@@ -122,9 +100,9 @@ static int CritTableLoad() {
}
} else {
dlog("Setting up critical hit table using RP fixes", DL_CRITICALS);
memcpy(baseCritTable, defaultTable, 19 * 6 * 9 * sizeof(CritStruct));
//memset(&baseCritTable[6 * 9 * 19], 0, 19 * 6 * 9 * sizeof(CritStruct));
memcpy(&baseCritTable[6 * 9 * 38], (CritStruct*)_pc_crit_succ_eff, 6 * 9 * sizeof(CritStruct)); // PC crit table
memcpy(baseCritTable, defaultTable, 19 * 6 * 9 * sizeof(CritInfo));
//memset(&baseCritTable[6 * 9 * 19], 0, 19 * 6 * 9 * sizeof(CritInfo));
memcpy(&baseCritTable[6 * 9 * 38], (CritInfo*)_pc_crit_succ_eff, 6 * 9 * sizeof(CritInfo)); // PC crit table
if (mode == 3) {
dlogr(" and CriticalOverrides.ini (new fmt)", DL_CRITICALS);
@@ -161,14 +139,14 @@ static int CritTableLoad() {
static void CriticalTableOverride() {
dlogr("Initializing critical table override...", DL_INIT);
baseCritTable = new CritStruct[CritTableSize]();
critTable = new CritStruct[CritTableSize];
baseCritTable = new CritInfo[CritTableSize]();
critTable = new CritInfo[CritTableSize];
playerCrit = &critTable[6 * 9 * 38];
SafeWrite32(0x423F96, (DWORD)playerCrit);
SafeWrite32(0x423FB3, (DWORD)critTable);
if (mode == 2 || mode == 3) { // bug fixes
CritStruct* defaultTable = (CritStruct*)_crit_succ_eff;
CritInfo* defaultTable = (CritInfo*)_crit_succ_eff;
SetEntry(2, 4, 1, 4, 0);
SetEntry(2, 4, 1, 5, 5216);
@@ -298,5 +276,5 @@ void CriticalsInit() {
void CritLoad() {
if (!Inited) return;
memcpy(critTable, baseCritTable, CritTableSize * sizeof(CritStruct)); // Apply loaded critical table
memcpy(critTable, baseCritTable, CritTableSize * sizeof(CritInfo)); // Apply loaded critical table
}
+11 -5
View File
@@ -409,8 +409,8 @@ static void __declspec(naked) DisplayBonusHtHDmg1_hook() {
}
}
static const DWORD DisplayBonusHtHDmg2Exit = 0x472569;
static void __declspec(naked) DisplayBonusHtHDmg2_hack() {
static const DWORD DisplayBonusHtHDmg2Exit = 0x472569;
__asm {
mov ecx, eax;
call stat_level_;
@@ -451,10 +451,16 @@ void DamageModInit() {
if (BonusHtHDmgFix) {
dlog("Applying Bonus HtH Damage Perk fix.", DL_INIT);
if (!DisplayBonusDmg) { // Subtract damage from perk bonus (vanilla displaying)
HookCall(0x435C0C, MeleeDmgDisplayPrintFix_hook); // DisplayFix (ListDrvdStats_)
HookCall(0x439921, MeleeDmgDisplayPrintFix_hook); // PrintFix (Save_as_ASCII_)
HookCall(0x472266, CommonDmgRngDispFix_hook); // MeleeWeap (display_stats_)
HookCall(0x472546, CommonDmgRngDispFix_hook); // Unarmed (display_stats_)
const DWORD meleeDmgDispPrtAddr[] = {
0x435C0C, // DisplayFix (ListDrvdStats_)
0x439921 // PrintFix (Save_as_ASCII_)
};
HookCalls(MeleeDmgDisplayPrintFix_hook, meleeDmgDispPrtAddr);
const DWORD commonDmgRngDispAddr[] = {
0x472266, // MeleeWeap (display_stats_)
0x472546 // Unarmed (display_stats_)
};
HookCalls(CommonDmgRngDispFix_hook, commonDmgRngDispAddr);
}
MakeCall(0x478492, HtHDamageFix1a_hack); // Unarmed (item_w_damage_)
HookCall(0x47854C, HtHDamageFix1b_hook); // MeleeWeap (item_w_damage_)
+9 -3
View File
@@ -22,6 +22,7 @@
#include "FalloutEngine.h"
#include "Arrays.h"
#include "DebugEditor.h"
#include "Graphics.h"
#include "ScriptExtender.h"
enum DECode {
@@ -52,7 +53,7 @@ struct sArray {
};
static void DEGameWinRedraw() {
ProcessBk();
if (GraphicsMode != 0) ProcessBk();
}
static bool SetBlocking(SOCKET s, bool block) {
@@ -287,8 +288,8 @@ void RunDebugEditor() {
WSACleanup();
}
static const DWORD dbg_error_ret = 0x453FD8;
static void __declspec(naked) dbg_error_hack() {
static const DWORD dbg_error_ret = 0x453FD8;
__asm {
cmp ebx, 1;
je hide;
@@ -312,6 +313,11 @@ artNotExist:
push edx;
push artDbgMsg;
call debug_printf_;
cmp isDebug, 0;
jne display;
add esp, 8;
retn;
display:
push edx; // filename
push artDbgMsg;
lea eax, [esp + 0x124 - 0x124 + 20]; // buf
@@ -369,7 +375,7 @@ static void DebugModePatch() {
if (iniGetInt("Debugging", "HideObjIsNullMsg", 0, ddrawIniDef)) {
MakeJump(0x453FD2, dbg_error_hack);
}
// prints a debug message about missing art file for critters to both debug.log and the message window
// prints a debug message about missing art file for critters to both debug.log and the message window in sfall debugging mode
HookCall(0x419B65, art_data_size_hook);
// Fix to prevent crashes when there is a '%' character in the printed message
+10 -1
View File
@@ -246,7 +246,7 @@ enum Stat : long
};
#define STAT_max_derived STAT_poison_resist
#define STAT_max_stat STAT_current_hp
#define STAT_max_stat STAT_current_hp
// Script data types
#define VAR_TYPE_INT (0xC001)
@@ -288,6 +288,15 @@ enum Skill : long
SKILL_count = 18
};
enum AnimCommand : long
{
RB_UNRESERVED = 0x1,
RB_RESERVED = 0x2,
RB_DONTSTAND = 0x4,
RB_UNKNOWN = 0x100,
RB_END_ANIM = 0x200
};
enum CritterFlags : long
{
CFLG_Sneak = 0x01, // Can sneak ?
+19 -10
View File
@@ -114,13 +114,13 @@ static void __declspec(naked) GetNumButtonsHook3() {
}
}
void ResetElevators() {
static void ResetElevators() {
//memset(&elevators[vanillaElevatorCount], 0, sizeof(sElevator) * (elevatorCount - vanillaElevatorCount));
//memset(&elevatorsFrms[vanillaElevatorCount], 0, sizeof(sElevatorFrms) * (elevatorCount - vanillaElevatorCount));
//for (int i = vanillaElevatorCount; i < elevatorCount; i++) elevatorType[i] = 0;
}
void LoadElevators(const char* elevFile) {
static void LoadElevators(const char* elevFile) {
//ResetElevators();
memcpy(elevators, (void*)_retvals, sizeof(sElevator) * vanillaElevatorCount);
@@ -158,26 +158,35 @@ void LoadElevators(const char* elevFile) {
}
}
void ElevatorsInit() {
static void ElevatorsInit2() {
HookCall(0x43EF83, GetMenuHook);
HookCall(0x43F141, CheckHotKeysHook);
//HookCall(0x43F2D2, UnknownHook2); // unused
SafeWrite8(0x43EF76, (BYTE)elevatorCount);
SafeWrite32(0x43EFA4, (DWORD)elevators);
SafeWrite32(0x43EFB9, (DWORD)elevators);
SafeWrite32(0x43F2FC, (DWORD)elevators);
SafeWrite32(0x43EFEA, (DWORD)&elevators[0].Tile1);
SafeWrite32(0x43F315, (DWORD)&elevators[0].Tile1);
const DWORD elevatorsAddr[] = {0x43EFA4, 0x43EFB9, 0x43F2FC};
SafeWriteBatch<DWORD>((DWORD)elevators, elevatorsAddr);
const DWORD elevatorsTileAddr[] = {0x43EFEA, 0x43F315};
SafeWriteBatch<DWORD>((DWORD)&elevators[0].Tile1, elevatorsTileAddr);
SafeWrite32(0x43F309, (DWORD)&elevators[0].Elevation1);
SafeWrite32(0x43F438, (DWORD)&elevatorsFrms[0].main);
SafeWrite32(0x43F475, (DWORD)&elevatorsFrms[0].buttons);
// _btncnt
SafeWrite32(0x43F65E, (DWORD)elevatorsBtnCount);
SafeWrite32(0x43F6BB, (DWORD)elevatorsBtnCount);
const DWORD elevsBtnCountAddr[] = {0x43F65E, 0x43F6BB};
SafeWriteBatch<DWORD>((DWORD)elevatorsBtnCount, elevsBtnCountAddr);
MakeCall(0x43F05D, GetNumButtonsHook1, 2);
MakeCall(0x43F184, GetNumButtonsHook2, 2);
MakeCall(0x43F1E4, GetNumButtonsHook3, 2);
}
void ElevatorsInit() {
std::string elevPath = GetConfigString("Misc", "ElevatorsFile", "", MAX_PATH);
if (!elevPath.empty()) {
dlog("Applying elevator patch.", DL_INIT);
ElevatorsInit2();
LoadElevators(elevPath.insert(0, ".\\").c_str());
dlogr(" Done", DL_INIT);
}
}

Some files were not shown because too many files have changed in this diff Show More