mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
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:
+9
-9
@@ -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_<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 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_<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)
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <stdint.h>
|
||||
|
||||
#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<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
|
||||
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());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
||||
@@ -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<fo::GameObject*> &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);
|
||||
|
||||
+20
-20
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.)
|
||||
|
||||
+91
-16
@@ -26,13 +26,14 @@
|
||||
|
||||
namespace sfall
|
||||
{
|
||||
|
||||
using namespace fo;
|
||||
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<DWORD, DWORD> sources;
|
||||
static std::unordered_map<fo::GameObject*, fo::GameObject*> targets;
|
||||
static std::unordered_map<fo::GameObject*, fo::GameObject*> 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;
|
||||
}
|
||||
|
||||
+6
-2
@@ -16,6 +16,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
|
||||
@@ -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<DotPosition> 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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
Reference in New Issue
Block a user