mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3acf051b49 | ||
|
|
0137504f3f | ||
|
|
ebcd505561 | ||
|
|
24cb02b7d8 | ||
|
|
48ce358451 | ||
|
|
c293a826bc | ||
|
|
0da7c7b266 | ||
|
|
d74df7fc78 | ||
|
|
df816bcd47 | ||
|
|
068988caad | ||
|
|
24c9499021 | ||
|
|
7f3de8d19a | ||
|
|
87f7fe4a54 | ||
|
|
01ba18100e | ||
|
|
8b26573073 | ||
|
|
1839790dfb | ||
|
|
cb1d5d359f | ||
|
|
b781e117a2 | ||
|
|
a7d9716b68 | ||
|
|
cdd2bfe338 | ||
|
|
7decec4a2c | ||
|
|
e3af4f5754 | ||
|
|
1b61bca702 | ||
|
|
f0f00a77ea | ||
|
|
58d41487fd | ||
|
|
73ccba22bf | ||
|
|
1f8b3636f3 |
+10
-6
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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_)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user