Skip to content

Commit

Permalink
Improved implementation of the clearing move path algorithm for AI.
Browse files Browse the repository at this point in the history
Added an additional argument to the Tilemap::make_path_func() function to build an array path with tile numbers.
  • Loading branch information
FakelsHub committed Oct 13, 2021
1 parent ce33690 commit af35f04
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 178 deletions.
1 change: 1 addition & 0 deletions sfall/FalloutEngine/Functions_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ WRAP_WATCOM_FFUNC3(void, check_for_death, fo::GameObject*, critter, long, amount
WRAP_WATCOM_FFUNC4(long, combat_check_bad_shot, fo::GameObject*, source, fo::GameObject*, target, long, hitMode, long, isSecondary)
WRAP_WATCOM_FFUNC5(bool, combat_is_shot_blocked, fo::GameObject*, source, DWORD, tileSource, DWORD, tileTarget, fo::GameObject*, target, long*, accumulator)
WRAP_WATCOM_FFUNC6(long, combat_safety_invalidate_weapon_func, fo::GameObject*, source, fo::GameObject*, weapon, long, hitMode, fo::GameObject*, targetA, DWORD*, outSafeRange, fo::GameObject*, targetB)
WRAP_WATCOM_FFUNC4(long, combatai_msg, fo::GameObject*, source, fo::ComputeAttackResult*, ctd, long, action, long, delay)
WRAP_WATCOM_FFUNC3(void, correctFidForRemovedItem, fo::GameObject*, critter, fo::GameObject*, item, long, slotFlag)
WRAP_WATCOM_FFUNC7(long, createWindow, const char*, winName, DWORD, x, DWORD, y, DWORD, width, DWORD, height, long, color, long, flags)
WRAP_WATCOM_FFUNC4(long, determine_to_hit, fo::GameObject*, source, fo::GameObject*, target, long, bodyPart, long, hitMode)
Expand Down
325 changes: 202 additions & 123 deletions sfall/Game/ImprovedAI/AI.Behavior.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions sfall/Game/ImprovedAI/AI.Behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AIBehavior {
};

static long __fastcall AICheckBeforeWeaponSwitch(fo::GameObject* target, long &hitMode, fo::GameObject* source, fo::GameObject* weapon);
static long __fastcall AIMoveStepsCloser(long flags, fo::GameObject* target, fo::GameObject* source, long distance);
};

constexpr int pickupCostAP = 3; // engine default cost
Expand Down
40 changes: 20 additions & 20 deletions sfall/Game/ImprovedAI/AI.Combat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ static void CombatAI_Extended(fo::GameObject* source, fo::GameObject* target) {
{
case AIBehavior::AttackResult::TargetDead:
DEV_PRINTF("\n[AI] Attack result: TARGET DEAD!\n");
if (target == fo::var::obj_dude) return;
if (target->protoId == fo::PID_Player) return;
findTargetAfterKill = true;
goto ReFindNewTarget; // поиск новой цели, текущая была убита

Expand Down Expand Up @@ -1015,24 +1015,24 @@ static void __declspec(naked) combat_attack_hook() {
*/
/////////////////////////////////////////////////////////////////////////////////////////

#ifndef NDEBUG
static void __declspec(naked) ai_move_steps_closer_debug() {
static const char* move_steps_closer_fail = "\nERROR: ai_move_steps_closer.";
__asm {
test eax, eax;
jns skip;
push move_steps_closer_fail;
call fo::funcoffs::debug_printf_;
add esp, 4;
skip:
add esp, 0x10;
pop ebp;
pop edi;
pop esi;
retn;
}
}
#endif
//#ifndef NDEBUG
//static void __declspec(naked) ai_move_steps_closer_debug() {
// static const char* move_steps_closer_fail = "\nERROR: ai_move_steps_closer.";
// __asm {
// test eax, eax;
// jns skip;
// push move_steps_closer_fail;
// call fo::funcoffs::debug_printf_;
// add esp, 4;
//skip:
// add esp, 0x10;
// pop ebp;
// pop edi;
// pop esi;
// retn;
// }
//}
//#endif

void AICombat::init(bool smartBehavior) {

Expand All @@ -1056,7 +1056,7 @@ void AICombat::init(bool smartBehavior) {

#ifndef NDEBUG
combatDebug = (sf::IniReader::GetConfigInt("CombatAI", "Debug", 0) != 0);
sf::MakeJump(0x42A1B6, ai_move_steps_closer_debug);
//sf::MakeJump(0x42A1B6, ai_move_steps_closer_debug);
#endif
}
}
Expand Down
24 changes: 13 additions & 11 deletions sfall/Game/ImprovedAI/AI.FuncHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@

#include "..\..\FalloutEngine\Fallout2.h"
#include "..\..\Utils.h"
//#include "..\..\Modules\AI.h"

#include "..\AI\AIHelpers.h"

#include "..\items.h"

#include "AI.Behavior.h"
#include "AI.FuncHelpers.h"

namespace game
Expand Down Expand Up @@ -68,13 +67,13 @@ long AIHelpersExt::CombatMoveToTile(fo::GameObject* source, long tile, long dist
}

long AIHelpersExt::ForceMoveToTarget(fo::GameObject* source, fo::GameObject* target, long dist) {
dist |= 0x02000000; // sfall force flag (stay and stay_close)
return fo::func::ai_move_steps_closer(source, target, ~dist, 0); // 0 - ok, -1 - don't move
// sfall force flag (stay and stay_close)
return AIBehavior::AIMoveStepsCloser(0x02000000, target, source, dist); // 0 - ok, -1 - don't move
}

long AIHelpersExt::MoveToTarget(fo::GameObject* source, fo::GameObject* target, long dist) {
dist |= 0x01000000; // sfall force flag (stay_close)
return fo::func::ai_move_steps_closer(source, target, ~dist, 0); // 0 - ok, -1 - don't move
// sfall force flag (stay_close)
return AIBehavior::AIMoveStepsCloser(0x01000000, target, source, dist); // 0 - ok, -1 - don't move
}

fo::GameObject* AIHelpersExt::AICheckWeaponSkill(fo::GameObject* source, fo::GameObject* hWeapon, fo::GameObject* sWeapon) {
Expand Down Expand Up @@ -286,7 +285,7 @@ bool AIHelpersExt::CanSeeObject(fo::GameObject* source, fo::GameObject* target)
// return fo::func::can_see(source, target) && AIHelpers::CanSeeObject(source, target);
//}

static fo::GameObject* __fastcall obj_ai_move_blocking_at(fo::GameObject* source, long tile, long elev) {
fo::GameObject* __fastcall AIHelpersExt::obj_ai_move_blocking_at(fo::GameObject* source, long tile, long elev) {
if (tile < 0 || tile >= 40000) return nullptr;

fo::ObjectTable* obj = fo::var::objectTable[tile];
Expand All @@ -297,8 +296,9 @@ static fo::GameObject* __fastcall obj_ai_move_blocking_at(fo::GameObject* source
long flags = object->flags;
// не установлены флаги Mouse_3d, NoBlock и WalkThru
if (!(flags & fo::ObjectFlag::Mouse_3d) && !(flags & fo::ObjectFlag::NoBlock) && !(flags & fo::ObjectFlag::WalkThru) && source != object) {
if (object->TypeFid() == fo::ObjType::OBJ_TYPE_CRITTER) fo::var::moveBlockObj = object;
return object;
if (object->TypeFid() != fo::ObjType::OBJ_TYPE_CRITTER || object->critter.teamNum != source->critter.teamNum) {
return object;
};
}
}
obj = obj->nextObject;
Expand All @@ -315,14 +315,16 @@ static fo::GameObject* __fastcall obj_ai_move_blocking_at(fo::GameObject* source
fo::GameObject* object = obj->object;
long flags = object->flags;
if (flags & fo::ObjectFlag::MultiHex && !(flags & fo::ObjectFlag::Mouse_3d) && !(flags & fo::ObjectFlag::NoBlock) && !(flags & fo::ObjectFlag::WalkThru) && source != object) {
if (object->TypeFid() == fo::ObjType::OBJ_TYPE_CRITTER) fo::var::moveBlockObj = object;
return object;
if (object->TypeFid() != fo::ObjType::OBJ_TYPE_CRITTER || object->critter.teamNum != source->critter.teamNum) {
return object;
};
}
}
obj = obj->nextObject;
}
}
} while (++direction < 6);

return nullptr;
}

Expand Down
2 changes: 2 additions & 0 deletions sfall/Game/ImprovedAI/AI.FuncHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class AIHelpersExt {

static bool CanSeeObject(fo::GameObject* source, fo::GameObject* target);

static fo::GameObject* __fastcall obj_ai_move_blocking_at(fo::GameObject* source, long tile, long elev);
// wrapper
static void obj_ai_move_blocking_at_();
};

Expand Down
66 changes: 43 additions & 23 deletions sfall/Game/tilemap.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* sfall
* Copyright (C) 2008 - 2021 Timeslip and sfall team
*
Expand Down Expand Up @@ -180,7 +180,7 @@ long __fastcall Tilemap::tile_num_beyond(long sourceTile, long targetTile, long
}
}

static void __declspec(naked) tile_num_beyond_hack() {
static void __declspec(naked) tile_num_beyond_replacement() {
__asm { //push ecx;
push ebx;
mov ecx, eax;
Expand Down Expand Up @@ -214,16 +214,17 @@ static __forceinline fo::GameObject* CheckTileBlocking(void* blockFunc, long til
//return object;
}

// idist_
// same as idist_
static __inline long DistanceFromPositions(long sX, long sY, long tX, long tY) {
long diffX = std::abs(tX - sX);
long diffY = std::abs(tY - sY);
long minDiff = (diffX <= diffY) ? diffX : diffY;
return (diffX + diffY) - (minDiff >> 1);
}

// optimized version
long __fastcall Tilemap::make_path_func(fo::GameObject* srcObject, long sourceTile, long targetTile, uint8_t* arrayRotation, long checkTargetTile, void* blockFunc) {
// optimized version with added 'type' arg
// type: 1 - tile path, 0 - rotation path
long __fastcall Tilemap::make_path_func(fo::GameObject* srcObject, long sourceTile, long targetTile, long type, void* arrayRef, long checkTargetTile, void* blockFunc) {

if (checkTargetTile && fo::func::obj_blocking_at_wrapper(srcObject, targetTile, srcObject->elevation, blockFunc)) return 0;

Expand Down Expand Up @@ -281,7 +282,9 @@ long __fastcall Tilemap::make_path_func(fo::GameObject* srcObject, long sourceTi
if (childData.tile == targetTile) break; // exit the loop path is built

*dadData = childData;
if (++dadData == m_dadData.end()) return 0; // path can't be built reached the end of the array (limit the maximum path length)
if (++dadData == m_dadData.end()) {
return 0; // path can't be built reached the end of the array (limit the maximum path length)
}

char rotation = 0;
do {
Expand All @@ -295,17 +298,16 @@ long __fastcall Tilemap::make_path_func(fo::GameObject* srcObject, long sourceTi
if (tile != targetTile) {
fo::GameObject* objBlock = CheckTileBlocking(blockFunc, tile, srcObject);
if (objBlock) {
// Fix for building the path to the central hex of a multihex object (from BugFixes.cpp)
// [FIX] Fixes building the path to the central hex of a multihex object (from BugFixes.cpp)
if (objBlock->tile != targetTile) {
if (!fo::func::anim_can_use_door(srcObject, objBlock)) continue; // block - next rotation
} else {
targetTile = tile; // replace the target tile (where the multihex object is located) with the current tile
}
}
}
if (++pathCounter >= 2000) {
//BREAKPOINT
return 0; // îãðàíè÷åíèå ìàêñèìàëüíîé äëèíû ïóòè?
if (++pathCounter >= 2000) { // ограничение максимальной длины пути?
return 0;
}

pathData = m_pathData.begin();
Expand Down Expand Up @@ -338,32 +340,50 @@ long __fastcall Tilemap::make_path_func(fo::GameObject* srcObject, long sourceTi
if (!pathCounter) return 0; // the path can't be built
}

uint8_t* arrayR = reinterpret_cast<uint8_t*>(arrayRef);
uint16_t* arrayT = reinterpret_cast<uint16_t*>(arrayRef);
size_t pathLen = 0;
uint8_t* array = arrayRotation;

// building and calculating the path length
do {
if (childData.tile == sourceTile) break; // reached the source tile
if (array) *array++ = childData.rotation;

if (arrayRef) {
if (type) {
*arrayT++ = (uint16_t)childData.tile;
} else {
*arrayR++ = childData.rotation;
}
}
while (childData.from_tile != dadData->tile) --dadData; // search a linked tile 'from -> tile'
childData = *dadData;
} while (++pathLen < 800);

if (arrayRotation && pathLen > 1) {
if (arrayRef && pathLen > 1) {
// reverse the array values
size_t count = pathLen >> 1;
do {
uint8_t last = *--array;
*array = *arrayRotation; // last < front
*arrayRotation++ = last;
} while (--count);
if (type) {
uint16_t* arrayFront = reinterpret_cast<uint16_t*>(arrayRef);;
do {
uint16_t last = *--arrayT;
*arrayT = *arrayFront; // last < front
*arrayFront++ = last;
} while (--count);
} else {
uint8_t* arrayFront = reinterpret_cast<uint8_t*>(arrayRef);;
do {
uint8_t last = *--arrayR;
*arrayR = *arrayFront; // last < front
*arrayFront++ = last;
} while (--count);
}
}
return pathLen;
}

//static void __declspec(naked) make_path_func_hack() {
//static void __declspec(naked) make_path_func_replacement() {
// __asm {
// xchg [esp], ecx; // ret addr <> array
// push 0; // type
// push ebx; // target tile
// push ecx; // ret addr
// mov ecx, eax;
Expand All @@ -372,10 +392,10 @@ long __fastcall Tilemap::make_path_func(fo::GameObject* srcObject, long sourceTi
//}

void Tilemap::init() {
sf::MakeJump(fo::funcoffs::tile_num_beyond_ + 1, tile_num_beyond_hack); // 0x4B1B84
sf::MakeJump(fo::funcoffs::tile_num_beyond_ + 1, tile_num_beyond_replacement); // 0x4B1B84

// test
//sf::MakeJump(fo::funcoffs::make_path_func_, make_path_func_hack); // 0x415EFC
// for test in game
//sf::MakeJump(fo::funcoffs::make_path_func_, make_path_func_replacement); // 0x415EFC
}

}
2 changes: 1 addition & 1 deletion sfall/Game/tilemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Tilemap {

static long __fastcall tile_num_beyond(long sourceTile, long targetTile, long maxRange);

static long __fastcall make_path_func(fo::GameObject* srcObject, long sourceTile, long targetTile, uint8_t* arrayRotation, long checkTargetTile, void* blockFunc);
static long __fastcall make_path_func(fo::GameObject* srcObject, long sourceTile, long targetTile, long type, void* arrayRef, long checkTargetTile, void* blockFunc);
};

}

0 comments on commit af35f04

Please sign in to comment.