mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Merge branch 'develop'
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
;This file handles modifications to perks and traits
|
;This file handles modifications to perks and traits
|
||||||
;When adding extra perks to a mod, it's advisable to use
|
|
||||||
; the fake perk scripting functions rather than make modifications
|
;When adding extra perks to a mod, it's advisable to use the fake perk scripting functions rather than make modifications
|
||||||
; in this file, as some perks have hardcoded effects which may
|
; in this file, as some perks have hardcoded effects which may catch you out
|
||||||
; catch you out
|
|
||||||
;Since traits need to be picked before any scripts run, they
|
;Since traits need to be picked before any scripts run, they have an additional NoHardcode option in this file which can
|
||||||
; have an additional NoHardcode option in this file which can
|
|
||||||
; be used to remove their hardcoded effects, and add new stat/skill effects
|
; be used to remove their hardcoded effects, and add new stat/skill effects
|
||||||
|
|
||||||
[PerksTweak]
|
[PerksTweak]
|
||||||
@@ -31,41 +30,35 @@ WeaponHandlingBonus=3
|
|||||||
;Set to 1 to enable the modifications for perks
|
;Set to 1 to enable the modifications for perks
|
||||||
Enable=0
|
Enable=0
|
||||||
|
|
||||||
;Name=The name of the perk (max 63 characters)
|
;Name: The name of the perk (max 63 characters)
|
||||||
;Desc=The description of the perk (max 255 characters)
|
;Desc: The description of the perk (max 255 characters)
|
||||||
;Image=The line number (0-indexed) of the corresponding FRM in skilldex.lst
|
;Image: The line number (0-indexed) of the corresponding FRM in skilldex.lst
|
||||||
;Ranks=The number of perk levels
|
;Ranks: The number of perk levels
|
||||||
;Level=The minimum required level
|
;Level: The minimum required level
|
||||||
;Type=If 0, the perk checks only Skill1 for skill requirements; if 1, it checks
|
;Type: If 0, the perk checks only Skill1 for skill requirements; if 1, it checks Skill1 or Skill2; if 2, it checks both Skill1 and Skill2
|
||||||
; Skill1 or Skill2; if 2, it checks both Skill1 and Skill2
|
;Stat: The modified stat or -1 if you don't want to change any stats
|
||||||
;Stat=The modified stat or -1 if you don't want to change any stats
|
;StatMag: The increased/decreased value to the modified stat
|
||||||
;StatMag=The increased/decreased value to the modified stat
|
|
||||||
;Skill1=The first skill (or -1 for none) to check, but if you set a specific bit
|
;Skill1/Skill2: The first/second skill (or -1 for none) to check, but if you set a specific bit 0x4000000 (67108864), it checks GVAR.
|
||||||
; (67108864 or 0x4000000), it checks GVAR. For example, if Skill1=67108864, then
|
; For example, if Skill1=0x4000004, then the perk will check GVAR_BAD_MONSTER (0x4000000 + 4) on Skill1Mag value
|
||||||
; the perk will check GVAR_PLAYER_REPUTATION (67108864 + 0) on Skill1Mag value
|
|
||||||
;Skill1Mag=Positive value is used as the minimum requirements of the first
|
;Skill1Mag/Skill2Mag: Positive value is used as the minimum requirements of the first/second skill/GVAR, and a negative value is interpreted as the maximum requirement.
|
||||||
; skill/GVAR, and a negative value is interpreted as the maximum requirement.
|
; For example, with Skill1=0 and Skill1Mag=100, if player has small guns skill >= 100 and meets all other requirements, the perk will be available in the
|
||||||
; For example, with Skill1=0 and Skill1Mag=100, if player has small guns skill
|
; selection window; with Skill1=1 and Skill1Mag=-120, the perk will not be available if player has big guns skill >= 120
|
||||||
; >= 100 and meets all other requirements, the perk will be available in the
|
|
||||||
; selection window; with Skill1=1 and Skill1Mag=-120, the perk will not be
|
;STR-LCK: Positive values are used as minimum requirements of SPECIAL stats, and negative values are interpreted as maximum requirements.
|
||||||
; available if player has big guns skill >= 120
|
; For some special perks processed by the engine (e.g. PERK_armor_powered and PERK_armor_combat), the values are not used as requirements
|
||||||
;Skill2=The second skill/GVAR (or -1 for none) to check
|
; but to add to corresponding SPECIAL stats
|
||||||
;Skill2Mag=Same as Skill1Mag, but for Skill2
|
|
||||||
;STR-LCK=Positive values are used as minimum requirements of SPECIAL stats, and
|
|
||||||
; negative values are interpreted as maximum requirements. For some special
|
|
||||||
; perks processed by the engine (e.g. PERK_armor_powered and PERK_armor_combat),
|
|
||||||
; the values are not used as requirements but to add to corresponding SPECIAL
|
|
||||||
; stats
|
|
||||||
|
|
||||||
;If the value is set to -99999, the variable will be ignored (similar to comment out that line)
|
;If the value is set to -99999, the variable will be ignored (similar to comment out that line)
|
||||||
|
|
||||||
;You can add simple extra perks with ID numbers from 119 to 255
|
;You can add simple extra perks with ID numbers from 119 to 255
|
||||||
; that do not require using scripting functions to change player stats, and can also be used for Armor perks
|
; that do not require using scripting functions to change player stats, and can also be used for Armor perks
|
||||||
;These parameters are only for extra perks
|
;These parameters are only for extra perks
|
||||||
;Stat1-Stat2 = same as Stat
|
;Stat1/Stat2: same as Stat
|
||||||
;Stat1Mag-Stat2Mag = same as StatMag
|
;Stat1Mag/Stat2Mag: same as StatMag
|
||||||
;Skill3-Skill5 = The skill to be modified (or -1 for none)
|
;Skill3-Skill5: The skill to be modified (or -1 for none)
|
||||||
;Skill3Mod-Skill5Mod = The increased/decreased value to the modified skill
|
;Skill3Mod-Skill5Mod: The increased/decreased value to the modified skill
|
||||||
|
|
||||||
;##############################################################################
|
;##############################################################################
|
||||||
;This is a modification to vanilla perk 0
|
;This is a modification to vanilla perk 0
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
;sfall configuration settings
|
;sfall configuration settings
|
||||||
;v4.2.5
|
;v4.2.6
|
||||||
|
|
||||||
[Main]
|
[Main]
|
||||||
;Change to 1 if you want to use command line args to tell sfall to use another ini file.
|
;Change to 1 if you want to use command line args to tell sfall to use another ini file.
|
||||||
@@ -21,7 +21,7 @@ UseCommandLine=0
|
|||||||
;Path to the folder in which the game will automatically search and load custom files (by *.dat mask, including folders)
|
;Path to the folder in which the game will automatically search and load custom files (by *.dat mask, including folders)
|
||||||
;The data load order of the files in the folder will be in reverse alphabetical order of the filenames
|
;The data load order of the files in the folder will be in reverse alphabetical order of the filenames
|
||||||
;The files placed in this folder will have higher priority than the PatchFileXX options
|
;The files placed in this folder will have higher priority than the PatchFileXX options
|
||||||
;Uncomment the option to specify a different folder path. The default path is <GameRoot>\mods\
|
;This option has no effect in v4.2.6 or later. The default path is <GameRoot>\mods\
|
||||||
;AutoSearchPath=mods
|
;AutoSearchPath=mods
|
||||||
|
|
||||||
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
|
|||||||
Binary file not shown.
@@ -1,14 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Auto Doors mod v1.1 for Fallout 2 by Mr.Stalin
|
Auto Doors mod v1.2 for Fallout 2 by Mr.Stalin
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
- allows the player to automatically open/walk through unlocked doors when not in combat
|
- allows the player to automatically open/walk through unlocked doors when not in combat
|
||||||
|
|
||||||
Requires sfall 3.7b or higher
|
Requires sfall 3.7b or higher
|
||||||
|
|
||||||
NOTE: this script requires compiler from sfall modderspack with -s option
|
NOTE: this script requires compiler from sfall modderspack with -s option (short circuit evaluation)
|
||||||
(short circuit evaluation)
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -30,8 +29,8 @@ procedure combatturn_handler;
|
|||||||
#define PORTAL (0)
|
#define PORTAL (0)
|
||||||
#define DOOR_FLAGS (0x24)
|
#define DOOR_FLAGS (0x24)
|
||||||
|
|
||||||
variable only_once := 0;
|
variable onlyOnce := 0;
|
||||||
variable arrayPid;
|
variable pidDoors;
|
||||||
|
|
||||||
procedure start begin
|
procedure start begin
|
||||||
if game_loaded then begin
|
if game_loaded then begin
|
||||||
@@ -41,11 +40,11 @@ procedure start begin
|
|||||||
set_global_script_repeat(40); // for sfall 3.x
|
set_global_script_repeat(40); // for sfall 3.x
|
||||||
call map_enter_p_proc;
|
call map_enter_p_proc;
|
||||||
end else begin
|
end else begin
|
||||||
if (only_once == 1 and combat_is_initialized) then begin
|
if (onlyOnce == 1 and combat_is_initialized) then begin
|
||||||
only_once := 2;
|
onlyOnce := 2;
|
||||||
call set_door_flag(0); // reset flag when entering combat mode
|
call set_door_flag(0); // reset flag when entering combat mode
|
||||||
end else if (only_once == 2 and not(combat_is_initialized)) then begin
|
end else if (onlyOnce == 2 and not(combat_is_initialized)) then begin
|
||||||
only_once := 1;
|
onlyOnce := 1;
|
||||||
if (sfall_ver_major >= 4) then set_global_script_repeat(0);
|
if (sfall_ver_major >= 4) then set_global_script_repeat(0);
|
||||||
call set_door_flag(FLAG_WALKTHRU); // set flag after combat mode ends
|
call set_door_flag(FLAG_WALKTHRU); // set flag after combat mode ends
|
||||||
end
|
end
|
||||||
@@ -53,34 +52,35 @@ procedure start begin
|
|||||||
end
|
end
|
||||||
|
|
||||||
procedure map_enter_p_proc begin
|
procedure map_enter_p_proc begin
|
||||||
if (arrayPid) then begin
|
if (pidDoors) then begin
|
||||||
// when changing maps, delete the current array to create a new one
|
// when changing the map, delete the current array to create a new one
|
||||||
free_array(arrayPid);
|
free_array(pidDoors);
|
||||||
arrayPid := 0;
|
pidDoors := 0;
|
||||||
end
|
end
|
||||||
only_once := 1;
|
onlyOnce := 1;
|
||||||
call set_door_flag(FLAG_WALKTHRU); // set flag when entering the map
|
call set_door_flag(FLAG_WALKTHRU); // set flag when entering the map
|
||||||
end
|
end
|
||||||
|
|
||||||
procedure set_door_flag(variable state) begin
|
procedure set_door_flag(variable state) begin
|
||||||
variable objectMap, obj, objPid, i;
|
variable objectMap, obj, objPid, i;
|
||||||
|
|
||||||
if (arrayPid) then begin
|
if (pidDoors) then begin
|
||||||
call set_door_flag_array(state);
|
call set_door_flag_array(state);
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
objectMap := list_as_array(LIST_SCENERY);
|
objectMap := list_as_array(LIST_SCENERY);
|
||||||
arrayPid := create_array(0, 0);
|
pidDoors := create_array(0, 0);
|
||||||
|
|
||||||
foreach (obj in objectMap) begin
|
foreach (obj in objectMap) begin
|
||||||
objPid := obj_pid(obj);
|
objPid := obj_pid(obj);
|
||||||
|
|
||||||
if (proto_data(objPid, sc_type) != PORTAL) or is_in_array(objPid, arrayPid) then
|
if (proto_data(objPid, sc_type) != PORTAL or get_proto_data(objPid, DOOR_FLAGS) or is_in_array(objPid, pidDoors)) then begin
|
||||||
continue; // next object
|
continue; // next object
|
||||||
|
end
|
||||||
|
|
||||||
resize_array(arrayPid, i + 1);
|
resize_array(pidDoors, i + 1);
|
||||||
arrayPid[i] := objPid;
|
pidDoors[i] := objPid; // add pid to array
|
||||||
i++;
|
i++;
|
||||||
set_proto_data(objPid, DOOR_FLAGS, state);
|
set_proto_data(objPid, DOOR_FLAGS, state);
|
||||||
end
|
end
|
||||||
@@ -88,14 +88,14 @@ end
|
|||||||
|
|
||||||
procedure set_door_flag_array(variable state) begin
|
procedure set_door_flag_array(variable state) begin
|
||||||
variable objPid;
|
variable objPid;
|
||||||
foreach (objPid in arrayPid) begin
|
foreach (objPid in pidDoors) begin
|
||||||
set_proto_data(objPid, DOOR_FLAGS, state);
|
set_proto_data(objPid, DOOR_FLAGS, state);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
procedure combatturn_handler begin
|
procedure combatturn_handler begin
|
||||||
if (only_once == 1) then begin
|
if (onlyOnce == 1) then begin
|
||||||
only_once := 2;
|
onlyOnce := 2;
|
||||||
call set_door_flag_array(0);
|
call set_door_flag_array(0);
|
||||||
set_global_script_repeat(60);
|
set_global_script_repeat(60);
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -272,6 +272,11 @@
|
|||||||
use_obj_on_obj(item, dude_obj); \
|
use_obj_on_obj(item, dude_obj); \
|
||||||
set_self(0)
|
set_self(0)
|
||||||
|
|
||||||
|
// returns the corrected tile distance between two objects to the distance variable (return value >= 9996 is an error when getting the distance)
|
||||||
|
#define distance_objs(distance, obj1, obj2) distance := tile_distance_objs(obj1, obj2) - 1; \
|
||||||
|
if (get_flags(obj1) bwand FLAG_MULTIHEX) distance--; \
|
||||||
|
if (get_flags(obj2) bwand FLAG_MULTIHEX) distance--
|
||||||
|
|
||||||
// sfall_funcX macros
|
// sfall_funcX macros
|
||||||
#define add_extra_msg_file(name) sfall_func1("add_extra_msg_file", name)
|
#define add_extra_msg_file(name) sfall_func1("add_extra_msg_file", name)
|
||||||
#define add_global_timer_event(time, fixedParam) sfall_func2("add_g_timer_event", time, fixedParam)
|
#define add_global_timer_event(time, fixedParam) sfall_func2("add_g_timer_event", time, fixedParam)
|
||||||
@@ -304,6 +309,10 @@
|
|||||||
#define get_object_ai_data(obj, aiParam) sfall_func2("get_object_ai_data", obj, aiParam)
|
#define get_object_ai_data(obj, aiParam) sfall_func2("get_object_ai_data", obj, aiParam)
|
||||||
#define get_object_data(obj, offset) sfall_func2("get_object_data", obj, offset)
|
#define get_object_data(obj, offset) sfall_func2("get_object_data", obj, offset)
|
||||||
#define get_outline(obj) sfall_func1("get_outline", obj)
|
#define get_outline(obj) sfall_func1("get_outline", obj)
|
||||||
|
#define get_pc_stat_max(stat) sfall_func1("get_stat_max", stat)
|
||||||
|
#define get_pc_stat_min(stat) sfall_func1("get_stat_min", stat)
|
||||||
|
#define get_npc_stat_max(stat) sfall_func2("get_stat_max", stat, 1)
|
||||||
|
#define get_npc_stat_min(stat) sfall_func2("get_stat_mix", stat, 1)
|
||||||
#define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum)
|
#define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum)
|
||||||
#define get_string_pointer(text) sfall_func1("get_string_pointer", text)
|
#define get_string_pointer(text) sfall_func1("get_string_pointer", text)
|
||||||
#define get_text_width(text) sfall_func1("get_text_width", text)
|
#define get_text_width(text) sfall_func1("get_text_width", text)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ The read_xxx functions take a memory address as the parameter and can read arbit
|
|||||||
|
|
||||||
The get/set_pc_base/extra_stat functions are equivalent to calling get/set_critter_base/extra_stat with dude_obj as the critter pointer. None of these stat functions take perks into account, and neither do they do range clamping to make sure the stats are valid. Use the normal get_critter_stat function to get a correctly perk adjusted and range clamped value for a stat.
|
The get/set_pc_base/extra_stat functions are equivalent to calling get/set_critter_base/extra_stat with dude_obj as the critter pointer. None of these stat functions take perks into account, and neither do they do range clamping to make sure the stats are valid. Use the normal get_critter_stat function to get a correctly perk adjusted and range clamped value for a stat.
|
||||||
|
|
||||||
The set_stat_max/min functions can be used to set the valid ranges on on stats. Values returned by get_current_stat will be clamped to this range. The set_pc_ function only effect the player, the set_npc_ functions only effect other critters, and the set_ functions effect both.
|
The set_stat_max/min functions can be used to set the valid ranges on stats. Values returned by get_current_stat will be clamped to this range. The set_pc_ function only effect the player, the set_npc_ functions only effect other critters, and the set_ functions effect both.
|
||||||
|
|
||||||
The input functions are only available if the user has the input hook turned on in ddraw.ini. Use input_funcs_available to check.
|
The input functions are only available if the user has the input hook turned on in ddraw.ini. Use input_funcs_available to check.
|
||||||
|
|
||||||
@@ -480,8 +480,8 @@ Some utility/math functions are available:
|
|||||||
- sets the current cursor mode
|
- sets the current cursor mode
|
||||||
|
|
||||||
> void sfall_func0("display_stats")
|
> void sfall_func0("display_stats")
|
||||||
- displays player stats in the inventory screen display window
|
- updates player's stats in the inventory display window or on the character screen
|
||||||
- works only in opened inventory
|
- NOTE: works only when the interface window is opened
|
||||||
|
|
||||||
> int sfall_func1("lock_is_jammed", object)
|
> int sfall_func1("lock_is_jammed", object)
|
||||||
- returns 1 if the lock (container or scenery) is currently jammed, 0 otherwise
|
- returns 1 if the lock (container or scenery) is currently jammed, 0 otherwise
|
||||||
@@ -730,6 +730,13 @@ optional arguments:
|
|||||||
- flags: mode flags (see MSGBOX_* constants in define_extra.h). Pass -1 to skip setting the flags (default flags are NORMAL and YESNO)
|
- flags: mode flags (see MSGBOX_* constants in define_extra.h). Pass -1 to skip setting the flags (default flags are NORMAL and YESNO)
|
||||||
- color1/2: the color index in Fallout palette. color1 sets the text color for the first line, and color2 for all subsequent lines of text (default color is 145)
|
- color1/2: the color index in Fallout palette. color1 sets the text color for the first line, and color2 for all subsequent lines of text (default color is 145)
|
||||||
|
|
||||||
|
> int sfall_func1("get_stat_min", stat)
|
||||||
|
> int sfall_func1("get_stat_max", stat)
|
||||||
|
> int sfall_func2("get_stat_min", stat, bool who)
|
||||||
|
> int sfall_func2("get_stat_max", stat, bool who)
|
||||||
|
- returns the maximum or minimum set value of the specified stat (see set_stat_max/min functions)
|
||||||
|
- who: 0 (false) or omitting the argument - returns the value of the player, 1 (true) - returns the value set for other critters
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
------ MORE INFO -------
|
------ MORE INFO -------
|
||||||
------------------------
|
------------------------
|
||||||
|
|||||||
+10
-4
@@ -9,10 +9,14 @@ namespace sfall
|
|||||||
|
|
||||||
std::multimap<long, long> writeAddress;
|
std::multimap<long, long> writeAddress;
|
||||||
|
|
||||||
static std::vector<long> excludeAddr = {
|
static std::vector<long> excludeWarning = {
|
||||||
0x44E949, 0x44E94A, 0x44E937, 0x4F5F40, 0x4CB850, // from movies.cpp
|
0x44E949, 0x44E94A, 0x44E937, 0x4F5F40, 0x4CB850, // from movies.cpp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static std::vector<long> excludeConflict = {
|
||||||
|
0x42A0F8, // bugfixes.cpp
|
||||||
|
};
|
||||||
|
|
||||||
struct HackPair {
|
struct HackPair {
|
||||||
long addr;
|
long addr;
|
||||||
long len;
|
long len;
|
||||||
@@ -135,8 +139,10 @@ void PrintAddrList() {
|
|||||||
if (diff == 0) {
|
if (diff == 0) {
|
||||||
dlog_f("0x%x L:%d [Overwriting]\n", DL_MAIN, el.addr, el.len);
|
dlog_f("0x%x L:%d [Overwriting]\n", DL_MAIN, el.addr, el.len);
|
||||||
} else if (diff < pl) {
|
} else if (diff < pl) {
|
||||||
dlog_f("0x%x L:%d [Conflict] with 0x%x L:%d\n", DL_MAIN, el.addr, el.len, pa, pl);
|
if (std::find(excludeConflict.cbegin(), excludeConflict.cend(), el.addr) == excludeConflict.cend()) {
|
||||||
MessageBoxA(0, "Conflict detected!", "", MB_TASKMODAL);
|
dlog_f("0x%x L:%d [Conflict] with 0x%x L:%d\n", DL_MAIN, el.addr, el.len, pa, pl);
|
||||||
|
MessageBoxA(0, "Conflict detected!", "", MB_TASKMODAL);
|
||||||
|
}
|
||||||
} else if (level >= 11 && diff == pl) {
|
} else if (level >= 11 && diff == pl) {
|
||||||
dlog_f("0x%x L:%d [Warning] Hacking near:0x%x\n", DL_MAIN, el.addr, el.len, pa);
|
dlog_f("0x%x L:%d [Warning] Hacking near:0x%x\n", DL_MAIN, el.addr, el.len, pa);
|
||||||
} else if (level >= 12) {
|
} else if (level >= 12) {
|
||||||
@@ -149,7 +155,7 @@ void PrintAddrList() {
|
|||||||
|
|
||||||
void CheckConflict(DWORD addr, long len) {
|
void CheckConflict(DWORD addr, long len) {
|
||||||
if (writeAddress.find(addr) != writeAddress.cend()) {
|
if (writeAddress.find(addr) != writeAddress.cend()) {
|
||||||
if (std::find(excludeAddr.cbegin(), excludeAddr.cend(), addr) != excludeAddr.cend()) return;
|
if (std::find(excludeWarning.cbegin(), excludeWarning.cend(), addr) != excludeWarning.cend()) return;
|
||||||
char buf[64];
|
char buf[64];
|
||||||
sprintf_s(buf, "Memory overwriting at address 0x%x", addr);
|
sprintf_s(buf, "Memory overwriting at address 0x%x", addr);
|
||||||
MessageBoxA(0, buf, "", MB_TASKMODAL);
|
MessageBoxA(0, buf, "", MB_TASKMODAL);
|
||||||
|
|||||||
@@ -246,14 +246,25 @@ fo::Window* GetWindow(long winType) {
|
|||||||
winID = fo::var::optnwin;
|
winID = fo::var::optnwin;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return (fo::Window*)-1;
|
return (fo::Window*)(-1);
|
||||||
}
|
}
|
||||||
return (winID > 0) ? fo::func::GNW_find(winID) : nullptr;
|
return (winID > 0) ? fo::func::GNW_find(winID) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long GetRangeTileNumbers(long sourceTile, long radius, long &outEnd) {
|
||||||
|
long hexRadius = 200 * (radius + 1);
|
||||||
|
|
||||||
|
outEnd = sourceTile + hexRadius;
|
||||||
|
if (outEnd > 40000) outEnd = 40000;
|
||||||
|
|
||||||
|
long startTile = sourceTile - hexRadius;
|
||||||
|
return (startTile < 0) ? 0 : startTile;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns an array of objects within the specified radius from the source tile
|
// Returns an array of objects within the specified radius from the source tile
|
||||||
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) {
|
||||||
for (long tile = 0; tile < 40000; tile++) {
|
long endTile;
|
||||||
|
for (long tile = GetRangeTileNumbers(sourceTile, radius, endTile); tile < endTile; tile++) {
|
||||||
fo::GameObject* obj = fo::func::obj_find_first_at_tile(elev, tile);
|
fo::GameObject* obj = fo::func::obj_find_first_at_tile(elev, tile);
|
||||||
while (obj) {
|
while (obj) {
|
||||||
if (type == -1 || type == obj->Type()) {
|
if (type == -1 || type == obj->Type()) {
|
||||||
@@ -267,6 +278,44 @@ void GetObjectsTileRadius(std::vector<fo::GameObject*> &objs, long sourceTile, l
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks the blocking tiles and returns the first blocking object
|
||||||
|
fo::GameObject* CheckAroundBlockingTiles(fo::GameObject* source, long dstTile) {
|
||||||
|
long rotation = 5;
|
||||||
|
do {
|
||||||
|
long chkTile = fo::func::tile_num_in_direction(dstTile, rotation, 1);
|
||||||
|
fo::GameObject* obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
|
||||||
|
if (obj) return obj;
|
||||||
|
} while (--rotation >= 0);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fo::GameObject* __fastcall MultiHexMoveIsBlocking(fo::GameObject* source, long dstTile) {
|
||||||
|
if (fo::func::tile_dist(source->tile, dstTile) > 1) {
|
||||||
|
return CheckAroundBlockingTiles(source, dstTile);
|
||||||
|
}
|
||||||
|
// Checks the blocking arc of adjacent tiles
|
||||||
|
long dir = fo::func::tile_dir(source->tile, dstTile);
|
||||||
|
|
||||||
|
long chkTile = fo::func::tile_num_in_direction(dstTile, dir, 1);
|
||||||
|
fo::GameObject* obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
|
||||||
|
if (obj) return obj;
|
||||||
|
|
||||||
|
// +1 direction
|
||||||
|
long rotation = (dir + 1) % 6;
|
||||||
|
chkTile = fo::func::tile_num_in_direction(dstTile, rotation, 1);
|
||||||
|
obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
|
||||||
|
if (obj) return obj;
|
||||||
|
|
||||||
|
// -1 direction
|
||||||
|
rotation = (dir + 5) % 6;
|
||||||
|
chkTile = fo::func::tile_num_in_direction(dstTile, rotation, 1);
|
||||||
|
obj = fo::func::obj_blocking_at(source, chkTile, source->elevation);
|
||||||
|
if (obj) return obj;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the type of the terrain sub tile at the the player's position on the world map
|
// Returns the type of the terrain sub tile at the the player's position on the world map
|
||||||
long wmGetCurrentTerrainType() {
|
long wmGetCurrentTerrainType() {
|
||||||
long* terrainId = *(long**)FO_VAR_world_subtile;
|
long* terrainId = *(long**)FO_VAR_world_subtile;
|
||||||
|
|||||||
@@ -85,8 +85,14 @@ long __fastcall GetTopWindowID(long xPos, long yPos);
|
|||||||
|
|
||||||
fo::Window* GetWindow(long winType);
|
fo::Window* GetWindow(long winType);
|
||||||
|
|
||||||
|
// Returns an array of objects within the specified radius from the source tile
|
||||||
void GetObjectsTileRadius(std::vector<fo::GameObject*> &objs, long sourceTile, long radius, long elev, long type = -1);
|
void GetObjectsTileRadius(std::vector<fo::GameObject*> &objs, long sourceTile, long radius, long elev, long type = -1);
|
||||||
|
|
||||||
|
// Checks the blocking tiles and returns the first blocking object
|
||||||
|
fo::GameObject* CheckAroundBlockingTiles(fo::GameObject* source, long dstTile);
|
||||||
|
|
||||||
|
fo::GameObject* __fastcall MultiHexMoveIsBlocking(fo::GameObject* source, long dstTile);
|
||||||
|
|
||||||
long wmGetCurrentTerrainType();
|
long wmGetCurrentTerrainType();
|
||||||
|
|
||||||
void SurfaceCopyToMem(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromSurface, BYTE* toMem);
|
void SurfaceCopyToMem(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromSurface, BYTE* toMem);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#define FO_VAR_aiInfoList 0x510948
|
#define FO_VAR_aiInfoList 0x510948
|
||||||
#define FO_VAR_ambient_light 0x51923C
|
#define FO_VAR_ambient_light 0x51923C
|
||||||
#define FO_VAR_anim_set 0x54CC14
|
#define FO_VAR_anim_set 0x54CC14
|
||||||
|
#define FO_VAR_anon_alias 0x56CAEC
|
||||||
#define FO_VAR_art 0x510738
|
#define FO_VAR_art 0x510738
|
||||||
#define FO_VAR_art_name 0x56C9E4
|
#define FO_VAR_art_name 0x56C9E4
|
||||||
#define FO_VAR_art_vault_guy_num 0x5108A4
|
#define FO_VAR_art_vault_guy_num 0x5108A4
|
||||||
|
|||||||
+17
-8
@@ -34,21 +34,30 @@ using namespace Fields;
|
|||||||
static std::unordered_map<fo::GameObject*, fo::GameObject*> targets;
|
static std::unordered_map<fo::GameObject*, fo::GameObject*> targets;
|
||||||
static std::unordered_map<fo::GameObject*, fo::GameObject*> sources;
|
static std::unordered_map<fo::GameObject*, fo::GameObject*> sources;
|
||||||
|
|
||||||
fo::GameObject* AI::sf_check_critters_in_lof(fo::GameObject* object, DWORD checkTile, DWORD team) {
|
// Returns the friendly critter or any blocking object in the line of fire
|
||||||
if (object && object->Type() == ObjType::OBJ_TYPE_CRITTER && object->critter.teamNum != team) { // not friendly fire
|
fo::GameObject* AI::CheckShootAndFriendlyInLineOfFire(fo::GameObject* object, long targetTile, long team) {
|
||||||
if (object->tile == checkTile) return nullptr;
|
if (object && object->Type() == ObjType::OBJ_TYPE_CRITTER && object->critter.teamNum != team) { // is not friendly fire
|
||||||
fo::GameObject* obj = nullptr; // continue checking the line of fire from object to checkTile
|
long objTile = object->tile;
|
||||||
fo::func::make_straight_path_func(object, object->tile, checkTile, 0, (DWORD*)&obj, 32, (void*)fo::funcoffs::obj_shoot_blocking_at_);
|
if (objTile == targetTile) return nullptr;
|
||||||
if (!sf_check_critters_in_lof(obj, checkTile, team)) return nullptr;
|
|
||||||
|
if (object->flags & fo::ObjectFlag::MultiHex) {
|
||||||
|
long dir = fo::func::tile_dir(objTile, targetTile);
|
||||||
|
objTile = fo::func::tile_num_in_direction(objTile, dir, 1);
|
||||||
|
if (objTile == targetTile) return nullptr; // just in case
|
||||||
|
}
|
||||||
|
// continue checking the line of fire from object tile to targetTile
|
||||||
|
fo::GameObject* obj = object; // for ignoring the object (multihex) when building the path
|
||||||
|
fo::func::make_straight_path_func(object, objTile, targetTile, 0, (DWORD*)&obj, 32, (void*)fo::funcoffs::obj_shoot_blocking_at_);
|
||||||
|
if (!CheckShootAndFriendlyInLineOfFire(obj, targetTile, team)) return nullptr;
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the friendly critter that is in the line of fire
|
// Returns the friendly critter in the line of fire
|
||||||
fo::GameObject* AI::CheckFriendlyFire(fo::GameObject* target, fo::GameObject* attacker) {
|
fo::GameObject* AI::CheckFriendlyFire(fo::GameObject* target, fo::GameObject* attacker) {
|
||||||
fo::GameObject* object = nullptr;
|
fo::GameObject* object = nullptr;
|
||||||
fo::func::make_straight_path_func(attacker, attacker->tile, target->tile, 0, (DWORD*)&object, 32, (void*)fo::funcoffs::obj_shoot_blocking_at_);
|
fo::func::make_straight_path_func(attacker, attacker->tile, target->tile, 0, (DWORD*)&object, 32, (void*)fo::funcoffs::obj_shoot_blocking_at_);
|
||||||
object = sf_check_critters_in_lof(object, target->tile, attacker->critter.teamNum);
|
object = CheckShootAndFriendlyInLineOfFire(object, target->tile, attacker->critter.teamNum);
|
||||||
return (!object || object->TypeFid() == fo::ObjType::OBJ_TYPE_CRITTER) ? object : nullptr; // 0 if there are no friendly critters
|
return (!object || object->TypeFid() == fo::ObjType::OBJ_TYPE_CRITTER) ? object : nullptr; // 0 if there are no friendly critters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ public:
|
|||||||
const char* name() { return "AI"; }
|
const char* name() { return "AI"; }
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
static fo::GameObject* sf_check_critters_in_lof(fo::GameObject* object, DWORD checkTile, DWORD team);
|
static fo::GameObject* CheckShootAndFriendlyInLineOfFire(fo::GameObject* object, long targetTile, long team);
|
||||||
static fo::GameObject* CheckFriendlyFire(fo::GameObject* target, fo::GameObject* attacker);
|
static fo::GameObject* CheckFriendlyFire(fo::GameObject* target, fo::GameObject* attacker);
|
||||||
|
|
||||||
// TODO: use subscription instead
|
// TODO: use subscription instead
|
||||||
|
|||||||
+120
-42
@@ -932,20 +932,41 @@ end:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __declspec(naked) MultiHexRetargetTileFix() {
|
||||||
|
__asm {
|
||||||
|
push edx; // retargeted tile
|
||||||
|
call fo::funcoffs::obj_blocking_at_;
|
||||||
|
pop edx;
|
||||||
|
test eax, eax;
|
||||||
|
jz isFreeTile;
|
||||||
|
retn;
|
||||||
|
isFreeTile:
|
||||||
|
test [ebp + flags + 1], 0x08; // is source multihex?
|
||||||
|
jnz isMultiHex;
|
||||||
|
retn;
|
||||||
|
isMultiHex:
|
||||||
|
push ecx;
|
||||||
|
mov ecx, ebp;
|
||||||
|
call fo::MultiHexMoveIsBlocking;
|
||||||
|
pop ecx;
|
||||||
|
retn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void __declspec(naked) MultiHexCombatMoveFix() {
|
static void __declspec(naked) MultiHexCombatMoveFix() {
|
||||||
static const DWORD ai_move_steps_closer_move_object_ret = 0x42A192;
|
static const DWORD ai_move_steps_closer_move_object_ret = 0x42A192;
|
||||||
__asm {
|
__asm {
|
||||||
mov edx, [esp + 4]; // source's destination tilenum
|
|
||||||
cmp [edi + tile], edx; // target's tilenum
|
|
||||||
je checkObj;
|
|
||||||
retn; // tilenums are not equal, always move to tile
|
|
||||||
checkObj:
|
|
||||||
test [edi + flags + 1], 0x08; // is target multihex?
|
test [edi + flags + 1], 0x08; // is target multihex?
|
||||||
jnz multiHex;
|
jnz multiHex;
|
||||||
test [esi + flags + 1], 0x08; // is source multihex?
|
test [esi + flags + 1], 0x08; // is source multihex?
|
||||||
jnz multiHex;
|
jnz multiHex;
|
||||||
retn; // move to tile
|
retn; // move to tile
|
||||||
multiHex:
|
multiHex:
|
||||||
|
mov edx, [esp + 4]; // source's destination tilenum
|
||||||
|
cmp [edi + tile], edx; // target's tilenum
|
||||||
|
je moveToObject;
|
||||||
|
retn; // tilenums are not equal, always move to tile
|
||||||
|
moveToObject:
|
||||||
add esp, 4;
|
add esp, 4;
|
||||||
jmp ai_move_steps_closer_move_object_ret; // move to object
|
jmp ai_move_steps_closer_move_object_ret; // move to object
|
||||||
}
|
}
|
||||||
@@ -954,17 +975,17 @@ multiHex:
|
|||||||
static void __declspec(naked) MultiHexCombatRunFix() {
|
static void __declspec(naked) MultiHexCombatRunFix() {
|
||||||
static const DWORD ai_move_steps_closer_run_object_ret = 0x42A169;
|
static const DWORD ai_move_steps_closer_run_object_ret = 0x42A169;
|
||||||
__asm {
|
__asm {
|
||||||
mov edx, [esp + 4]; // source's destination tilenum
|
|
||||||
cmp [edi + tile], edx; // target's tilenum
|
|
||||||
je checkObj;
|
|
||||||
retn; // tilenums are not equal, always run to tile
|
|
||||||
checkObj:
|
|
||||||
test [edi + flags + 1], 0x08; // is target multihex?
|
test [edi + flags + 1], 0x08; // is target multihex?
|
||||||
jnz multiHex;
|
jnz multiHex;
|
||||||
test [esi + flags + 1], 0x08; // is source multihex?
|
test [esi + flags + 1], 0x08; // is source multihex?
|
||||||
jnz multiHex;
|
jnz multiHex;
|
||||||
retn; // run to tile
|
retn; // run to tile
|
||||||
multiHex:
|
multiHex:
|
||||||
|
mov edx, [esp + 4]; // source's destination tilenum
|
||||||
|
cmp [edi + tile], edx; // target's tilenum
|
||||||
|
je runToObject;
|
||||||
|
retn; // tilenums are not equal, always run to tile
|
||||||
|
runToObject:
|
||||||
add esp, 4;
|
add esp, 4;
|
||||||
jmp ai_move_steps_closer_run_object_ret; // run to object
|
jmp ai_move_steps_closer_run_object_ret; // run to object
|
||||||
}
|
}
|
||||||
@@ -1216,27 +1237,6 @@ end:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __declspec(naked) wmTeleportToArea_hack() {
|
|
||||||
__asm {
|
|
||||||
cmp ebx, ds:[FO_VAR_WorldMapCurrArea]
|
|
||||||
je end
|
|
||||||
mov ds:[FO_VAR_WorldMapCurrArea], ebx
|
|
||||||
sub eax, edx
|
|
||||||
add eax, ds:[FO_VAR_wmAreaInfoList]
|
|
||||||
mov edx, [eax+0x30] // wmAreaInfoList.world_posy
|
|
||||||
mov ds:[FO_VAR_world_ypos], edx
|
|
||||||
mov edx, [eax+0x2C] // wmAreaInfoList.world_posx
|
|
||||||
mov ds:[FO_VAR_world_xpos], edx
|
|
||||||
end:
|
|
||||||
xor eax, eax
|
|
||||||
mov ds:[FO_VAR_target_xpos], eax
|
|
||||||
mov ds:[FO_VAR_target_ypos], eax
|
|
||||||
mov ds:[FO_VAR_In_WorldMap], eax
|
|
||||||
push 0x4C5A77
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __declspec(naked) db_get_file_list_hack() {
|
static void __declspec(naked) db_get_file_list_hack() {
|
||||||
__asm {
|
__asm {
|
||||||
push edi
|
push edi
|
||||||
@@ -1578,6 +1578,7 @@ static void __declspec(naked) compute_damage_hack() {
|
|||||||
|
|
||||||
static int currDescLen = 0;
|
static int currDescLen = 0;
|
||||||
static bool showItemDescription = false;
|
static bool showItemDescription = false;
|
||||||
|
|
||||||
static void __stdcall AppendText(const char* text, const char* desc) {
|
static void __stdcall AppendText(const char* text, const char* desc) {
|
||||||
if (showItemDescription && currDescLen == 0) {
|
if (showItemDescription && currDescLen == 0) {
|
||||||
strncpy_s(tempBuffer, desc, 161);
|
strncpy_s(tempBuffer, desc, 161);
|
||||||
@@ -1651,6 +1652,7 @@ skip:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static DWORD expSwiftLearner; // experience points for print
|
static DWORD expSwiftLearner; // experience points for print
|
||||||
|
|
||||||
static void __declspec(naked) statPCAddExperienceCheckPMs_hack() {
|
static void __declspec(naked) statPCAddExperienceCheckPMs_hack() {
|
||||||
__asm {
|
__asm {
|
||||||
mov expSwiftLearner, edi;
|
mov expSwiftLearner, edi;
|
||||||
@@ -2299,6 +2301,42 @@ static void __declspec(naked) action_climb_ladder_hack() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __declspec(naked) wmTeleportToArea_hack() {
|
||||||
|
static const DWORD wmTeleportToArea_Ret = 0x4C5A77;
|
||||||
|
__asm {
|
||||||
|
xor ecx, ecx;
|
||||||
|
cmp ebx, ds:[FO_VAR_WorldMapCurrArea];
|
||||||
|
je end;
|
||||||
|
mov ds:[FO_VAR_WorldMapCurrArea], ebx;
|
||||||
|
sub eax, edx;
|
||||||
|
add eax, ds:[FO_VAR_wmAreaInfoList];
|
||||||
|
cmp dword ptr [eax + 0x34], 1; // wmAreaInfoList.size
|
||||||
|
mov edx, [eax + 0x30]; // wmAreaInfoList.world_posy
|
||||||
|
mov eax, [eax + 0x2C]; // wmAreaInfoList.world_posx
|
||||||
|
jg largeLoc;
|
||||||
|
je mediumLoc;
|
||||||
|
//smallLoc:
|
||||||
|
sub eax, 5;
|
||||||
|
lea edx, [edx - 5];
|
||||||
|
mediumLoc:
|
||||||
|
sub eax, 10
|
||||||
|
lea edx, [edx - 10];
|
||||||
|
// check negative values
|
||||||
|
test eax, eax;
|
||||||
|
cmovl eax, ecx;
|
||||||
|
test edx, edx;
|
||||||
|
cmovl edx, ecx;
|
||||||
|
largeLoc:
|
||||||
|
mov ds:[FO_VAR_world_ypos], edx;
|
||||||
|
mov ds:[FO_VAR_world_xpos], eax;
|
||||||
|
end:
|
||||||
|
mov ds:[FO_VAR_target_xpos], ecx;
|
||||||
|
mov ds:[FO_VAR_target_ypos], ecx;
|
||||||
|
mov ds:[FO_VAR_In_WorldMap], ecx;
|
||||||
|
jmp wmTeleportToArea_Ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void __declspec(naked) wmAreaMarkVisitedState_hack() {
|
static void __declspec(naked) wmAreaMarkVisitedState_hack() {
|
||||||
static const DWORD wmAreaMarkVisitedState_Ret = 0x4C46A2;
|
static const DWORD wmAreaMarkVisitedState_Ret = 0x4C46A2;
|
||||||
//static const DWORD wmAreaMarkVisitedState_Error = 0x4C4698;
|
//static const DWORD wmAreaMarkVisitedState_Error = 0x4C4698;
|
||||||
@@ -2321,6 +2359,14 @@ static void __declspec(naked) wmAreaMarkVisitedState_hack() {
|
|||||||
mediumLoc:
|
mediumLoc:
|
||||||
sub eax, 10;
|
sub eax, 10;
|
||||||
lea edx, [edx - 10];
|
lea edx, [edx - 10];
|
||||||
|
// check negative values
|
||||||
|
push ecx;
|
||||||
|
xor ecx, ecx;
|
||||||
|
test eax, eax;
|
||||||
|
cmovl eax, ecx;
|
||||||
|
test edx, edx;
|
||||||
|
cmovl edx, ecx;
|
||||||
|
pop ecx;
|
||||||
largeLoc:
|
largeLoc:
|
||||||
lea ebx, [esp]; // ppSubTile out
|
lea ebx, [esp]; // ppSubTile out
|
||||||
push edx;
|
push edx;
|
||||||
@@ -2361,8 +2407,8 @@ fixRadius:
|
|||||||
|
|
||||||
static void __declspec(naked) wmWorldMap_hack() {
|
static void __declspec(naked) wmWorldMap_hack() {
|
||||||
__asm {
|
__asm {
|
||||||
mov ebx, [ebx + 0x34]; // wmAreaInfoList.size
|
cmp dword ptr [ebx + 0x34], 1; // wmAreaInfoList.size
|
||||||
cmp ebx, 1;
|
mov ebx, 0;
|
||||||
jg largeLoc;
|
jg largeLoc;
|
||||||
je mediumLoc;
|
je mediumLoc;
|
||||||
//smallLoc:
|
//smallLoc:
|
||||||
@@ -2371,8 +2417,12 @@ static void __declspec(naked) wmWorldMap_hack() {
|
|||||||
mediumLoc:
|
mediumLoc:
|
||||||
sub eax, 10;
|
sub eax, 10;
|
||||||
lea edx, [edx - 10];
|
lea edx, [edx - 10];
|
||||||
|
// check negative values
|
||||||
|
test eax, eax;
|
||||||
|
cmovl eax, ebx;
|
||||||
|
test edx, edx;
|
||||||
|
cmovl edx, ebx;
|
||||||
largeLoc:
|
largeLoc:
|
||||||
xor ebx, ebx;
|
|
||||||
jmp fo::funcoffs::wmPartyInitWalking_;
|
jmp fo::funcoffs::wmPartyInitWalking_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2898,6 +2948,9 @@ void BugFixes::init()
|
|||||||
// Fix for multihex critters moving too close and overlapping their targets in combat
|
// Fix for multihex critters moving too close and overlapping their targets in combat
|
||||||
MakeCall(0x42A14F, MultiHexCombatRunFix, 1);
|
MakeCall(0x42A14F, MultiHexCombatRunFix, 1);
|
||||||
MakeCall(0x42A178, MultiHexCombatMoveFix, 1);
|
MakeCall(0x42A178, MultiHexCombatMoveFix, 1);
|
||||||
|
// Check neighboring tiles to prevent critters from overlapping other object tiles when moving to the retargeted tile
|
||||||
|
SafeWrite16(0x42A3A6, 0xE889); // xor eax, eax > mov eax, ebp (fix retargeting tile for multihex critters)
|
||||||
|
HookCall(0x42A3A8, MultiHexRetargetTileFix); // cai_retargetTileFromFriendlyFire_
|
||||||
dlogr(" Done", DL_INIT);
|
dlogr(" Done", DL_INIT);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
@@ -2957,13 +3010,6 @@ void BugFixes::init()
|
|||||||
// Fix for checking the horizontal position on the y-axis instead of x when setting coordinates on the world map
|
// Fix for checking the horizontal position on the y-axis instead of x when setting coordinates on the world map
|
||||||
SafeWrite8(0x4C4743, 0xC6); // cmp esi, eax
|
SafeWrite8(0x4C4743, 0xC6); // cmp esi, eax
|
||||||
|
|
||||||
// Partial fix for incorrect positioning after exiting small locations (e.g. Ghost Farm)
|
|
||||||
//if (GetConfigInt("Misc", "SmallLocExitFix", 1)) {
|
|
||||||
dlog("Applying fix for incorrect positioning after exiting small locations.", DL_INIT);
|
|
||||||
MakeJump(0x4C5A41, wmTeleportToArea_hack);
|
|
||||||
dlogr(" Done", DL_INIT);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (GetConfigInt("Misc", "PrintToFileFix", 1)) {
|
//if (GetConfigInt("Misc", "PrintToFileFix", 1)) {
|
||||||
dlog("Applying print to file fix.", DL_INIT);
|
dlog("Applying print to file fix.", DL_INIT);
|
||||||
MakeCall(0x4C67D4, db_get_file_list_hack);
|
MakeCall(0x4C67D4, db_get_file_list_hack);
|
||||||
@@ -3279,13 +3325,20 @@ void BugFixes::init()
|
|||||||
MakeCall(0x411FD6, action_use_an_item_on_object_hack);
|
MakeCall(0x411FD6, action_use_an_item_on_object_hack);
|
||||||
MakeCall(0x411DF7, action_climb_ladder_hack); // bug caused by anim_move_to_tile_ fix
|
MakeCall(0x411DF7, action_climb_ladder_hack); // bug caused by anim_move_to_tile_ fix
|
||||||
|
|
||||||
|
// Partial fix for incorrect positioning after exiting small/medium locations (e.g. Ghost Farm)
|
||||||
|
//if (GetConfigInt("Misc", "SmallLocExitFix", 1)) {
|
||||||
|
dlog("Applying fix for incorrect positioning after exiting small/medium locations.", DL_INIT);
|
||||||
|
MakeJump(0x4C5A41, wmTeleportToArea_hack);
|
||||||
|
dlogr(" Done", DL_INIT);
|
||||||
|
//}
|
||||||
|
|
||||||
// Fix for Scout perk being taken into account when setting the visibility of locations with mark_area_known function
|
// Fix for Scout perk being taken into account when setting the visibility of locations with mark_area_known function
|
||||||
// also fix the incorrect coordinates for small/medium location circles that the engine uses to highlight their sub-tiles
|
// also fix the incorrect coordinates for small/medium location circles that the engine uses to highlight their sub-tiles
|
||||||
// and fix visited tiles on the world map being darkened again when a location is added next to them
|
// and fix visited tiles on the world map being darkened again when a location is added next to them
|
||||||
MakeJump(0x4C466F, wmAreaMarkVisitedState_hack);
|
MakeJump(0x4C466F, wmAreaMarkVisitedState_hack);
|
||||||
SafeWrite8(0x4C46AB, 0x58); // esi > ebx
|
SafeWrite8(0x4C46AB, 0x58); // esi > ebx
|
||||||
|
|
||||||
// Fix the position of the target marker for small/medium location circles
|
// Fix the position of the destination marker for small/medium location circles
|
||||||
MakeCall(0x4C03AA, wmWorldMap_hack, 2);
|
MakeCall(0x4C03AA, wmWorldMap_hack, 2);
|
||||||
|
|
||||||
// Fix to prevent using number keys to enter unvisited areas on a town map
|
// Fix to prevent using number keys to enter unvisited areas on a town map
|
||||||
@@ -3352,6 +3405,31 @@ void BugFixes::init()
|
|||||||
MakeJump(0x4C372B, wmSubTileMarkRadiusVisited_hack);
|
MakeJump(0x4C372B, wmSubTileMarkRadiusVisited_hack);
|
||||||
SafeWrite16(0x4C3723, 0xC931); // mov ecx, esi > xor ecx, ecx
|
SafeWrite16(0x4C3723, 0xC931); // mov ecx, esi > xor ecx, ecx
|
||||||
SafeWrite8(0x4C3727, 0x51); // push esi > push ecx
|
SafeWrite8(0x4C3727, 0x51); // push esi > push ecx
|
||||||
|
|
||||||
|
// Fix the code in combat_is_shot_blocked_ to correctly get the next tile from a multihex object instead of the previous
|
||||||
|
// object or source tile
|
||||||
|
// Note: this bug does not cause an error in the function work
|
||||||
|
BYTE codeData[] = {
|
||||||
|
0x8B, 0x70, 0x04, // mov esi, [eax + 4]
|
||||||
|
0xF6, 0x40, 0x25, 0x08, // test [eax + flags2], MultiHex_
|
||||||
|
0x74, 0x1E, // jz 0x426D83
|
||||||
|
0x39, 0xEE, // cmp esi, ebp
|
||||||
|
0x74, 0x1A, // jz 0x426D83
|
||||||
|
0x90
|
||||||
|
};
|
||||||
|
SafeWriteBytes(0x426D5C, codeData, 14); // combat_is_shot_blocked_
|
||||||
|
|
||||||
|
// Fix for NPC stuck in an animation loop in combat when trying to move close to a multihex critter
|
||||||
|
// this prevents moving to the multihex critter when the critters are close together
|
||||||
|
BYTE codeData1[] = {
|
||||||
|
0x89, 0xF0, // mov eax, esi
|
||||||
|
0x89, 0xFA, // mov edx, edi
|
||||||
|
0xE8, 0x00, 0x00, 0x0, 0x0, // call obj_dist_
|
||||||
|
0x83, 0xF8, 0x01, // cmp eax, 1
|
||||||
|
0x0F, 0x8E, 0xAB, 0x0, 0x0, 0x0, // jle 0x42A1B1 (exit)
|
||||||
|
};
|
||||||
|
SafeWriteBytes(0x42A0F4, codeData1, 18); // ai_move_steps_closer_
|
||||||
|
HookCall(0x42A0F8, (void*)fo::funcoffs::obj_dist_);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ static void __declspec(naked) compute_spray_rounds_distribution() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BurstMods::init() {
|
void BurstMods::init() {
|
||||||
if (GetConfigInt("Misc", "ComputeSprayMod", 0)) {
|
if (GetConfigInt("Misc", "ComputeSprayMod", 0)) {
|
||||||
dlog("Applying ComputeSpray changes.", DL_INIT);
|
dlog("Applying ComputeSpray changes.", DL_INIT);
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ static void LoadElevators(const char* elevFile) {
|
|||||||
elevatorType[i] = min(type, elevatorCount - 1);
|
elevatorType[i] = min(type, elevatorCount - 1);
|
||||||
if (i >= vanillaElevatorCount) {
|
if (i >= vanillaElevatorCount) {
|
||||||
int cBtn = iniGetInt(section, "ButtonCount", 2, elevFile);
|
int cBtn = iniGetInt(section, "ButtonCount", 2, elevFile);
|
||||||
if (cBtn > exitsPerElevator) cBtn = exitsPerElevator;
|
if (cBtn < 2) cBtn = 2;
|
||||||
elevatorsBtnCount[i] = max(2, cBtn);
|
elevatorsBtnCount[i] = min(cBtn, exitsPerElevator);
|
||||||
}
|
}
|
||||||
elevatorsFrms[i].main = iniGetInt(section, "MainFrm", elevatorsFrms[i].main, elevFile);
|
elevatorsFrms[i].main = iniGetInt(section, "MainFrm", elevatorsFrms[i].main, elevFile);
|
||||||
elevatorsFrms[i].buttons = iniGetInt(section, "ButtonsFrm", elevatorsFrms[i].buttons, elevFile);
|
elevatorsFrms[i].buttons = iniGetInt(section, "ButtonsFrm", elevatorsFrms[i].buttons, elevFile);
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ skip:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static DWORD explosion_effect_starting_dir = 0;
|
static DWORD explosion_effect_starting_dir = 0;
|
||||||
|
|
||||||
static void __declspec(naked) explosion_effect_hook() {
|
static void __declspec(naked) explosion_effect_hook() {
|
||||||
static const DWORD explosion_effect_hook_back = 0x411AB9;
|
static const DWORD explosion_effect_hook_back = 0x411AB9;
|
||||||
__asm {
|
__asm {
|
||||||
|
|||||||
@@ -452,8 +452,10 @@ static DWORD __stdcall QuickSaveGame(fo::DbFile* file, char* filename) {
|
|||||||
|
|
||||||
FILETIME ftCurrSlot;
|
FILETIME ftCurrSlot;
|
||||||
GetSaveFileTime(filename, &ftCurrSlot);
|
GetSaveFileTime(filename, &ftCurrSlot);
|
||||||
|
|
||||||
if (currSlot == 0 || ftCurrSlot.dwHighDateTime > ftPrevSlot.dwHighDateTime
|
if (currSlot == 0 || ftCurrSlot.dwHighDateTime > ftPrevSlot.dwHighDateTime
|
||||||
|| (ftCurrSlot.dwHighDateTime == ftPrevSlot.dwHighDateTime && ftCurrSlot.dwLowDateTime > ftPrevSlot.dwLowDateTime)) {
|
|| (ftCurrSlot.dwHighDateTime == ftPrevSlot.dwHighDateTime && ftCurrSlot.dwLowDateTime > ftPrevSlot.dwLowDateTime))
|
||||||
|
{
|
||||||
ftPrevSlot.dwHighDateTime = ftCurrSlot.dwHighDateTime;
|
ftPrevSlot.dwHighDateTime = ftCurrSlot.dwHighDateTime;
|
||||||
ftPrevSlot.dwLowDateTime = ftCurrSlot.dwLowDateTime;
|
ftPrevSlot.dwLowDateTime = ftCurrSlot.dwLowDateTime;
|
||||||
|
|
||||||
|
|||||||
@@ -244,21 +244,21 @@ isNotReading:
|
|||||||
|
|
||||||
static void __declspec(naked) CheckHeroExist() {
|
static void __declspec(naked) CheckHeroExist() {
|
||||||
__asm {
|
__asm {
|
||||||
cmp esi, critterArraySize; // check if loading hero art
|
|
||||||
jle endFunc;
|
|
||||||
mov eax, FO_VAR_art_name; // critter art file name address (file name)
|
mov eax, FO_VAR_art_name; // critter art file name address (file name)
|
||||||
|
cmp esi, critterArraySize; // check if loading hero art
|
||||||
|
jg checkArt;
|
||||||
|
retn;
|
||||||
|
checkArt:
|
||||||
call fo::funcoffs::db_access_; // check art file exists
|
call fo::funcoffs::db_access_; // check art file exists
|
||||||
test eax, eax;
|
test eax, eax;
|
||||||
jnz endFunc;
|
jz notExists;
|
||||||
|
mov eax, FO_VAR_art_name;
|
||||||
// if file not found load regular critter art instead
|
retn;
|
||||||
|
notExists: // if file not found load regular critter art instead
|
||||||
sub esi, critterArraySize;
|
sub esi, critterArraySize;
|
||||||
add esp, 4; // drop func ret address
|
add esp, 4; // drop func ret address
|
||||||
mov eax, 0x4194E2;
|
mov eax, 0x4194E2;
|
||||||
jmp eax;
|
jmp eax;
|
||||||
endFunc:
|
|
||||||
mov eax, FO_VAR_art_name;
|
|
||||||
retn;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,17 +341,22 @@ static long __stdcall AddHeroCritNames() { // art_init_
|
|||||||
char *CritList = critterArt.names; // critter list offset
|
char *CritList = critterArt.names; // critter list offset
|
||||||
char *HeroList = CritList + critterArraySize; // set start of hero critter list after regular critter list
|
char *HeroList = CritList + critterArraySize; // set start of hero critter list after regular critter list
|
||||||
|
|
||||||
memset(HeroList, 0, critterArraySize);
|
std::memset(HeroList, 0, critterArraySize);
|
||||||
|
|
||||||
for (DWORD i = 0; i < critterListSize; i++) { // copy critter name list to hero name list
|
for (DWORD i = 0; i < critterListSize; i++) { // copy critter name list to hero name list
|
||||||
*HeroList = '_'; // insert a '_' char at the front of new hero critt names. fallout wont load the same name twice
|
*HeroList = '_'; // insert a '_' char at the front of new hero critt names. fallout wont load the same name twice
|
||||||
memcpy(HeroList + 1, CritList, 11);
|
std::memcpy(HeroList + 1, CritList, 11);
|
||||||
HeroList += 13;
|
HeroList += 13;
|
||||||
CritList += 13;
|
CritList += 13;
|
||||||
}
|
}
|
||||||
return critterArt.total;
|
return critterArt.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void DoubleArtAlias() {
|
||||||
|
DWORD* crittersAliasData = *(DWORD**)FO_VAR_anon_alias;
|
||||||
|
std::memcpy(crittersAliasData + critterListSize, crittersAliasData, critterListSize * 4);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////GRAPHICS HERO FUNCTIONS///////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////GRAPHICS HERO FUNCTIONS///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static void DrawPC() {
|
static void DrawPC() {
|
||||||
@@ -473,8 +478,8 @@ reset: // set race and style to defaults
|
|||||||
call LoadHeroDat;
|
call LoadHeroDat;
|
||||||
pop ecx;
|
pop ecx;
|
||||||
pop edx;
|
pop edx;
|
||||||
call fo::funcoffs::proto_dude_update_gender_;
|
|
||||||
endFunc:
|
endFunc:
|
||||||
|
call fo::funcoffs::proto_dude_update_gender_;
|
||||||
mov eax, 1;
|
mov eax, 1;
|
||||||
retn;
|
retn;
|
||||||
}
|
}
|
||||||
@@ -597,8 +602,8 @@ static void DrawCharNote(bool style, int winRef, DWORD xPosWin, DWORD yPosWin, B
|
|||||||
textHeight = fo::GetTextHeight();
|
textHeight = fo::GetTextHeight();
|
||||||
fo::PrintText(TitleMsg, colour, 0, 0, 265, 280, PadSurface);
|
fo::PrintText(TitleMsg, colour, 0, 0, 265, 280, PadSurface);
|
||||||
// draw line
|
// draw line
|
||||||
memset(PadSurface + 280 * textHeight, colour, 265);
|
std::memset(PadSurface + 280 * textHeight, colour, 265);
|
||||||
memset(PadSurface + 280 * (textHeight + 1), colour, 265);
|
std::memset(PadSurface + 280 * (textHeight + 1), colour, 265);
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD lineNum = 0;
|
DWORD lineNum = 0;
|
||||||
@@ -1414,6 +1419,9 @@ static void EnableHeroAppearanceMod() {
|
|||||||
// Double size of critter art index creating a new area for hero art (art_read_lst_)
|
// Double size of critter art index creating a new area for hero art (art_read_lst_)
|
||||||
HookCall(0x4196B0, DoubleArt);
|
HookCall(0x4196B0, DoubleArt);
|
||||||
|
|
||||||
|
// Copy inherited values of critter art into the extended part of the _anon_alias array (art_init_)
|
||||||
|
HookCall(0x418CA2, DoubleArtAlias);
|
||||||
|
|
||||||
// Add new hero critter names at end of critter list (art_init_)
|
// Add new hero critter names at end of critter list (art_init_)
|
||||||
MakeCall(0x418B39, AddHeroCritNames);
|
MakeCall(0x418B39, AddHeroCritNames);
|
||||||
|
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ capsMultiDrop:
|
|||||||
__asm retn;
|
__asm retn;
|
||||||
}
|
}
|
||||||
__asm add esp, 4;
|
__asm add esp, 4;
|
||||||
__asm jmp InvenActionObjDropRet; // no caps drop
|
__asm jmp InvenActionObjDropRet; // no caps drop
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __declspec(naked) InvenActionExplosiveDropHack() {
|
static void __declspec(naked) InvenActionExplosiveDropHack() {
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ void GetSavePath(char* buf, char* ftype) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string saveSfallDataFailMsg;
|
static std::string saveSfallDataFailMsg;
|
||||||
|
|
||||||
static void __stdcall SaveGame2() {
|
static void __stdcall SaveGame2() {
|
||||||
char buf[MAX_PATH];
|
char buf[MAX_PATH];
|
||||||
GetSavePath(buf, "gv");
|
GetSavePath(buf, "gv");
|
||||||
@@ -168,6 +169,7 @@ errorSave:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string saveFailMsg;
|
static std::string saveFailMsg;
|
||||||
|
|
||||||
static DWORD __stdcall CombatSaveTest() {
|
static DWORD __stdcall CombatSaveTest() {
|
||||||
if (!saveInCombatFix && !PartyControl::IsNpcControlled()) return 1;
|
if (!saveInCombatFix && !PartyControl::IsNpcControlled()) return 1;
|
||||||
if (inLoop & COMBAT) {
|
if (inLoop & COMBAT) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user