diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 92eedfaa..a56dda1b 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -107,6 +107,11 @@ FadeMultiplier=100 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Interface] +;Set to 1 to expand the number of action points displayed on the interface bar +;Requires new IFACE_E.frm and HR_IFACE_E.frm files in art\intrface\ (included in sfall.dat) to display correctly +;The minimum supported version of High Resolution Patch is 4.1.8 +ActionPointsBar=0 + ;Set to 1 to use the expanded world map interface ;Set to 2 to skip correcting the position of entrance markers on town maps ;You can use resized FRMs in 700x682 for town maps in the expanded world map interface @@ -114,22 +119,17 @@ FadeMultiplier=100 ;The resolution of hi-res patch must be set to at least 890x720 ExpandWorldMap=0 -;Set to 1 to expand the number of action points displayed on the interface bar -;Requires new IFACE_E.frm and HR_IFACE_E.frm files in art\intrface\ (included in sfall.dat) to display correctly -;The minimum supported version of High Resolution Patch is 4.1.8 -ActionPointsBar=0 - ;Set to 1 to enable drawing a dotted line when traveling on the world map (similar to Fallout 1) WorldMapTravelMarkers=0 ;Uncomment these lines to change the appearance of the markers -;The color index in Fallout default palette (valid range: 1..255; default is 133) -;TravelMarkerColor=133 +;The color index in Fallout default palette (valid range: 1..255; default is 134) +;TravelMarkerColor=134 ;The lengh of the dots in pixels (valid range: 1..10) -;TravelMarkerLength=1 +;TravelMarkerLength=2 ;The spacing between the dots in pixels (valid range: 1..10) ;TravelMarkerSpaces=2 -;Set to 1 to display terrain types when moving the cursor over a green triangle on the world map +;Set to 1 to display terrain types when moving the cursor over the green triangle on the world map WorldMapTerrainInfo=0 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 72dbb80b..4760fe67 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -192,9 +192,15 @@ HOOK_FINDTARGET (hs_findtarget.int) Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. -The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries. +The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries, but this is fixed in sfall 4.2.3/3.8.23. Use sfall_return to give the 4 targets, in order of preference. If you want to specify less than 4 targets, fill in the extra spaces with 0's or pass -1 to skip the return value. +NOTE: The engine can choose targets by the following criteria: +1) The nearest enemy to the attacker. +2) The enemy that attacked the attacker. +3) The enemy that attacked an NPC from the same team as the attacker. +4) The enemy that is attacked by an NPC from the same team as the attacker. + critter arg1 - The attacker critter arg2 - A possible target critter arg3 - A possible target diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index d8283bde..a5df6f73 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -#include #include #include "Functions.h" @@ -33,11 +32,11 @@ namespace fo static MessageNode messageBuf; -const char* _stdcall GetMessageStr(const MessageList* fileAddr, long messageId) { +const char* GetMessageStr(const MessageList* fileAddr, long messageId) { return fo::func::getmsg(fileAddr, &messageBuf, messageId); } -const char* _stdcall MessageSearch(const MessageList* fileAddr, long messageId) { +const char* MessageSearch(const MessageList* fileAddr, long messageId) { messageBuf.number = messageId; if (fo::func::message_search(fileAddr, &messageBuf) == 1) { return messageBuf.message; @@ -212,8 +211,8 @@ void GetObjectsTileRadius(std::vector &objs, long sourceTile, l } } -// Returns the name of the terrain type in the position of the player's marker on the world map -const char* wmGetCurrentTerrainName() { +// Returns the type of the terrain sub tile at the the player's position on the world map +long wmGetCurrentTerrainType() { long* terrainId = *(long**)FO_VAR_world_subtile; if (terrainId == nullptr) { __asm { @@ -223,7 +222,12 @@ const char* wmGetCurrentTerrainName() { call fo::funcoffs::wmFindCurSubTileFromPos_; } } - return GetMessageStr(&fo::var::wmMsgFile, 1000 + *terrainId); + return *terrainId; +} + +// Returns the name of the terrain type at the the player's position on the world map +const char* wmGetCurrentTerrainName() { + return GetMessageStr(&fo::var::wmMsgFile, 1000 + wmGetCurrentTerrainType()); } //--------------------------------------------------------- diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 375bef6f..d4f77343 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -36,10 +36,10 @@ inline void DisplayPrint(const std::string& str) { } // returns message string from given file or "Error" when not found -const char* _stdcall GetMessageStr(const MessageList* fileAddr, long messageId); +const char* GetMessageStr(const MessageList* fileAddr, long messageId); // similar to GetMessageStr, but returns nullptr when no message is found -const char* _stdcall MessageSearch(const MessageList* fileAddr, long messageId); +const char* MessageSearch(const MessageList* fileAddr, long messageId); // returns pointer to prototype by PID, or nullptr on failure Proto* GetProto(long pid); @@ -80,6 +80,8 @@ long __fastcall GetTopWindowID(long xPos, long yPos); void GetObjectsTileRadius(std::vector &objs, long sourceTile, long radius, long elev, long type); +long wmGetCurrentTerrainType(); + const char* wmGetCurrentTerrainName(); void RectCopyToMemory(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromBuff, BYTE* toMem); diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index a7a3d298..72faf5dc 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -648,26 +648,26 @@ enum BodyType : long enum KillType : long { - men_type = 0, - women_type = 1, - children_type = 2, - super_mutant_type = 3, - ghoul_type = 4, - brahmin_type = 5, - radscorpion_type = 6, - rat_type = 7, - floater_type = 8, - centaur_type = 9, - robot_type = 10, - dog_type = 11, - manti_type = 12, - deathclaw_type = 13, - plant_type = 14, - gecko_type = 15, - alien_type = 16, - giant_ant_type = 17, - big_boss_type = 18, - count + KILL_TYPE_men = 0, + KILL_TYPE_women = 1, + KILL_TYPE_children = 2, + KILL_TYPE_super_mutant = 3, + KILL_TYPE_ghoul = 4, + KILL_TYPE_brahmin = 5, + KILL_TYPE_radscorpion = 6, + KILL_TYPE_rat = 7, + KILL_TYPE_floater = 8, + KILL_TYPE_centaur = 9, + KILL_TYPE_robot = 10, + KILL_TYPE_dog = 11, + KILL_TYPE_manti = 12, + KILL_TYPE_deathclaw = 13, + KILL_TYPE_plant = 14, + KILL_TYPE_gecko = 15, + KILL_TYPE_alien = 16, + KILL_TYPE_giant_ant = 17, + KILL_TYPE_big_bad_boss = 18, + KILL_TYPE_count }; #define PLAYER_ID (18000) diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index a077a1c2..075f82f8 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -32,7 +32,7 @@ void __declspec(naked) dev_printf(const char* fmt, ...) { __asm jmp fo::funcoffs::debug_printf_; } #else -void dev_printf(const char* fmt, ...) {} +void dev_printf(...) {} #endif // Fallout2.exe was compiled using WATCOM compiler, which uses Watcom register calling convention. diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index 4f4abc72..3e30f2b0 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -30,7 +30,11 @@ namespace func { // Prints debug message to debug.log file for develop build +#ifndef NDEBUG void dev_printf(const char* fmt, ...); +#else +void dev_printf(...); +#endif /* Add functions here if they have non-trivial wrapper implementation (like vararg functions or too many arguments, etc.) diff --git a/sfall/Modules/AI.cpp b/sfall/Modules/AI.cpp index 9e4f79c6..7f09defc 100644 --- a/sfall/Modules/AI.cpp +++ b/sfall/Modules/AI.cpp @@ -26,13 +26,14 @@ namespace sfall { + using namespace fo; using namespace Fields; -typedef std::unordered_map::const_iterator iter; +typedef std::unordered_map::const_iterator iter; -static std::unordered_map targets; -static std::unordered_map sources; +static std::unordered_map targets; +static std::unordered_map sources; static void __declspec(naked) ai_try_attack_hook_FleeFix() { __asm { @@ -85,11 +86,80 @@ static void __declspec(naked) ai_check_drugs_hook() { } } +static bool __fastcall TargetExistInList(fo::GameObject* target, fo::GameObject** targetList) { + char i = 4; + do { + if (*targetList == target) return true; + targetList++; + } while (--i); + return false; +} + +static void __declspec(naked) ai_find_attackers_hack_target2() { + __asm { + mov edi, [esp + 0x24 - 0x24 + 4] // critter (target) + pushadc; + lea edx, [ebp - 4]; // start list of targets + mov ecx, edi; + call TargetExistInList; + test al, al; + popadc; + jnz skip; + inc edx; + mov [ebp], edi; +skip: + retn; + } +} + +static void __declspec(naked) ai_find_attackers_hack_target3() { + __asm { + mov edi, [esp + 0x24 - 0x20 + 4] // critter (target) + push eax; + push edx; + mov eax, 4; // count targets + lea edx, [ebp - 4 * 2]; // start list of targets +continue: + cmp edi, [edx]; + je break; // target == targetList + lea edx, [edx + 4]; // next target in list + dec al; + jnz continue; +break: + test al, al; + pop edx; + pop eax; + jz skip; + xor edi, edi; + retn; +skip: + inc edx; + retn; + } +} + +static void __declspec(naked) ai_find_attackers_hack_target4() { + __asm { + mov eax, [ecx + eax]; // critter (target) + pushadc; + lea edx, [esi - 4 * 3]; // start list of targets + mov ecx, eax; + call TargetExistInList; + test al, al; + popadc; + jnz skip; + inc edx; + mov [esi], eax; +skip: + retn; + } +} + //////////////////////////////////////////////////////////////////////////////// -static DWORD RetryCombatLastAP; static DWORD RetryCombatMinAP; static void __declspec(naked) RetryCombatHook() { + static DWORD RetryCombatLastAP = 0; __asm { mov RetryCombatLastAP, 0; retry: @@ -116,13 +186,13 @@ end: //////////////////////////////////////////////////////////////////////////////// -static void __fastcall CombatAttackHook(DWORD source, DWORD target) { - sources[target] = source; - targets[source] = target; +static void __fastcall CombatAttackHook(fo::GameObject* source, fo::GameObject* target) { + sources[target] = source; // who attacked the 'target' from the last time + targets[source] = target; // who was attacked by the 'source' from the last time } static void __declspec(naked) combat_attack_hook() { - _asm { + __asm { push ecx; push edx; push eax; @@ -136,7 +206,7 @@ static void __declspec(naked) combat_attack_hook() { } static DWORD combatDisabled; -void _stdcall AIBlockCombat(DWORD i) { +void __stdcall AIBlockCombat(DWORD i) { combatDisabled = i ? 1 : 0; } @@ -183,10 +253,10 @@ end: } void AI::init() { - //HookCall(0x42AE1D, ai_attack_hook); - //HookCall(0x42AE5C, ai_attack_hook); - HookCall(0x426A95, combat_attack_hook); // combat_attack_this_ - HookCall(0x42A796, combat_attack_hook); // ai_attack_ + HookCalls(combat_attack_hook, { + 0x426A95, // combat_attack_this_ + 0x42A796 // ai_attack_ + }); MakeJump(0x45F6AF, BlockCombatHook1); // intface_use_item_ HookCall(0x4432A6, BlockCombatHook2); // game_handle_input_ @@ -211,14 +281,19 @@ void AI::init() { HookCalls(ai_try_attack_hook_FleeFix, {0x42ABA8, 0x42ACE5}); // 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); } -DWORD _stdcall AIGetLastAttacker(DWORD target) { +fo::GameObject* _stdcall AIGetLastAttacker(fo::GameObject* target) { iter itr = sources.find(target); - return (itr != sources.end()) ? itr->second: 0; + return (itr != sources.end()) ? itr->second : 0; } -DWORD _stdcall AIGetLastTarget(DWORD source) { +fo::GameObject* _stdcall AIGetLastTarget(fo::GameObject* source) { iter itr = targets.find(source); return (itr != targets.end()) ? itr->second : 0; } diff --git a/sfall/Modules/AI.h b/sfall/Modules/AI.h index bb452dff..d123f4ff 100644 --- a/sfall/Modules/AI.h +++ b/sfall/Modules/AI.h @@ -16,6 +16,10 @@ * along with this program. If not, see . */ +#pragma once + +#include "..\FalloutEngine\Fallout2.h" + #include "Module.h" namespace sfall @@ -31,8 +35,8 @@ public: void _stdcall AICombatStart(); void _stdcall AICombatEnd(); -DWORD _stdcall AIGetLastAttacker(DWORD target); -DWORD _stdcall AIGetLastTarget(DWORD source); +fo::GameObject* _stdcall AIGetLastAttacker(fo::GameObject* target); +fo::GameObject* _stdcall AIGetLastTarget(fo::GameObject* source); void _stdcall AIBlockCombat(DWORD i); diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index a168612a..1d33bfff 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -462,7 +462,7 @@ static void WorldmapViewportPatch() { dlogr(" Done", DL_INIT); } -///////////////////////// FALLOUT 1 WORLDMAP FEATURES ////////////////////////// +///////////////////////// FALLOUT 1 WORLD MAP FEATURES ///////////////////////// enum TerrainHoverImage { width = 100, @@ -482,12 +482,12 @@ struct DotPosition { }; static std::vector dots; -static long optionLenDot = 1; +static long optionLenDot = 2; static long optionSpaceDot = 2; static unsigned char colorDot = 0; static long spaceLen = 2; -static long dotLen = 1; +static long dotLen = 2; static long dot_xpos = 0; static long dot_ypos = 0; @@ -694,7 +694,7 @@ static void WorldMapInterfacePatch() { dlog("Applying world map travel markers patch.", DL_INIT); optionLenDot = GetConfigInt("Interface", "TravelMarkerLength", optionLenDot); optionSpaceDot = GetConfigInt("Interface", "TravelMarkerSpaces", optionSpaceDot); - int color = GetConfigInt("Interface", "TravelMarkerColor", 133); // color index in palette: R = 252, G = 0, B = 0 + int color = GetConfigInt("Interface", "TravelMarkerColor", 134); // color index in palette: R = 224, G = 0, B = 0 if (color > 255) color = 255; else if (color < 1) color = 1; colorDot = color; diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 6bafbc3c..97d0f981 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -90,7 +90,7 @@ static void __declspec(naked) action_use_skill_on_hook_science() { __asm { cmp esi, ds:[FO_VAR_obj_dude]; jne end; - mov eax, robot_type; // KillType + mov eax, KILL_TYPE_robot; retn; end: jmp fo::funcoffs::critter_kill_count_type_;