Fixed duplicate critters being added to the target list for AI

Edited hookscripts.txt to mention the fix.

Tweaked the default settings for world map travel markers.

Some code correction.
This commit is contained in:
NovaRain
2020-01-09 11:38:44 +08:00
parent af049eaa38
commit fece180f0b
11 changed files with 157 additions and 62 deletions
+9 -9
View File
@@ -107,6 +107,11 @@ FadeMultiplier=100
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[Interface] [Interface]
;Set to 1 to expand the number of action points displayed on the interface bar
;Requires new IFACE_E.frm and HR_IFACE_<res>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 1 to use the expanded world map interface
;Set to 2 to skip correcting the position of entrance markers on town maps ;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 ;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 ;The resolution of hi-res patch must be set to at least 890x720
ExpandWorldMap=0 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_<res>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) ;Set to 1 to enable drawing a dotted line when traveling on the world map (similar to Fallout 1)
WorldMapTravelMarkers=0 WorldMapTravelMarkers=0
;Uncomment these lines to change the appearance of the markers ;Uncomment these lines to change the appearance of the markers
;The color index in Fallout default palette (valid range: 1..255; default is 133) ;The color index in Fallout default palette (valid range: 1..255; default is 134)
;TravelMarkerColor=133 ;TravelMarkerColor=134
;The lengh of the dots in pixels (valid range: 1..10) ;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) ;The spacing between the dots in pixels (valid range: 1..10)
;TravelMarkerSpaces=2 ;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 WorldMapTerrainInfo=0
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+7 -1
View File
@@ -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. 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. 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. 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 arg1 - The attacker
critter arg2 - A possible target critter arg2 - A possible target
critter arg3 - A possible target critter arg3 - A possible target
+10 -6
View File
@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <stdint.h> #include <stdint.h>
#include "Functions.h" #include "Functions.h"
@@ -33,11 +32,11 @@ namespace fo
static MessageNode messageBuf; 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); 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; messageBuf.number = messageId;
if (fo::func::message_search(fileAddr, &messageBuf) == 1) { if (fo::func::message_search(fileAddr, &messageBuf) == 1) {
return messageBuf.message; return messageBuf.message;
@@ -212,8 +211,8 @@ void GetObjectsTileRadius(std::vector<fo::GameObject*> &objs, long sourceTile, l
} }
} }
// Returns the name of the terrain type in the position of the player's marker on the world map // Returns the type of the terrain sub tile at the the player's position on the world map
const char* wmGetCurrentTerrainName() { long wmGetCurrentTerrainType() {
long* terrainId = *(long**)FO_VAR_world_subtile; long* terrainId = *(long**)FO_VAR_world_subtile;
if (terrainId == nullptr) { if (terrainId == nullptr) {
__asm { __asm {
@@ -223,7 +222,12 @@ const char* wmGetCurrentTerrainName() {
call fo::funcoffs::wmFindCurSubTileFromPos_; 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());
} }
//--------------------------------------------------------- //---------------------------------------------------------
+4 -2
View File
@@ -36,10 +36,10 @@ inline void DisplayPrint(const std::string& str) {
} }
// returns message string from given file or "Error" when not found // 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 // 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 // returns pointer to prototype by PID, or nullptr on failure
Proto* GetProto(long pid); Proto* GetProto(long pid);
@@ -80,6 +80,8 @@ long __fastcall GetTopWindowID(long xPos, long yPos);
void GetObjectsTileRadius(std::vector<fo::GameObject*> &objs, long sourceTile, long radius, long elev, long type); void GetObjectsTileRadius(std::vector<fo::GameObject*> &objs, long sourceTile, long radius, long elev, long type);
long wmGetCurrentTerrainType();
const char* wmGetCurrentTerrainName(); const char* wmGetCurrentTerrainName();
void RectCopyToMemory(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromBuff, BYTE* toMem); void RectCopyToMemory(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromBuff, BYTE* toMem);
+20 -20
View File
@@ -648,26 +648,26 @@ enum BodyType : long
enum KillType : long enum KillType : long
{ {
men_type = 0, KILL_TYPE_men = 0,
women_type = 1, KILL_TYPE_women = 1,
children_type = 2, KILL_TYPE_children = 2,
super_mutant_type = 3, KILL_TYPE_super_mutant = 3,
ghoul_type = 4, KILL_TYPE_ghoul = 4,
brahmin_type = 5, KILL_TYPE_brahmin = 5,
radscorpion_type = 6, KILL_TYPE_radscorpion = 6,
rat_type = 7, KILL_TYPE_rat = 7,
floater_type = 8, KILL_TYPE_floater = 8,
centaur_type = 9, KILL_TYPE_centaur = 9,
robot_type = 10, KILL_TYPE_robot = 10,
dog_type = 11, KILL_TYPE_dog = 11,
manti_type = 12, KILL_TYPE_manti = 12,
deathclaw_type = 13, KILL_TYPE_deathclaw = 13,
plant_type = 14, KILL_TYPE_plant = 14,
gecko_type = 15, KILL_TYPE_gecko = 15,
alien_type = 16, KILL_TYPE_alien = 16,
giant_ant_type = 17, KILL_TYPE_giant_ant = 17,
big_boss_type = 18, KILL_TYPE_big_bad_boss = 18,
count KILL_TYPE_count
}; };
#define PLAYER_ID (18000) #define PLAYER_ID (18000)
+1 -1
View File
@@ -32,7 +32,7 @@ void __declspec(naked) dev_printf(const char* fmt, ...) {
__asm jmp fo::funcoffs::debug_printf_; __asm jmp fo::funcoffs::debug_printf_;
} }
#else #else
void dev_printf(const char* fmt, ...) {} void dev_printf(...) {}
#endif #endif
// Fallout2.exe was compiled using WATCOM compiler, which uses Watcom register calling convention. // Fallout2.exe was compiled using WATCOM compiler, which uses Watcom register calling convention.
+4
View File
@@ -30,7 +30,11 @@ namespace func
{ {
// Prints debug message to debug.log file for develop build // Prints debug message to debug.log file for develop build
#ifndef NDEBUG
void dev_printf(const char* fmt, ...); 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.) Add functions here if they have non-trivial wrapper implementation (like vararg functions or too many arguments, etc.)
+90 -15
View File
@@ -26,13 +26,14 @@
namespace sfall namespace sfall
{ {
using namespace fo; using namespace fo;
using namespace Fields; using namespace Fields;
typedef std::unordered_map<DWORD, DWORD>::const_iterator iter; typedef std::unordered_map<fo::GameObject*, fo::GameObject*>::const_iterator iter;
static std::unordered_map<DWORD, DWORD> targets; static std::unordered_map<fo::GameObject*, fo::GameObject*> targets;
static std::unordered_map<DWORD, DWORD> sources; static std::unordered_map<fo::GameObject*, fo::GameObject*> sources;
static void __declspec(naked) ai_try_attack_hook_FleeFix() { static void __declspec(naked) ai_try_attack_hook_FleeFix() {
__asm { __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 DWORD RetryCombatMinAP;
static void __declspec(naked) RetryCombatHook() { static void __declspec(naked) RetryCombatHook() {
static DWORD RetryCombatLastAP = 0;
__asm { __asm {
mov RetryCombatLastAP, 0; mov RetryCombatLastAP, 0;
retry: retry:
@@ -116,13 +186,13 @@ end:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static void __fastcall CombatAttackHook(DWORD source, DWORD target) { static void __fastcall CombatAttackHook(fo::GameObject* source, fo::GameObject* target) {
sources[target] = source; sources[target] = source; // who attacked the 'target' from the last time
targets[source] = target; targets[source] = target; // who was attacked by the 'source' from the last time
} }
static void __declspec(naked) combat_attack_hook() { static void __declspec(naked) combat_attack_hook() {
_asm { __asm {
push ecx; push ecx;
push edx; push edx;
push eax; push eax;
@@ -136,7 +206,7 @@ static void __declspec(naked) combat_attack_hook() {
} }
static DWORD combatDisabled; static DWORD combatDisabled;
void _stdcall AIBlockCombat(DWORD i) { void __stdcall AIBlockCombat(DWORD i) {
combatDisabled = i ? 1 : 0; combatDisabled = i ? 1 : 0;
} }
@@ -183,10 +253,10 @@ end:
} }
void AI::init() { void AI::init() {
//HookCall(0x42AE1D, ai_attack_hook); HookCalls(combat_attack_hook, {
//HookCall(0x42AE5C, ai_attack_hook); 0x426A95, // combat_attack_this_
HookCall(0x426A95, combat_attack_hook); // combat_attack_this_ 0x42A796 // ai_attack_
HookCall(0x42A796, combat_attack_hook); // ai_attack_ });
MakeJump(0x45F6AF, BlockCombatHook1); // intface_use_item_ MakeJump(0x45F6AF, BlockCombatHook1); // intface_use_item_
HookCall(0x4432A6, BlockCombatHook2); // game_handle_input_ HookCall(0x4432A6, BlockCombatHook2); // game_handle_input_
@@ -211,14 +281,19 @@ void AI::init() {
HookCalls(ai_try_attack_hook_FleeFix, {0x42ABA8, 0x42ACE5}); HookCalls(ai_try_attack_hook_FleeFix, {0x42ABA8, 0x42ACE5});
// Disable fleeing when NPC cannot move closer to target // Disable fleeing when NPC cannot move closer to target
BlockCall(0x42ADF6); // ai_try_attack_ 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); 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); iter itr = targets.find(source);
return (itr != targets.end()) ? itr->second : 0; return (itr != targets.end()) ? itr->second : 0;
} }
+6 -2
View File
@@ -16,6 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once
#include "..\FalloutEngine\Fallout2.h"
#include "Module.h" #include "Module.h"
namespace sfall namespace sfall
@@ -31,8 +35,8 @@ public:
void _stdcall AICombatStart(); void _stdcall AICombatStart();
void _stdcall AICombatEnd(); void _stdcall AICombatEnd();
DWORD _stdcall AIGetLastAttacker(DWORD target); fo::GameObject* _stdcall AIGetLastAttacker(fo::GameObject* target);
DWORD _stdcall AIGetLastTarget(DWORD source); fo::GameObject* _stdcall AIGetLastTarget(fo::GameObject* source);
void _stdcall AIBlockCombat(DWORD i); void _stdcall AIBlockCombat(DWORD i);
+4 -4
View File
@@ -462,7 +462,7 @@ static void WorldmapViewportPatch() {
dlogr(" Done", DL_INIT); dlogr(" Done", DL_INIT);
} }
///////////////////////// FALLOUT 1 WORLDMAP FEATURES ////////////////////////// ///////////////////////// FALLOUT 1 WORLD MAP FEATURES /////////////////////////
enum TerrainHoverImage { enum TerrainHoverImage {
width = 100, width = 100,
@@ -482,12 +482,12 @@ struct DotPosition {
}; };
static std::vector<DotPosition> dots; static std::vector<DotPosition> dots;
static long optionLenDot = 1; static long optionLenDot = 2;
static long optionSpaceDot = 2; static long optionSpaceDot = 2;
static unsigned char colorDot = 0; static unsigned char colorDot = 0;
static long spaceLen = 2; static long spaceLen = 2;
static long dotLen = 1; static long dotLen = 2;
static long dot_xpos = 0; static long dot_xpos = 0;
static long dot_ypos = 0; static long dot_ypos = 0;
@@ -694,7 +694,7 @@ static void WorldMapInterfacePatch() {
dlog("Applying world map travel markers patch.", DL_INIT); dlog("Applying world map travel markers patch.", DL_INIT);
optionLenDot = GetConfigInt("Interface", "TravelMarkerLength", optionLenDot); optionLenDot = GetConfigInt("Interface", "TravelMarkerLength", optionLenDot);
optionSpaceDot = GetConfigInt("Interface", "TravelMarkerSpaces", optionSpaceDot); 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; if (color > 255) color = 255; else if (color < 1) color = 1;
colorDot = color; colorDot = color;
+1 -1
View File
@@ -90,7 +90,7 @@ static void __declspec(naked) action_use_skill_on_hook_science() {
__asm { __asm {
cmp esi, ds:[FO_VAR_obj_dude]; cmp esi, ds:[FO_VAR_obj_dude];
jne end; jne end;
mov eax, robot_type; // KillType mov eax, KILL_TYPE_robot;
retn; retn;
end: end:
jmp fo::funcoffs::critter_kill_count_type_; jmp fo::funcoffs::critter_kill_count_type_;