Merge branch 'develop'

This commit is contained in:
NovaRain
2020-07-03 21:37:11 +08:00
30 changed files with 428 additions and 195 deletions
+28 -35
View File
@@ -1,10 +1,9 @@
;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
; in this file, as some perks have hardcoded effects which may
; catch you out
;Since traits need to be picked before any scripts run, they
; have an additional NoHardcode option in this file which can
;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 catch you out
;Since traits need to be picked before any scripts run, they have an additional NoHardcode option in this file which can
; be used to remove their hardcoded effects, and add new stat/skill effects
[PerksTweak]
@@ -31,41 +30,35 @@ WeaponHandlingBonus=3
;Set to 1 to enable the modifications for perks
Enable=0
;Name=The name of the perk (max 63 characters)
;Desc=The description of the perk (max 255 characters)
;Image=The line number (0-indexed) of the corresponding FRM in skilldex.lst
;Ranks=The number of perk levels
;Level=The minimum required level
;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
;Stat=The modified stat or -1 if you don't want to change any stats
;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
; (67108864 or 0x4000000), it checks GVAR. For example, if Skill1=67108864, then
; the perk will check GVAR_PLAYER_REPUTATION (67108864 + 0) on Skill1Mag value
;Skill1Mag=Positive value is used as the minimum requirements of the first
; 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
; selection window; with Skill1=1 and Skill1Mag=-120, the perk will not be
; available if player has big guns skill >= 120
;Skill2=The second skill/GVAR (or -1 for none) to check
;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
;Name: The name of the perk (max 63 characters)
;Desc: The description of the perk (max 255 characters)
;Image: The line number (0-indexed) of the corresponding FRM in skilldex.lst
;Ranks: The number of perk levels
;Level: The minimum required level
;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
;Stat: The modified stat or -1 if you don't want to change any stats
;StatMag: The increased/decreased value to the modified stat
;Skill1/Skill2: The first/second skill (or -1 for none) to check, but if you set a specific bit 0x4000000 (67108864), it checks GVAR.
; For example, if Skill1=0x4000004, then the perk will check GVAR_BAD_MONSTER (0x4000000 + 4) on Skill1Mag value
;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.
; 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
; selection window; with Skill1=1 and Skill1Mag=-120, the perk will not be available if player has big guns skill >= 120
;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)
;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
;These parameters are only for extra perks
;Stat1-Stat2 = same as Stat
;Stat1Mag-Stat2Mag = same as StatMag
;Skill3-Skill5 = The skill to be modified (or -1 for none)
;Skill3Mod-Skill5Mod = The increased/decreased value to the modified skill
;Stat1/Stat2: same as Stat
;Stat1Mag/Stat2Mag: same as StatMag
;Skill3-Skill5: The skill to be modified (or -1 for none)
;Skill3Mod-Skill5Mod: The increased/decreased value to the modified skill
;##############################################################################
;This is a modification to vanilla perk 0
+2 -2
View File
@@ -1,5 +1,5 @@
;sfall configuration settings
;v4.2.5
;v4.2.6
[Main]
;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)
;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
;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
;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
Requires sfall 3.7b or higher
NOTE: this script requires compiler from sfall modderspack with -s option
(short circuit evaluation)
NOTE: this script requires compiler from sfall modderspack with -s option (short circuit evaluation)
*/
@@ -30,8 +29,8 @@ procedure combatturn_handler;
#define PORTAL (0)
#define DOOR_FLAGS (0x24)
variable only_once := 0;
variable arrayPid;
variable onlyOnce := 0;
variable pidDoors;
procedure start begin
if game_loaded then begin
@@ -41,11 +40,11 @@ procedure start begin
set_global_script_repeat(40); // for sfall 3.x
call map_enter_p_proc;
end else begin
if (only_once == 1 and combat_is_initialized) then begin
only_once := 2;
if (onlyOnce == 1 and combat_is_initialized) then begin
onlyOnce := 2;
call set_door_flag(0); // reset flag when entering combat mode
end else if (only_once == 2 and not(combat_is_initialized)) then begin
only_once := 1;
end else if (onlyOnce == 2 and not(combat_is_initialized)) then begin
onlyOnce := 1;
if (sfall_ver_major >= 4) then set_global_script_repeat(0);
call set_door_flag(FLAG_WALKTHRU); // set flag after combat mode ends
end
@@ -53,34 +52,35 @@ procedure start begin
end
procedure map_enter_p_proc begin
if (arrayPid) then begin
// when changing maps, delete the current array to create a new one
free_array(arrayPid);
arrayPid := 0;
if (pidDoors) then begin
// when changing the map, delete the current array to create a new one
free_array(pidDoors);
pidDoors := 0;
end
only_once := 1;
onlyOnce := 1;
call set_door_flag(FLAG_WALKTHRU); // set flag when entering the map
end
procedure set_door_flag(variable state) begin
variable objectMap, obj, objPid, i;
if (arrayPid) then begin
if (pidDoors) then begin
call set_door_flag_array(state);
return;
end
objectMap := list_as_array(LIST_SCENERY);
arrayPid := create_array(0, 0);
pidDoors := create_array(0, 0);
foreach (obj in objectMap) begin
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
end
resize_array(arrayPid, i + 1);
arrayPid[i] := objPid;
resize_array(pidDoors, i + 1);
pidDoors[i] := objPid; // add pid to array
i++;
set_proto_data(objPid, DOOR_FLAGS, state);
end
@@ -88,14 +88,14 @@ end
procedure set_door_flag_array(variable state) begin
variable objPid;
foreach (objPid in arrayPid) begin
foreach (objPid in pidDoors) begin
set_proto_data(objPid, DOOR_FLAGS, state);
end
end
procedure combatturn_handler begin
if (only_once == 1) then begin
only_once := 2;
if (onlyOnce == 1) then begin
onlyOnce := 2;
call set_door_flag_array(0);
set_global_script_repeat(60);
end
+9
View File
@@ -272,6 +272,11 @@
use_obj_on_obj(item, dude_obj); \
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
#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)
@@ -304,6 +309,10 @@
#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_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_string_pointer(text) sfall_func1("get_string_pointer", text)
#define get_text_width(text) sfall_func1("get_text_width", text)
+10 -3
View File
@@ -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 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.
@@ -480,8 +480,8 @@ Some utility/math functions are available:
- sets the current cursor mode
> void sfall_func0("display_stats")
- displays player stats in the inventory screen display window
- works only in opened inventory
- updates player's stats in the inventory display window or on the character screen
- NOTE: works only when the interface window is opened
> int sfall_func1("lock_is_jammed", object)
- 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)
- 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 -------
------------------------
+8 -2
View File
@@ -9,10 +9,14 @@ namespace sfall
std::multimap<long, long> writeAddress;
static std::vector<long> excludeAddr = {
static std::vector<long> excludeWarning = {
0x44E949, 0x44E94A, 0x44E937, 0x4F5F40, 0x4CB850, // from movies.cpp
};
static std::vector<long> excludeConflict = {
0x42A0F8, // bugfixes.cpp
};
struct HackPair {
long addr;
long len;
@@ -135,8 +139,10 @@ void PrintAddrList() {
if (diff == 0) {
dlog_f("0x%x L:%d [Overwriting]\n", DL_MAIN, el.addr, el.len);
} else if (diff < pl) {
if (std::find(excludeConflict.cbegin(), excludeConflict.cend(), el.addr) == excludeConflict.cend()) {
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) {
dlog_f("0x%x L:%d [Warning] Hacking near:0x%x\n", DL_MAIN, el.addr, el.len, pa);
} else if (level >= 12) {
@@ -149,7 +155,7 @@ void PrintAddrList() {
void CheckConflict(DWORD addr, long len) {
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];
sprintf_s(buf, "Memory overwriting at address 0x%x", addr);
MessageBoxA(0, buf, "", MB_TASKMODAL);
+51 -2
View File
@@ -246,14 +246,25 @@ fo::Window* GetWindow(long winType) {
winID = fo::var::optnwin;
break;
default:
return (fo::Window*)-1;
return (fo::Window*)(-1);
}
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
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);
while (obj) {
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
long wmGetCurrentTerrainType() {
long* terrainId = *(long**)FO_VAR_world_subtile;
+6
View File
@@ -85,8 +85,14 @@ long __fastcall GetTopWindowID(long xPos, long yPos);
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);
// 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();
void SurfaceCopyToMem(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromSurface, BYTE* toMem);
+1
View File
@@ -9,6 +9,7 @@
#define FO_VAR_aiInfoList 0x510948
#define FO_VAR_ambient_light 0x51923C
#define FO_VAR_anim_set 0x54CC14
#define FO_VAR_anon_alias 0x56CAEC
#define FO_VAR_art 0x510738
#define FO_VAR_art_name 0x56C9E4
#define FO_VAR_art_vault_guy_num 0x5108A4
+17 -8
View File
@@ -34,21 +34,30 @@ using namespace Fields;
static std::unordered_map<fo::GameObject*, fo::GameObject*> targets;
static std::unordered_map<fo::GameObject*, fo::GameObject*> sources;
fo::GameObject* AI::sf_check_critters_in_lof(fo::GameObject* object, DWORD checkTile, DWORD team) {
if (object && object->Type() == ObjType::OBJ_TYPE_CRITTER && object->critter.teamNum != team) { // not friendly fire
if (object->tile == checkTile) return nullptr;
fo::GameObject* obj = nullptr; // continue checking the line of fire from object to checkTile
fo::func::make_straight_path_func(object, object->tile, checkTile, 0, (DWORD*)&obj, 32, (void*)fo::funcoffs::obj_shoot_blocking_at_);
if (!sf_check_critters_in_lof(obj, checkTile, team)) return nullptr;
// Returns the friendly critter or any blocking object in the line of fire
fo::GameObject* AI::CheckShootAndFriendlyInLineOfFire(fo::GameObject* object, long targetTile, long team) {
if (object && object->Type() == ObjType::OBJ_TYPE_CRITTER && object->critter.teamNum != team) { // is not friendly fire
long objTile = object->tile;
if (objTile == targetTile) 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;
}
// 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* object = nullptr;
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
}
+1 -1
View File
@@ -30,7 +30,7 @@ public:
const char* name() { return "AI"; }
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);
// TODO: use subscription instead
+120 -42
View File
@@ -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 const DWORD ai_move_steps_closer_move_object_ret = 0x42A192;
__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?
jnz multiHex;
test [esi + flags + 1], 0x08; // is source multihex?
jnz multiHex;
retn; // move to tile
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;
jmp ai_move_steps_closer_move_object_ret; // move to object
}
@@ -954,17 +975,17 @@ multiHex:
static void __declspec(naked) MultiHexCombatRunFix() {
static const DWORD ai_move_steps_closer_run_object_ret = 0x42A169;
__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?
jnz multiHex;
test [esi + flags + 1], 0x08; // is source multihex?
jnz multiHex;
retn; // run to tile
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;
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() {
__asm {
push edi
@@ -1578,6 +1578,7 @@ static void __declspec(naked) compute_damage_hack() {
static int currDescLen = 0;
static bool showItemDescription = false;
static void __stdcall AppendText(const char* text, const char* desc) {
if (showItemDescription && currDescLen == 0) {
strncpy_s(tempBuffer, desc, 161);
@@ -1651,6 +1652,7 @@ skip:
}
static DWORD expSwiftLearner; // experience points for print
static void __declspec(naked) statPCAddExperienceCheckPMs_hack() {
__asm {
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 const DWORD wmAreaMarkVisitedState_Ret = 0x4C46A2;
//static const DWORD wmAreaMarkVisitedState_Error = 0x4C4698;
@@ -2321,6 +2359,14 @@ static void __declspec(naked) wmAreaMarkVisitedState_hack() {
mediumLoc:
sub eax, 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:
lea ebx, [esp]; // ppSubTile out
push edx;
@@ -2361,8 +2407,8 @@ fixRadius:
static void __declspec(naked) wmWorldMap_hack() {
__asm {
mov ebx, [ebx + 0x34]; // wmAreaInfoList.size
cmp ebx, 1;
cmp dword ptr [ebx + 0x34], 1; // wmAreaInfoList.size
mov ebx, 0;
jg largeLoc;
je mediumLoc;
//smallLoc:
@@ -2371,8 +2417,12 @@ static void __declspec(naked) wmWorldMap_hack() {
mediumLoc:
sub eax, 10;
lea edx, [edx - 10];
// check negative values
test eax, eax;
cmovl eax, ebx;
test edx, edx;
cmovl edx, ebx;
largeLoc:
xor ebx, ebx;
jmp fo::funcoffs::wmPartyInitWalking_;
}
}
@@ -2898,6 +2948,9 @@ void BugFixes::init()
// Fix for multihex critters moving too close and overlapping their targets in combat
MakeCall(0x42A14F, MultiHexCombatRunFix, 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);
//}
@@ -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
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)) {
dlog("Applying print to file fix.", DL_INIT);
MakeCall(0x4C67D4, db_get_file_list_hack);
@@ -3279,13 +3325,20 @@ void BugFixes::init()
MakeCall(0x411FD6, action_use_an_item_on_object_hack);
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
// 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
MakeJump(0x4C466F, wmAreaMarkVisitedState_hack);
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);
// 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);
SafeWrite16(0x4C3723, 0xC931); // mov ecx, esi > xor ecx, 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_);
}
}
-1
View File
@@ -66,7 +66,6 @@ static void __declspec(naked) compute_spray_rounds_distribution() {
}
}
void BurstMods::init() {
if (GetConfigInt("Misc", "ComputeSprayMod", 0)) {
dlog("Applying ComputeSpray changes.", DL_INIT);
+2 -2
View File
@@ -120,8 +120,8 @@ static void LoadElevators(const char* elevFile) {
elevatorType[i] = min(type, elevatorCount - 1);
if (i >= vanillaElevatorCount) {
int cBtn = iniGetInt(section, "ButtonCount", 2, elevFile);
if (cBtn > exitsPerElevator) cBtn = exitsPerElevator;
elevatorsBtnCount[i] = max(2, cBtn);
if (cBtn < 2) cBtn = 2;
elevatorsBtnCount[i] = min(cBtn, exitsPerElevator);
}
elevatorsFrms[i].main = iniGetInt(section, "MainFrm", elevatorsFrms[i].main, elevFile);
elevatorsFrms[i].buttons = iniGetInt(section, "ButtonsFrm", elevatorsFrms[i].buttons, elevFile);
+1
View File
@@ -68,6 +68,7 @@ skip:
}
static DWORD explosion_effect_starting_dir = 0;
static void __declspec(naked) explosion_effect_hook() {
static const DWORD explosion_effect_hook_back = 0x411AB9;
__asm {
+3 -1
View File
@@ -452,8 +452,10 @@ static DWORD __stdcall QuickSaveGame(fo::DbFile* file, char* filename) {
FILETIME ftCurrSlot;
GetSaveFileTime(filename, &ftCurrSlot);
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.dwLowDateTime = ftCurrSlot.dwLowDateTime;
+21 -13
View File
@@ -244,21 +244,21 @@ isNotReading:
static void __declspec(naked) CheckHeroExist() {
__asm {
cmp esi, critterArraySize; // check if loading hero art
jle endFunc;
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
test eax, eax;
jnz endFunc;
// if file not found load regular critter art instead
jz notExists;
mov eax, FO_VAR_art_name;
retn;
notExists: // if file not found load regular critter art instead
sub esi, critterArraySize;
add esp, 4; // drop func ret address
mov eax, 0x4194E2;
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 *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
*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;
CritList += 13;
}
return critterArt.total;
}
static void DoubleArtAlias() {
DWORD* crittersAliasData = *(DWORD**)FO_VAR_anon_alias;
std::memcpy(crittersAliasData + critterListSize, crittersAliasData, critterListSize * 4);
}
///////////////////////////////////////////////////////////////GRAPHICS HERO FUNCTIONS///////////////////////////////////////////////////////////////
static void DrawPC() {
@@ -473,8 +478,8 @@ reset: // set race and style to defaults
call LoadHeroDat;
pop ecx;
pop edx;
call fo::funcoffs::proto_dude_update_gender_;
endFunc:
call fo::funcoffs::proto_dude_update_gender_;
mov eax, 1;
retn;
}
@@ -597,8 +602,8 @@ static void DrawCharNote(bool style, int winRef, DWORD xPosWin, DWORD yPosWin, B
textHeight = fo::GetTextHeight();
fo::PrintText(TitleMsg, colour, 0, 0, 265, 280, PadSurface);
// draw line
memset(PadSurface + 280 * textHeight, colour, 265);
memset(PadSurface + 280 * (textHeight + 1), colour, 265);
std::memset(PadSurface + 280 * textHeight, colour, 265);
std::memset(PadSurface + 280 * (textHeight + 1), colour, 265);
}
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_)
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_)
MakeCall(0x418B39, AddHeroCritNames);
+2
View File
@@ -118,6 +118,7 @@ void GetSavePath(char* buf, char* ftype) {
}
static std::string saveSfallDataFailMsg;
static void __stdcall SaveGame2() {
char buf[MAX_PATH];
GetSavePath(buf, "gv");
@@ -168,6 +169,7 @@ errorSave:
}
static std::string saveFailMsg;
static DWORD __stdcall CombatSaveTest() {
if (!saveInCombatFix && !PartyControl::IsNpcControlled()) return 1;
if (inLoop & COMBAT) {
+7 -7
View File
@@ -204,7 +204,7 @@ static bool NormalizePath(std::string &path) {
return true;
}
// Patches placed at the back of the vector will have priority in the chain over the front (previous) patches
// Patches placed at the back of the vector will have priority in the chain over the front(previous) patches
static void GetExtraPatches() {
char patchFile[12] = "PatchFile";
for (int i = 0; i < 100; i++) {
@@ -213,14 +213,14 @@ static void GetExtraPatches() {
if (patch.empty() || !NormalizePath(patch) || GetFileAttributes(patch.c_str()) == INVALID_FILE_ATTRIBUTES) continue;
patchFiles.push_back(patch);
}
std::string searchPath = GetConfigString("ExtraPatches", "AutoSearchPath", "mods\\", MAX_PATH);
if (!searchPath.empty() && NormalizePath(searchPath)) {
if (searchPath.back() != '\\') searchPath += "\\";
std::string searchPath = "mods\\"; //GetConfigString("ExtraPatches", "AutoSearchPath", "mods\\", MAX_PATH);
//if (!searchPath.empty() && NormalizePath(searchPath)) {
//if (searchPath.back() != '\\') searchPath += "\\";
std::string path(".\\" + searchPath + "*.dat");
std::string pathMask(".\\mods\\*.dat");
dlogr("Loading custom patches:", DL_MAIN);
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile(path.c_str(), &findData);
HANDLE hFind = FindFirstFile(pathMask.c_str(), &findData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
std::string name(searchPath + findData.cFileName);
@@ -230,7 +230,7 @@ static void GetExtraPatches() {
} while (FindNextFile(hFind, &findData));
FindClose(hFind);
}
}
//}
// Remove first duplicates
size_t size = patchFiles.size();
for (size_t i = 1; i < size; ++i) {

Some files were not shown because too many files have changed in this diff Show More