Added ALTERNATE_AMMO_METRE and NumPathNodes options

This commit is contained in:
NovaRain
2021-12-11 09:19:00 +08:00
parent 26b3fac1e0
commit fee4720ef5
7 changed files with 375 additions and 20 deletions
+225
View File
@@ -4,6 +4,8 @@
* *
*/ */
#include <array>
#include "..\main.h" #include "..\main.h"
#include "..\FalloutEngine\Fallout2.h" #include "..\FalloutEngine\Fallout2.h"
@@ -184,9 +186,232 @@ static void __declspec(naked) tile_num_beyond_replacement() {
} }
} }
struct sfChild {
long tile;
long from_tile;
long distance;
WORD accumulator; // the higher the value, the less likely it is to use this tile to build a path
char rotation;
};
static std::vector<sfChild> m_pathData;
static std::vector<sfChild> m_dadData;
static std::array<BYTE, 5000> seenTile;
static long maxPathNodes = 2000;
static __forceinline fo::GameObject* CheckTileBlocking(void* blockFunc, long tile, fo::GameObject* object) {
using namespace fo::Fields;
__asm {
mov eax, object;
mov edx, tile;
mov ebx, [eax + elevation];
call blockFunc;
//mov object, eax;
}
//return object;
}
// 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 with added args
// type: 1 - tile path, 0 - rotation path
// maxNodes: limiting the building of a path
long __fastcall Tilemap::make_path_func(fo::GameObject* srcObject, long sourceTile, long targetTile, long type, long maxNodes, void* arrayRef, long checkTargetTile, void* blockFunc) {
if (checkTargetTile && fo::func::obj_blocking_at_wrapper(srcObject, targetTile, srcObject->elevation, blockFunc)) return 0;
bool inCombat = fo::var::combat_state & fo::CombatStateFlag::InCombat;
int8_t critterType = fo::KillType::KILL_TYPE_men;
bool isCritter = srcObject->IsCritter();
if (isCritter) {
critterType = static_cast<int8_t>(fo::func::critter_kill_count_type(srcObject));
}
seenTile.fill(0);
seenTile[sourceTile >> 3] = 1 << (sourceTile & 7);
auto& it = m_pathData.begin();
it->tile = sourceTile;
it->from_tile = -1;
it->distance = fo::func::tile_idistance(sourceTile, targetTile); // maximum distance
it->rotation = 0;
it->accumulator = 0;
while (++it != m_pathData.end()) it->tile = -1;
long targetX, targetY;
fo::func::tile_coord(targetTile, &targetX, &targetY);
auto& dadData = m_dadData.begin();
size_t pathCounter = 1;
sfChild childData;
long node = 0;
if (maxNodes > maxPathNodes) maxNodes = maxPathNodes;
// search path tiles
while (true) {
auto& pathData = m_pathData.begin();
if (pathCounter > 0) {
size_t counter = 0;
long prevDistance;
// search for the element with the smallest distance
for (auto currData = pathData; currData != m_pathData.end(); ++currData) {
if (currData->tile != -1) {
long currDistance = currData->distance + currData->accumulator;
if (counter == 0 || currDistance < prevDistance) {
prevDistance = currDistance;
pathData = currData;
}
if (++counter >= pathCounter) break;
}
}
}
childData = *pathData; // copy element data
pathData->tile = -1; // set a free element
pathCounter--;
if (childData.tile == targetTile) break; // path is built, exit the loop
*dadData++ = childData;
if (++node >= maxNodes) { //++dadData == m_dadData.end()
return 0; // path cannot be built, reached the end of the array (limit of the maximum path length)
}
char rotation = 0;
do {
long tile = fo::func::tile_num_in_direction(childData.tile, rotation, 1);
long seenIndex = tile >> 3;
BYTE seenMask = 1 << (tile & 7);
if (!(seenTile[seenIndex] & seenMask)) {
seenTile[seenIndex] |= seenMask;
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)
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) { // limit of the maximum path length?
return 0; // *** do not remove the breakpoint until this is cleared up ***
}
pathData = m_pathData.begin();
while (pathData->tile != -1) ++pathData; // search the first free element
pathData->tile = tile;
pathData->from_tile = childData.tile;
pathData->rotation = rotation;
pathData->accumulator = childData.accumulator + 50; // here childData.accumulator has the value
if (!inCombat && rotation != childData.rotation) pathData->accumulator += 10;
long x, y;
fo::func::tile_coord(tile, &x, &y);
pathData->distance = DistanceFromPositions(x, y, targetX, targetY);
if (isCritter) {
fo::GameObject* object = fo::func::obj_find_first_at_tile(srcObject->elevation, tile);
while (object) {
// TODO: add check for other PIDs
if (object->protoId >= fo::ProtoID::PID_RAD_GOO_1 && object->protoId <= fo::ProtoID::PID_RAD_GOO_4) {
pathData->accumulator += (critterType == fo::KillType::KILL_TYPE_gecko) ? 100 : 400;
break;
}
object = fo::func::obj_find_next_at_tile();
}
}
}
} while (++rotation < 6);
if (!pathCounter) return 0; // path cannot be built
}
BYTE* arrayR = reinterpret_cast<BYTE*>(arrayRef);
WORD* arrayT = reinterpret_cast<WORD*>(arrayRef);
size_t pathLen = 0;
// building and calculating the path length
do {
if (childData.tile == sourceTile) break; // reached the source tile
if (arrayRef) {
if (type) {
*arrayT++ = (WORD)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 (arrayRef && pathLen > 1) {
// reverse the array values
size_t count = pathLen >> 1;
if (type) {
WORD* arrayFront = reinterpret_cast<WORD*>(arrayRef);
do {
WORD last = *--arrayT;
*arrayT = *arrayFront; // last < front
*arrayFront++ = last;
} while (--count);
} else {
BYTE* arrayFront = reinterpret_cast<BYTE*>(arrayRef);
do {
BYTE last = *--arrayR;
*arrayR = *arrayFront; // last < front
*arrayFront++ = last;
} while (--count);
}
}
return pathLen;
}
static void __declspec(naked) make_path_func_replacement() {
__asm {
xchg [esp], ecx; // ret addr <> array
push maxPathNodes; // (sfall addition)
push 0; // type rotation (sfall addition)
push ebx; // target tile
push ecx; // ret addr
mov ecx, eax;
jmp Tilemap::make_path_func;
}
}
static bool replaced = false;
void Tilemap::SetPathMaxNodes(long maxNodes) {
maxPathNodes = maxNodes;
if (!replaced) {
replaced = true;
sf::MakeJump(fo::funcoffs::make_path_func_, make_path_func_replacement); // 0x415EFC
} else {
m_pathData.resize(maxNodes);
m_dadData.resize(maxNodes);
}
}
void Tilemap::init() { void Tilemap::init() {
// Replace tile_num_beyond_ function // Replace tile_num_beyond_ function
sf::MakeJump(fo::funcoffs::tile_num_beyond_ + 1, tile_num_beyond_replacement); // 0x4B1B84 sf::MakeJump(fo::funcoffs::tile_num_beyond_ + 1, tile_num_beyond_replacement); // 0x4B1B84
//m_pathData.reserve(40000);
//m_dadData.reserve(40000);
m_pathData.resize(maxPathNodes);
m_dadData.resize(maxPathNodes);
} }
} }
+4
View File
@@ -16,6 +16,10 @@ public:
static void obj_path_blocking_at_(); static void obj_path_blocking_at_();
static long __fastcall tile_num_beyond(long sourceTile, long targetTile, long maxRange); 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, long type, long maxNodes, void* arrayRef, long checkTargetTile, void* blockFunc);
static void SetPathMaxNodes(long maxNodes);
}; };
} }
+19 -8
View File
@@ -4,6 +4,8 @@
* *
*/ */
#pragma comment(lib, "psapi.lib")
#include <psapi.h> #include <psapi.h>
#include "..\main.h" #include "..\main.h"
@@ -13,9 +15,10 @@
#include "..\WinProc.h" #include "..\WinProc.h"
#include "..\Modules\Graphics.h" #include "..\Modules\Graphics.h"
#include "..\Modules\LoadOrder.h" #include "..\Modules\LoadOrder.h"
#include "..\Modules\SubModules\WindowRender.h" #include "..\Modules\SubModules\WindowRender.h"
#include "..\Game\tilemap.h"
#include "viewmap\ViewMap.h" #include "viewmap\ViewMap.h"
#include "SplashScreen.h" #include "SplashScreen.h"
#include "MainMenu.h" #include "MainMenu.h"
@@ -56,10 +59,7 @@ long Setting::ScreenHeight() { return SCR_HEIGHT; }
long Setting::ColorBits() { return COLOUR_BITS; } long Setting::ColorBits() { return COLOUR_BITS; }
static void GetHRPModule() { static void GetHRPModule() {
static const DWORD loadFunc = 0x4FE1D0; baseDLLAddr = (DWORD)GetModuleHandleA("f2_res.dll");
//HMODULE dll;
__asm call loadFunc; // get HRP loading address
__asm mov baseDLLAddr, eax;
sf::dlog_f("Loaded f2_res.dll library at the memory address: 0x%x\n", DL_MAIN, baseDLLAddr); sf::dlog_f("Loaded f2_res.dll library at the memory address: 0x%x\n", DL_MAIN, baseDLLAddr);
} }
@@ -141,7 +141,11 @@ static __declspec(naked) void combat_turn_run_hook() {
static __declspec(naked) void gmouse_bk_process() { static __declspec(naked) void gmouse_bk_process() {
__asm { __asm {
push edx;
push ecx;
call sfall::WinProc::WaitMessageWindow; call sfall::WinProc::WaitMessageWindow;
pop ecx;
pop edx;
jmp fo::funcoffs::gmouse_bk_process_; jmp fo::funcoffs::gmouse_bk_process_;
} }
} }
@@ -306,6 +310,10 @@ void Setting::init(const char* exeFileName, std::string &cmdline) {
IFaceBar::IFACE_BAR_WIDTH = sf::IniReader::GetInt("IFACE", "IFACE_BAR_WIDTH", (SCR_WIDTH >= 800) ? 800 : 640, f2ResIni); IFaceBar::IFACE_BAR_WIDTH = sf::IniReader::GetInt("IFACE", "IFACE_BAR_WIDTH", (SCR_WIDTH >= 800) ? 800 : 640, f2ResIni);
IFaceBar::IFACE_BAR_SIDES_ORI = (sf::IniReader::GetInt("IFACE", "IFACE_BAR_SIDES_ORI", 0, f2ResIni) != 0); IFaceBar::IFACE_BAR_SIDES_ORI = (sf::IniReader::GetInt("IFACE", "IFACE_BAR_SIDES_ORI", 0, f2ResIni) != 0);
IFaceBar::ALTERNATE_AMMO_METRE = sf::IniReader::GetInt("IFACE", "ALTERNATE_AMMO_METRE", 0, f2ResIni);
IFaceBar::ALTERNATE_AMMO_LIGHT = (BYTE)sf::IniReader::GetInt("IFACE", "ALTERNATE_AMMO_LIGHT", 196, f2ResIni);
IFaceBar::ALTERNATE_AMMO_DARK = (BYTE)sf::IniReader::GetInt("IFACE", "ALTERNATE_AMMO_DARK", 75, f2ResIni);
Dialog::DIALOG_SCRN_ART_FIX = (sf::IniReader::GetInt("OTHER_SETTINGS", "DIALOG_SCRN_ART_FIX", 1, f2ResIni) != 0); Dialog::DIALOG_SCRN_ART_FIX = (sf::IniReader::GetInt("OTHER_SETTINGS", "DIALOG_SCRN_ART_FIX", 1, f2ResIni) != 0);
Dialog::DIALOG_SCRN_BACKGROUND = (sf::IniReader::GetInt("OTHER_SETTINGS", "DIALOG_SCRN_BACKGROUND", 0, f2ResIni) != 0); Dialog::DIALOG_SCRN_BACKGROUND = (sf::IniReader::GetInt("OTHER_SETTINGS", "DIALOG_SCRN_BACKGROUND", 0, f2ResIni) != 0);
@@ -313,6 +321,9 @@ void Setting::init(const char* exeFileName, std::string &cmdline) {
sf::WindowRender::EnableRecalculateFadeSteps(); sf::WindowRender::EnableRecalculateFadeSteps();
} }
int nodes = sf::IniReader::GetInt("MAPS", "NumPathNodes", 1, f2ResIni);
if (nodes > 1) game::Tilemap::SetPathMaxNodes((nodes < 20) ? nodes * 2000 : 40000);
if (sf::IniReader::GetInt("OTHER_SETTINGS", "BARTER_PC_INV_DROP_FIX", 1, f2ResIni)) { if (sf::IniReader::GetInt("OTHER_SETTINGS", "BARTER_PC_INV_DROP_FIX", 1, f2ResIni)) {
// barter_move_from_table_inventory_ // barter_move_from_table_inventory_
if (fo::var::getInt(0x47523D) == 80) sf::SafeWrite32(0x47523D, 100); // x_start if (fo::var::getInt(0x47523D) == 80) sf::SafeWrite32(0x47523D, 100); // x_start
@@ -330,9 +341,9 @@ void Setting::init(const char* exeFileName, std::string &cmdline) {
sf::HookCall(0x4C73B1, fadeSystemPalette_hook); sf::HookCall(0x4C73B1, fadeSystemPalette_hook);
sf::HookCall(0x4227E5, combat_turn_run_hook); sf::HookCall(0x4227E5, combat_turn_run_hook);
sf::HookCalls(gmouse_bk_process, { sf::HookCalls(gmouse_bk_process, {
0x460EB1, 0x460EFF, 0x460F55, 0x460FAE, 0x46101E, 0x4610FA, // intface_rotate_numbers_ 0x460EB0, 0x460EFE, 0x460F54, 0x460FAD, 0x46101D, 0x4610F9, // intface_rotate_numbers_
0x45FA4E, // intface_end_window_open_ 0x45FA4D, // intface_end_window_open_
0x45FBA7, // intface_end_window_close_ 0x45FBA6, // intface_end_window_close_
}); });
} }
+116 -2
View File
@@ -22,7 +22,13 @@ long IFaceBar::IFACE_BAR_MODE; // 1 - the bottom of the map view window extends
long IFaceBar::IFACE_BAR_SIDE_ART; long IFaceBar::IFACE_BAR_SIDE_ART;
long IFaceBar::IFACE_BAR_WIDTH; long IFaceBar::IFACE_BAR_WIDTH;
bool IFaceBar::IFACE_BAR_SIDES_ORI; // 1 - Iface-bar side graphics extend from the Screen edges to the Iface-Bar bool IFaceBar::IFACE_BAR_SIDES_ORI; // 1 - Iface-bar side graphics extend from the Screen edges to the Iface-Bar
// 1 - Single colour, the colours used can be set below with the ALTERNATE_AMMO_LIGHT and ALTERNATE_AMMO_DARK options
// 2 - Changes colour depending how much ammo remains in your current weapon
// 3 - Divides the metre into several differently coloured sections
long IFaceBar::ALTERNATE_AMMO_METRE; long IFaceBar::ALTERNATE_AMMO_METRE;
BYTE IFaceBar::ALTERNATE_AMMO_LIGHT;
BYTE IFaceBar::ALTERNATE_AMMO_DARK;
static long xPosition; static long xPosition;
static long yPosition; static long yPosition;
@@ -361,6 +367,112 @@ static void __declspec(naked) intface_draw_ammo_lights_hack() {
} }
} }
///////////////////////////// Alternate Ammo Metre /////////////////////////////
static void GetAmmoMetreColors(long yPercent, BYTE &outClr1, BYTE &outClr2) {
if (IFaceBar::ALTERNATE_AMMO_METRE == 1) {
outClr1 = IFaceBar::ALTERNATE_AMMO_LIGHT;
outClr2 = IFaceBar::ALTERNATE_AMMO_DARK;
return;
}
if (yPercent < 15) { // 20%
outClr1 = 136;
outClr2 = 181;
} else if (yPercent < 29) { // 40%
outClr1 = 132;
outClr2 = 140;
} else if (yPercent < 43) { // 60%
outClr1 = 145;
outClr2 = 154;
} else if (yPercent < 57) { // 80%
outClr1 = 58;
outClr2 = 66;
} else {
outClr1 = 215;
outClr2 = 75;
}
}
static void __fastcall DrawAlternateAmmoMetre(long x, long y) {
fo::Window* win = fo::func::GNW_find(fo::var::interfaceWindow);
x += xOffset - 2;
long startOffset = x + (25 * win->width);
BYTE* surface = win->surface + startOffset;
*(DWORD*)surface = 0x0F0F0F0F;
surface[4] = 15;
surface += win->width;
if (y < 70) {
// empty
long count = ((69 - y) / 2) + 1;
do {
surface[0] = 11;
surface[1] = 13;
surface[2] = 13;
surface[3] = 13;
surface[4] = 15;
surface += win->width;
surface[0] = 11;
surface[1] = 15;
surface[2] = 15;
surface[3] = 15;
surface[4] = 15;
surface += win->width;
} while (--count);
}
if (y > 0) {
BYTE lColor, dColor;
GetAmmoMetreColors(y, lColor, dColor);
do {
surface[0] = 11;
surface[1] = lColor;
surface[2] = lColor;
surface[3] = lColor;
surface[4] = dColor;
surface += win->width;
surface[0] = 11;
surface[1] = dColor;
surface[2] = dColor;
surface[3] = dColor;
surface[4] = dColor;
surface += win->width;
y -= 2;
if (IFaceBar::ALTERNATE_AMMO_METRE == 3) GetAmmoMetreColors(y, lColor, dColor);
} while (y > 0);
}
*(DWORD*)surface = 0x0A0A0A0A;
surface[4] = 10;
fo::BoundRect rect;
rect.x = x;
rect.y = 26;
rect.offx = x + 3;
rect.offy = 26 + 70;
fo::func::win_draw_rect(fo::var::interfaceWindow, (RECT*)&rect);
}
static void __declspec(naked) intface_update_ammo_lights_hook() {
__asm {
push ecx;
mov ecx, eax;
call DrawAlternateAmmoMetre;
pop ecx;
retn;
}
}
////////////////////////////////////////////////////////////////////////////////
void IFaceBar::Hide() { void IFaceBar::Hide() {
InterfaceHide(fo::var::getInt(FO_VAR_interfaceWindow)); InterfaceHide(fo::var::getInt(FO_VAR_interfaceWindow));
} }
@@ -445,8 +557,10 @@ void IFaceBar::init() {
0x45E9FD // intface_hide_ 0x45E9FD // intface_hide_
}); });
// if (ALTERNATE_AMMO_METRE > 0) { if (ALTERNATE_AMMO_METRE) {
// } if (ALTERNATE_AMMO_METRE < 0 || ALTERNATE_AMMO_METRE > 3) ALTERNATE_AMMO_METRE = 1;
sf::HookCall(0x45F954, intface_update_ammo_lights_hook); // replace intface_draw_ammo_lights_
}
if (IFACE_BAR_MODE > 0) { if (IFACE_BAR_MODE > 0) {
// Set view map height to game resolution // Set view map height to game resolution
+3
View File
@@ -17,7 +17,10 @@ public:
static long IFACE_BAR_SIDE_ART; static long IFACE_BAR_SIDE_ART;
static long IFACE_BAR_WIDTH; static long IFACE_BAR_WIDTH;
static bool IFACE_BAR_SIDES_ORI; static bool IFACE_BAR_SIDES_ORI;
static long ALTERNATE_AMMO_METRE; static long ALTERNATE_AMMO_METRE;
static BYTE ALTERNATE_AMMO_LIGHT;
static BYTE ALTERNATE_AMMO_DARK;
static long display_width; static long display_width;
static char* display_string_buf; static char* display_string_buf;
+3 -3
View File
@@ -1171,7 +1171,7 @@ void Graphics::init() {
int gMode = IniReader::GetConfigInt("Graphics", "Mode", 4); int gMode = IniReader::GetConfigInt("Graphics", "Mode", 4);
if (gMode >= 4) Graphics::mode = gMode; if (gMode >= 4) Graphics::mode = gMode;
if (Graphics::mode < 0 && Graphics::mode > 6) { if (Graphics::mode < 0 || Graphics::mode > 6) {
Graphics::mode = 0; Graphics::mode = 0;
} }
IsWindowedMode = (mode == 2 || mode == 3 || mode == 5 || mode == 6); IsWindowedMode = (mode == 2 || mode == 3 || mode == 5 || mode == 6);
@@ -1180,11 +1180,11 @@ void Graphics::init() {
if (Graphics::mode >= 4) { if (Graphics::mode >= 4) {
dlog("Applying DX9 graphics patch.", DL_INIT); dlog("Applying DX9 graphics patch.", DL_INIT);
#define _DLL_NAME "d3dx9_43.dll" #define _DLL_NAME "d3dx9_43.dll"
HMODULE h = LoadLibraryEx(_DLL_NAME, 0, LOAD_LIBRARY_AS_DATAFILE); HMODULE h = LoadLibraryExA(_DLL_NAME, 0, LOAD_LIBRARY_AS_DATAFILE);
if (!h) { if (!h) {
dlogr(" Failed", DL_INIT); dlogr(" Failed", DL_INIT);
MessageBoxA(0, "You have selected DirectX graphics mode, but " _DLL_NAME " is missing.\n" MessageBoxA(0, "You have selected DirectX graphics mode, but " _DLL_NAME " is missing.\n"
"Switch back to DirectDraw mode, or install an up to date version of DirectX 9.0c.", 0, MB_TASKMODAL | MB_ICONERROR); "Switch back to DirectDraw (Mode=0), or install an up to date version of DirectX 9.0c.", 0, MB_TASKMODAL | MB_ICONERROR);
#undef _DLL_NAME #undef _DLL_NAME
ExitProcess(-1); ExitProcess(-1);
} }
+5 -7
View File
@@ -16,8 +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/>.
*/ */
#pragma comment(lib, "psapi.lib")
#include "main.h" #include "main.h"
#include "FalloutEngine\Fallout2.h" #include "FalloutEngine\Fallout2.h"
@@ -212,17 +210,17 @@ static void SfallInit() {
LoggingInit(); LoggingInit();
// enabling debugging features
isDebug = (IniReader::GetIntDefaultConfig("Debugging", "Enable", 0) != 0);
if (!ddraw.dll) dlog("Error: Cannot load the original ddraw.dll library.\n");
if (!HRP::Setting::CheckExternalPatch()) { if (!HRP::Setting::CheckExternalPatch()) {
WinProc::init(); WinProc::init();
} else { } else {
ShowCursor(0); ShowCursor(0);
} }
// enabling debugging features
isDebug = (IniReader::GetIntDefaultConfig("Debugging", "Enable", 0) != 0);
if (!ddraw.dll) dlog("Error: Cannot load the original ddraw.dll library.\n");
if (!isDebug || !IniReader::GetIntDefaultConfig("Debugging", "SkipCompatModeCheck", 0)) { if (!isDebug || !IniReader::GetIntDefaultConfig("Debugging", "SkipCompatModeCheck", 0)) {
int is64bit; int is64bit;
typedef int (__stdcall *chk64bitproc)(HANDLE, int*); typedef int (__stdcall *chk64bitproc)(HANDLE, int*);