diff --git a/artifacts/config_files/Perks.ini b/artifacts/config_files/Perks.ini index 5fb2ce579..9d31295a5 100644 --- a/artifacts/config_files/Perks.ini +++ b/artifacts/config_files/Perks.ini @@ -7,8 +7,8 @@ ; have an additional NoHardcode option in this file which can ; be used to remove their hardcoded effects, and add new stat/skill effects -;Name=The name of the perk -;Desc=The description of the perk +;Name=The name of the perk (max 63 characters) +;Desc=The description of the perk (max 1023 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 diff --git a/artifacts/config_files/elevators.ini b/artifacts/config_files/elevators.ini index 578c8ddd9..b33863e97 100644 --- a/artifacts/config_files/elevators.ini +++ b/artifacts/config_files/elevators.ini @@ -1,11 +1,12 @@ ;Controls the elevators -;Image must match up with the image of an existing elevator +;Image must match up with the image of an existing elevator (can be overrided in sfall 4.1.4/3.8.14 or newer) ;Make sure you specify the correct number of exit targets ;The maximum number of elevators is currently capped at 50 +;Use the line number (0-indexed) of the corresponding FRM in intrface.lst to set the appearance of the elevator ;Override elevator 0 -[000] -;This elevator uses the frm of the original forth elevator +[0] +;This elevator will use the settings of the original forth elevator Image=4 ;Set up the first exit point @@ -23,4 +24,23 @@ ID3=50 Elevation3=0 Tile3=12944 -;No forth exit point \ No newline at end of file +;No forth exit point + +;Override FRM images of elevator 0. Set ButtonsFrm to -1 to use default buttons from MainFrm +MainFrm=143 +ButtonsFrm=-1 + + +;An example of a new elevator +[24] +;Set Image to 24 to create a new elevator type (0-23 are the original elevator types) +Image=24 + +;Set the number of buttons +ButtonCount=3 + +;Set the appearance of the elevator +MainFrm=148 +ButtonsFrm=151 + +;Set up the exit points for all three buttons (see examples above) diff --git a/artifacts/config_files/npcarmor.ini b/artifacts/config_files/npcarmor.ini index dca0babbc..65572995d 100644 --- a/artifacts/config_files/npcarmor.ini +++ b/artifacts/config_files/npcarmor.ini @@ -1,3 +1,15 @@ +;WeaponAnims codes +; 1 - Knife (D) +; 2 - Club (E) +; 3 - Sledgehammer (F) +; 4 - Spear (G) +; 5 - Pistol (H) +; 6 - SMG (I) +; 7 - Rifle (J) +; 8 - Big Gun (K) +; 9 - Minigun (L) +; 10 - Rocket Launcher (M) +; 11-15 - sfall additional weapon animation codes ; This section maps 7 armor types to corresponding armor item PIDs [ArmorTypes] diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index c163da341..910ce8483 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -1,5 +1,5 @@ ;sfall configuration settings -;v4.1.3 +;v4.1.4 [Main] ;Change to 1 if you want to use command line args to tell sfall to use another ini file. @@ -601,6 +601,10 @@ EnableMusicInDialogue=0 ;Set to 1 to prevent the player from running while sneaking without Silent Running perk DontTurnOffSneakIfYouRun=0 +;Changes the distance at which the player will switch to walking when trying to use objects (valid range: 0..3) +;Set to 0 to disable switching. (Default is 3) +UseWalkDistance=3 + ;Set to 1 to fix the bug that unable to sell used geiger counters or stealth boys CanSellUsedGeiger=1 @@ -703,6 +707,9 @@ DontDeleteProtos=0 ;Set to 1 to give scripts direct access to Fallout's address space, and to make arbitrary calls into Fallout's code AllowUnsafeScripting=0 +;Set to 1 to force sfall to search for global scripts every time the game loads rather than only once on the first game start +AlwaysFindScripts=0 + ;Set to 1 to force sfall to inject all hooks code into the game, even if corresponding hook scripts don't exist InjectAllGameHooks=0 diff --git a/artifacts/mods/gl_npcarmor.int b/artifacts/mods/gl_npcarmor.int index c6cfde80a..45aa8ae4c 100644 Binary files a/artifacts/mods/gl_npcarmor.int and b/artifacts/mods/gl_npcarmor.int differ diff --git a/artifacts/mods/gl_npcarmor.ssl b/artifacts/mods/gl_npcarmor.ssl index d6476dd32..548a24850 100644 --- a/artifacts/mods/gl_npcarmor.ssl +++ b/artifacts/mods/gl_npcarmor.ssl @@ -22,7 +22,9 @@ variable modIni := "npcarmor.ini", defaultFids, armorPidMap, // maps armor PID to it's "type" - leather armor, metal, power armor, etc. - npcMap; + npcMap, + altWeapon, + unWieldWeapon; procedure check_armor_change(variable critter, variable item, variable isWorn) begin variable npc, armorType, fid; @@ -49,16 +51,38 @@ procedure check_weapon_change(variable critter, variable item, variable isWield) if isWield then begin newWeaponAnim := get_proto_data(obj_pid(item), PROTO_WP_ANIM); weaponAnimList := string_split(npc["WeaponAnims"], ","); - if newWeaponAnim then begin + if newWeaponAnim then begin // anim code 0 - none/unarmed foreach (i in weaponAnimList) begin if (newWeaponAnim == atoi(i)) then return -1; end - end else begin // anim code 0 - none/unarmed - return -1; + return 0; end + end else begin + unWieldWeapon := obj_pid(item); end end - return 0; + return -1; +end + +procedure search_alt_weapon(variable critter) begin + variable obj, res, i := 0; + obj := inven_ptr(critter, 0); + while (obj) do begin + if (obj_item_subtype(obj) == item_type_weapon) then begin + if (unWieldWeapon == 0 or unWieldWeapon != obj_pid(obj)) then begin + res := check_weapon_change(critter, obj, 1); + if (res == -1) then begin + altWeapon := obj; + wield_obj_critter(critter, obj); + altWeapon := 0; + break; + end + end + end + i++; + obj := inven_ptr(critter, i); + end + unWieldWeapon := 0; end // for NPCs when they change armor/weapon themselves @@ -74,11 +98,16 @@ procedure invenwield_handler begin if (fid != -1) then begin art_change_fid_num(critter, fid); end + return; end - if (critter and item and (slot == INVEN_TYPE_RIGHT_HAND or slot == INVEN_TYPE_LEFT_HAND)) then begin + if (critter and item and slot == INVEN_TYPE_RIGHT_HAND) then begin + if (altWeapon == item) then return; canWield := check_weapon_change(critter, item, isWorn); set_sfall_return(canWield); + if (canWield != -1) then begin + call search_alt_weapon(critter); + end end end diff --git a/artifacts/scripting/arrays.txt b/artifacts/scripting/arrays.txt index 482e27a6e..14542a0e7 100644 --- a/artifacts/scripting/arrays.txt +++ b/artifacts/scripting/arrays.txt @@ -9,21 +9,21 @@ Array elements are accessed by index or key. For example: // this code puts some string in array "list" at index 5: list[5] := "Value"; - + There are 2 different types of arrays currently available: 1) Lists - a set of values with specific size (number of elements), where all elements have numeric indexes starting from zero (0) up to array length minus one. For example: - + // this creates list with 3 elements. Element "A" has index 0, element "B" has index 1, element "C" - 2 list := ["A", "B", "C"]; - + Limitations: - all indexes are numeric, starting from 0; - to assign value to a specific index, you must first resize array to contain this index (for example, if list is of size 3 (indexes from 0 to 2), you can't assign value to index 4 unless you change list size to 5 first). - + 2) Maps (or associative arrays) - a set of key=>value pairs, where all elements (values) are accessed by corresponding keys. Differences from list: - maps don't have specific size (to assign values, you don't need to resize array first); @@ -37,24 +37,24 @@ Both array types have their pros and cons and are suited for different tasks. Basically arrays are implemented using number of new operators (scripting functions). But for ease of use, there are some new syntax elements: 1) Accessing elements. Use square brackets: - + display_msg(arr[5]); mymap["price"] := 515.23; - + 2) Alternative accessing for maps. Use dot: display_msg(mymap.name); mymap.price := 232.23; - + 3) Array expressions. Create and fill arrays with just one expression: // create list with 5 values [5, 777, 0, 3.14, "Cool Value"] - + // create map: {5: "Five", "health": 50, "speed": 0.252} - -NOTES: + +NOTES: - make sure to call "fix_array" if you want new array to be available in the next frame or "save_array" if you want to use it for a longer period (see next section for details) @@ -64,16 +64,16 @@ NOTES: foreach (item in myarray) begin // this block is executed for each array element, where "item" contains current value on each step end - + // alternative syntax: foreach (key: item in myarray) begin // "key" will contain current key (or numeric index, for lists) end See "Script editor\docs\sslc readme.txt" file for full information on new SSL syntax features. - + >>> STORING ARRAYS <<< - + Apart from lists/maps arrays are divided by how they are stored. There a 3 types of arrays: @@ -85,21 +85,21 @@ where you create temp_array, it will not be available next time your global scri This type of arrays are always available (by their ID) until you start a new game or load a saved game (at which point they are deleted). 3) Saved. If you want your array to really stay for a while, use function "save_array" to make any array "saved". However, they are, like permanent arrays, -"deleted" from memory when loading game. In order to use them properly, you must load them from the savegame using "load_array" whenever you want to use them. +"deleted" from memory when loading game. In order to use them properly, you must load them from the savegame using "load_array" whenever you want to use them. Example: - + variable savedArray; procedure start begin if game_loaded then begin savedArray := load_array("traps"); end else begin foreach trap in traps begin - .... + .... end end end - - + + >>> PRACTICAL EXAMPLES <<< @@ -114,8 +114,8 @@ Example: // call it: call give_item(dude_obj, {PID_SHOTGUN: 1, PID_SHOTGUN_SHELLS: 4, PID_STIMPAK: 3}); - - + + > Create arrays of objects (maps) for advanced scripting: variable traps; @@ -130,14 +130,14 @@ Example: traps[k] := load_array("trap_"+k); // each object is stored separately end end - + procedure add_trap(variable trapArray) begin variable index; index := len_array(traps); save_array("trap_"+k, trapArray); array_push(traps, trapArray); end - + // use them: foreach trap in traps begin if (self_elevation == trap["elev"] and tile_distance(self_tile, trap["tile"]) < trap["radius"]) then @@ -151,14 +151,16 @@ Example: *mixed means any type -> int create_array(int size, int nothing): +> int create_array(int size, int flags): - creates permanent array (but not "saved") - if size is >= 0, creates list with given size - if size == -1, creates map (associative array) -- second argument is not used yet, just use 0 +- if size == -1 and flags == 2, creates a "lookup" map in which the values of existing keys are read-only and can't be updated. +This type of array allows you to store a zero (0) key value +- NOTE: in earlier versions (up to 4.1.3/3.8.13) the second argument is not used, just use 0 - returns arrayID (valid until array is deleted) -> int temp_array(int size, int nothing): +> int temp_array(int size, int flags): - works exactly like "create_array", only created array becomes "temporary" > void fix_array(int arrayID): @@ -169,6 +171,7 @@ Example: - if used on list, "key" must be numeric and within valid index range (0..size-1) - if used on map, key can be of any type - to "unset" a value from map, just set it to zero (0) +- NOTE: to add a value of 0 for the key, use the float value of 0.0 - this works exactly like statement: arrayID[key] := value; @@ -182,7 +185,7 @@ Example: - changes array size - applicable to maps too, but only to reduce elements - there are number of special negative values of "size" which perform various operations on the array, -use macros sort_array, sort_array_reverse, reverse_array, shuffle_array from sfall.h header +use macros sort_array, sort_array_reverse, reverse_array, shuffle_array from sfall.h header > void free_array(int arrayID): - deletes any array diff --git a/artifacts/scripting/headers/lib.arrays.h b/artifacts/scripting/headers/lib.arrays.h index c3bdcdbc7..8bee30d89 100644 --- a/artifacts/scripting/headers/lib.arrays.h +++ b/artifacts/scripting/headers/lib.arrays.h @@ -20,6 +20,9 @@ /* Generic array functions */ +// returns True if the specified key exists in the associative array +procedure map_contains_key(variable arrayMap, variable key); + // push new item at the end of array, returns array procedure array_push(variable array, variable item); @@ -151,6 +154,14 @@ procedure load_collection(variable name); #define ARRAY_SET_BLOCK_SIZE (10) +procedure map_contains_key(variable arrayMap, variable key) begin + variable i; + for (i := 0; i < len_array(arrayMap); i++) begin + if (array_key(arrayMap, i) == key) then return true; + end + return false; +end + /** * Returns first index of zero value */ diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index f112e6f13..7594f33fb 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -59,6 +59,7 @@ #define HOOK_USESKILLON (35) #define HOOK_ONEXPLOSION (36) #define HOOK_SUBCOMBATDAMAGE (37) +#define HOOK_SETLIGHTING (38) //Valid arguments to list_begin #define LIST_CRITTERS (0) @@ -116,6 +117,10 @@ #define create_array_map (create_array(-1, 0)) // create temporary map #define temp_array_map (temp_array(-1, 0)) +// create persistent lookup map (see arrays.txt for details) +#define create_lookup_map (create_array(-1, 2)) +// create temporary lookup map +#define temp_lookup_map (temp_array(-1, 2)) // true if array is map, false otherwise #define array_is_map(x) (array_key(x, -1) == 1) // returns temp list with names of all arrays saved with save_array() in alphabetical order @@ -128,14 +133,18 @@ #define array_exists(array) (len_array(array) != -1) // remove all elements from array #define clear_array(array) resize_array(array, 0) -// sort array in ascending order +// sort array or map by key in ascending order #define sort_array(array) resize_array(array, -2) -// sort array in descending order +// sort array or map by key in descending order #define sort_array_reverse(array) resize_array(array, -3) -// reverse elements in list +// reverse elements in list/map #define reverse_array(array) resize_array(array, -4) -// randomly shuffle elements in list +// randomly shuffle elements in list/map #define shuffle_array(array) resize_array(array, -5) +// sort map in ascending order by value +#define sort_map_value(array) resize_array(array, -6) +// sort map in descending order by value +#define sort_map_value_desc(array) resize_array(array, -7) // remove element from map or just replace value with 0 for list #define unset_array(array, item) set_array(array, item, 0) // same as "key_pressed" but checks VK codes instead of DX codes diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index e6b544812..e60fcc1d7 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -603,3 +603,16 @@ int arg11 - The calculated amount of damage (usually 0, required when using mixed arg12 - Computed attack results (see C_ATTACK_* for offsets and use get/set_object_data functions to get/set the data) int ret1 - The returned amount of damage + +------------------------------------------- + +HOOK_SETLIGHTING (hs_setlighting.int) + +Runs before setting the light level for an object or a map. You can override the result. + +Obj arg1 - the object being set, or -1 when setting the light level for a map +int arg2 - the light intensity +int arg3 - the light radius, or -1 when setting the light level for a map + +int ret1 - overrides the light intensity. Intensity range is from 0 to 65536 +int ret2 - overrides the light radius. Radius range is from 0 to 8 (works only for the object) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 43238f0d1..23262aebf 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -141,7 +141,7 @@ array - array ID to be used with array-related functions (actually an integer) - In this case use force_encounter_with_flags with the ENCOUNTER_FLAG_NO_CAR flag set. > int get_light_level() -- ambient light level in range 0..65535 +- ambient light level in range 0..65536 - The value returned by get_light_level may not exactly match that set by set_light_level, as set_light_level applies modifiers from the night vision perk. > void set_map_time_multi(float multi) @@ -325,7 +325,7 @@ Some utility/math functions are available: - this calls an internal engine function which is used to determine the perception range of critters (which you can override using HOOK_WITHINPERCEPTION) > int tile_light(int elevation, int tileNum) -- returns light intensity at the given tile in range from 0 to 65535 +- returns light intensity at the given tile in range from 0 to 65536 > object obj_blocking_line(object objFrom, int tileTo, int blockingType) - returns first object which blocks direct linear path from objFrom to tileTo using selected blocking function (see BLOCKING_TYPE_* constants in sfall.h) diff --git a/artifacts/scripting/sfall opcode list.txt b/artifacts/scripting/sfall opcode list.txt index 4f4c0b042..8dd6e5c07 100644 --- a/artifacts/scripting/sfall opcode list.txt +++ b/artifacts/scripting/sfall opcode list.txt @@ -132,13 +132,13 @@ 0x819d - void set_sfall_global(string/int varname, int/float value) 0x819e - int get_sfall_global_int(string/int varname) 0x819f - float get_sfall_global_float(string/int varname) -0x822d - int create_array(int elementcount, int elementsize) +0x822d - int create_array(int elementcount, int flags) 0x822e - void set_array(int array, any element, any value) 0x822f - any get_array(int array, any element) 0x8230 - void free_array(int array) 0x8231 - int len_array(int array) 0x8232 - void resize_array(int array, int newelementcount) -0x8233 - int temp_array(int elementcount, int elementsize) +0x8233 - int temp_array(int elementcount, int flags) 0x8234 - void fix_array(int array) 0x8239 - int scan_array(int array, int/float var) 0x8256 - int array_key(int array, int index) diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index cedd049e1..316e654b0 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -44,7 +44,7 @@ namespace func WRAP_WATCOM_CALL2(offs, arg1, arg2) #define WRAP_WATCOM_CALL4(offs, arg1, arg2, arg3, arg4) \ - __asm mov ecx, arg4 \ + __asm mov ecx, arg4 \ WRAP_WATCOM_CALL3(offs, arg1, arg2, arg3) #define WRAP_WATCOM_CALL5(offs, arg1, arg2, arg3, arg4, arg5) \ @@ -59,19 +59,41 @@ namespace func __asm push arg7 \ WRAP_WATCOM_CALL6(offs, arg1, arg2, arg3, arg4, arg5, arg6) +// defines wrappers for __fastcall +#define WRAP_WATCOM_FCALL1(offs, arg1) \ + __asm mov eax, ecx \ + WRAP_WATCOM_CALL0(offs) + +#define WRAP_WATCOM_FCALL2(offs, arg1, arg2) \ + WRAP_WATCOM_FCALL1(offs, arg1) + +#define WRAP_WATCOM_FCALL3(offs, arg1, arg2, arg3) \ + __asm mov ebx, arg3 \ + WRAP_WATCOM_FCALL1(offs, arg1) + +#define WRAP_WATCOM_FCALL4(offs, arg1, arg2, arg3, arg4) \ + __asm mov eax, ecx \ + __asm mov ebx, arg3 \ + __asm mov ecx, arg4 \ + WRAP_WATCOM_CALL0(offs) + +#define WRAP_WATCOM_FCALL5(offs, arg1, arg2, arg3, arg4, arg5) \ + __asm push arg5 \ + WRAP_WATCOM_FCALL4(offs, arg1, arg2, arg3, arg4) + +#define WRAP_WATCOM_FCALL6(offs, arg1, arg2, arg3, arg4, arg5, arg6) \ + __asm push arg6 \ + WRAP_WATCOM_FCALL5(offs, arg1, arg2, arg3, arg4, arg5) + +#define WRAP_WATCOM_FCALL7(offs, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + __asm push arg7 \ + WRAP_WATCOM_FCALL6(offs, arg1, arg2, arg3, arg4, arg5, arg6) + + bool __stdcall art_exists(long artFid) { WRAP_WATCOM_CALL1(art_exists_, artFid) } -long __fastcall _word_wrap(const char* text, int maxWidth, DWORD* buf, BYTE* count) { - __asm { - mov eax, ecx; - mov ecx, count; - mov ebx, buf; - call fo::funcoffs::_word_wrap_; - } -} - // Returns the name of the critter const char* __stdcall critter_name(GameObject* critter) { WRAP_WATCOM_CALL1(critter_name_, critter) @@ -138,6 +160,11 @@ long __stdcall db_get_file_list(const char* searchMask, char* * *fileList) { WRAP_WATCOM_CALL2(db_get_file_list_, searchMask, fileList) } +// Check fallout paths for file +long __stdcall CheckFile(char *fileName, DWORD *sizeOut) { + WRAP_WATCOM_CALL2(db_dir_entry_, fileName, sizeOut) +} + // prints message to debug.log file void __declspec(naked) debug_printf(const char* fmt, ...) { __asm jmp fo::funcoffs::debug_printf_ @@ -279,6 +306,14 @@ long __stdcall message_exit(MessageList *msgList) { WRAP_WATCOM_CALL1(message_exit_, msgList) } +GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func) { + __asm { + mov eax, ecx; + mov ebx, elevation; + call func; + } +} + GameObject* __stdcall obj_find_first_at_tile(long elevation, long tileNum) { WRAP_WATCOM_CALL2(obj_find_first_at_tile_, elevation, tileNum) } @@ -358,7 +393,7 @@ void __stdcall DialogOut(const char* text) { xor eax, eax; push eax; // ColorMsg push eax; // DisplayMsg - mov al, ds:[0x6AB718]; + mov al, byte ptr ds:[0x6AB718]; push eax; // ColorIndex push 0x74; // y mov ecx, 0xC0; // x @@ -369,6 +404,20 @@ void __stdcall DialogOut(const char* text) { } } +void __fastcall DrawWinLine(int winRef, DWORD startXPos, DWORD endXPos, DWORD startYPos, DWORD endYPos, BYTE colour) { + __asm { + xor eax, eax; + mov al, colour; + push eax; + push endYPos; + mov eax, ecx; // winRef + mov ecx, endXPos; + mov ebx, startYPos; + //mov edx, xStartPos; + call fo::funcoffs::win_line_; + } +} + #define WRAP_WATCOM_FUNC0(retType, name) \ retType __stdcall name() { \ @@ -405,9 +454,40 @@ void __stdcall DialogOut(const char* text) { WRAP_WATCOM_CALL6(name##_, arg1, arg2, arg3, arg4, arg5, arg6) \ } -#define WRAP_WATCOM_FUNC7(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5, arg6t, arg6, arg7t, arg7) \ - retType __stdcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5, arg6t arg6, arg7t arg7) { \ - WRAP_WATCOM_CALL7(name##_, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + +#define WRAP_WATCOM_FFUNC1(retType, name, arg1t, arg1) \ + retType __fastcall name(arg1t arg1) { \ + WRAP_WATCOM_FCALL1(name##_, arg1) \ + } + +#define WRAP_WATCOM_FFUNC2(retType, name, arg1t, arg1, arg2t, arg2) \ + retType __fastcall name(arg1t arg1, arg2t arg2) { \ + WRAP_WATCOM_FCALL2(name##_, arg1, arg2) \ + } + +#define WRAP_WATCOM_FFUNC3(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3) { \ + WRAP_WATCOM_FCALL3(name##_, arg1, arg2, arg3) \ + } + +#define WRAP_WATCOM_FFUNC4(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4) { \ + WRAP_WATCOM_FCALL4(name##_, arg1, arg2, arg3, arg4) \ + } + +#define WRAP_WATCOM_FFUNC5(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5) { \ + WRAP_WATCOM_FCALL5(name##_, arg1, arg2, arg3, arg4, arg5) \ + } + +#define WRAP_WATCOM_FFUNC6(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5, arg6t, arg6) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5, arg6t arg6) { \ + WRAP_WATCOM_FCALL6(name##_, arg1, arg2, arg3, arg4, arg5, arg6) \ + } + +#define WRAP_WATCOM_FFUNC7(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5, arg6t, arg6, arg7t, arg7) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5, arg6t arg6, arg7t arg7) { \ + WRAP_WATCOM_FCALL7(name##_, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ } #include "Functions_def.h" diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index 7829352af..91f0e9be8 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -36,8 +36,6 @@ namespace func bool __stdcall art_exists(long artFid); -long __fastcall _word_wrap(const char* text, int maxWidth, DWORD* buf, BYTE* count); - // Returns the name of the critter const char* __stdcall critter_name(GameObject* critter); @@ -162,6 +160,8 @@ long __stdcall message_load(MessageList *msgList, const char *msgFilePath); // destroys message list long __stdcall message_exit(MessageList *msgList); +GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func); + GameObject* __stdcall obj_find_first_at_tile(long elevation, long tileNum); GameObject* __stdcall obj_find_next_at_tile(); @@ -221,8 +221,27 @@ void __stdcall DialogOut(const char* text); #define WRAP_WATCOM_FUNC6(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5, arg6t, arg6) \ retType __stdcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5, arg6t arg6); -#define WRAP_WATCOM_FUNC7(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5, arg6t, arg6, arg7t, arg7) \ - retType __stdcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5, arg6t arg6, arg7t arg7); + +#define WRAP_WATCOM_FFUNC1(retType, name, arg1t, arg1) \ + retType __fastcall name(arg1t arg1); + +#define WRAP_WATCOM_FFUNC2(retType, name, arg1t, arg1, arg2t, arg2) \ + retType __fastcall name(arg1t arg1, arg2t arg2); + +#define WRAP_WATCOM_FFUNC3(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3); + +#define WRAP_WATCOM_FFUNC4(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4); + +#define WRAP_WATCOM_FFUNC5(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5); + +#define WRAP_WATCOM_FFUNC6(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5, arg6t, arg6) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5, arg6t arg6); + +#define WRAP_WATCOM_FFUNC7(retType, name, arg1t, arg1, arg2t, arg2, arg3t, arg3, arg4t, arg4, arg5t, arg5, arg6t, arg6, arg7t, arg7) \ + retType __fastcall name(arg1t arg1, arg2t arg2, arg3t arg3, arg4t arg4, arg5t arg5, arg6t arg6, arg7t arg7); #include "Functions_def.h" @@ -233,7 +252,15 @@ void __stdcall DialogOut(const char* text); #undef WRAP_WATCOM_FUNC4 #undef WRAP_WATCOM_FUNC5 #undef WRAP_WATCOM_FUNC6 -#undef WRAP_WATCOM_FUNC7 +//#undef WRAP_WATCOM_FUNC7 + +#undef WRAP_WATCOM_FFUNC1 +#undef WRAP_WATCOM_FFUNC2 +#undef WRAP_WATCOM_FFUNC3 +#undef WRAP_WATCOM_FFUNC4 +#undef WRAP_WATCOM_FFUNC5 +#undef WRAP_WATCOM_FFUNC6 +#undef WRAP_WATCOM_FFUNC7 } } diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index cfcb185ff..9738254d0 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -14,6 +14,14 @@ NOTES: be careful not to use reserved words, including ASM instructions (push, pop, mov, div, etc.) */ +// For functions that have 3 or more arguments, it is preferable to use the fastcall calling convention +// because the compiler builds the better/optimized code when calling the engine functions +WRAP_WATCOM_FFUNC4(long, _word_wrap, const char*, text, int, maxWidth, DWORD*, buf, BYTE*, count) +WRAP_WATCOM_FFUNC7(long, createWindow, const char*, winName, long, x, long, y, long, width, long, height, long, bgColorIndex, long, flags) +WRAP_WATCOM_FFUNC7(void, make_straight_path_func, fo::GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) +WRAP_WATCOM_FFUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation) + +// stdcall WRAP_WATCOM_FUNC1(AIcap*, ai_cap, GameObject*, critter) WRAP_WATCOM_FUNC1(Program*, allocateProgram, const char*, filePath) WRAP_WATCOM_FUNC0(void, art_flush) @@ -26,7 +34,6 @@ WRAP_WATCOM_FUNC4(BYTE*, art_ptr_lock_data, long, frmId, long, frameNum, long, r WRAP_WATCOM_FUNC4(BYTE*, art_lock, long, frmId, DWORD*, lockPtr, long*, widthOut, long*, heightOut) WRAP_WATCOM_FUNC1(long, art_ptr_unlock, DWORD, lockId) WRAP_WATCOM_FUNC2(long, barter_compute_value, GameObject*, source, GameObject*, target) -WRAP_WATCOM_FUNC7(long, createWindow, const char*, winName, long, x, long, y, long, width, long, height, long, bgColorIndex, long, flags) WRAP_WATCOM_FUNC1(void*, dbase_open, const char*, fileName) WRAP_WATCOM_FUNC1(void, dbase_close, void*, dbPtr) WRAP_WATCOM_FUNC3(long, db_freadShortCount, DbFile*, file, WORD*, dest, long, count) @@ -40,6 +47,7 @@ WRAP_WATCOM_FUNC4(void, display_target_inventory, long, inventoryOffset, long, v WRAP_WATCOM_FUNC2(long, combat_turn, GameObject*, critter, long, isDudeTurn) WRAP_WATCOM_FUNC1(long, critter_is_dead, GameObject*, critter) WRAP_WATCOM_FUNC1(void, EndLoad, DbFile*, file) +WRAP_WATCOM_FUNC1(long, folder_print_line, const char*, text) WRAP_WATCOM_FUNC1(long, game_get_global_var, long, globalVar) WRAP_WATCOM_FUNC1(void, gdialogDisplayMsg, const char*, message) WRAP_WATCOM_FUNC1(long, gmouse_3d_set_mode, long, mode) @@ -78,8 +86,8 @@ WRAP_WATCOM_FUNC2(long, obj_pid_new, fo::GameObject*, object, long, pid) // checks/unjams jammed locks WRAP_WATCOM_FUNC1(long, obj_lock_is_jammed, GameObject*, object) WRAP_WATCOM_FUNC1(void, obj_unjam_lock, GameObject*, object) -WRAP_WATCOM_FUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation) WRAP_WATCOM_FUNC6(long, pick_death, GameObject*, attacker, GameObject*, target, GameObject*, weapon, long, amount, long, anim, long, hitFromBack) +WRAP_WATCOM_FUNC0(void, proto_dude_update_gender) WRAP_WATCOM_FUNC2(long, queue_find_first, GameObject*, object, long, qType) WRAP_WATCOM_FUNC3(long, register_object_animate, GameObject*, object, long, anim, long, delay) WRAP_WATCOM_FUNC3(long, register_object_animate_and_hide, GameObject*, object, long, anim, long, delay) @@ -105,13 +113,15 @@ WRAP_WATCOM_FUNC1(long, register_object_must_erase, GameObject*, object) // WRAP_WATCOM_FUNC3(long, register_object_run_to_tile_, GameObject*, object; WRAP_WATCOM_FUNC3(long, register_object_take_out, GameObject*, object, long, holdFrameId, long, nothing) WRAP_WATCOM_FUNC3(long, register_object_turn_towards, GameObject*, object, long, tileNum, long, nothing) +WRAP_WATCOM_FUNC2(long, stat_get_base_direct, GameObject*, critter, long, statID) // adds experience points to PC WRAP_WATCOM_FUNC1(void, stat_pc_add_experience, long, amount) +WRAP_WATCOM_FUNC1(long, text_font, long, fontNum) // redraws the whole screen WRAP_WATCOM_FUNC0(void, tile_refresh_display) // redraws the given rectangle on screen WRAP_WATCOM_FUNC2(void, tile_refresh_rect, BoundRect*, boundRect, long, elevation) -WRAP_WATCOM_FUNC1(long, text_font, long, fontNum) +WRAP_WATCOM_FUNC1(long, trait_level, long, traitID) WRAP_WATCOM_FUNC6(DWORD, win_add, long, x, long, y, long, width, long, height, long, bgColorIndex, long, flags) WRAP_WATCOM_FUNC1(void, win_show, DWORD, winRef) WRAP_WATCOM_FUNC1(void, win_hide, DWORD, winRef) diff --git a/sfall/FalloutEngine/Structs.h b/sfall/FalloutEngine/Structs.h index dcc6ff4ba..4c813873e 100644 --- a/sfall/FalloutEngine/Structs.h +++ b/sfall/FalloutEngine/Structs.h @@ -260,6 +260,12 @@ struct ElevatorExit { long tile; }; +#pragma pack(1) +struct ElevatorFrms { + DWORD main; + DWORD buttons; +}; + #pragma pack(1) struct FrmFile { long id; //0x00 diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 23c280ebb..346a88b4a 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -20,7 +20,6 @@ #define FO_VAR_bboxslot 0x5970E0 #define FO_VAR_bckgnd 0x5707A4 #define FO_VAR_black_palette 0x663FD0 -#define FO_VAR_BlueColor 0x6A38EF #define FO_VAR_bottom_line 0x664524 #define FO_VAR_btable 0x59E944 #define FO_VAR_btncnt 0x43EA1C @@ -42,7 +41,6 @@ #define FO_VAR_curr_pc_stat 0x6681AC #define FO_VAR_curr_stack 0x59E96C #define FO_VAR_cursor_line 0x664514 -#define FO_VAR_DarkGreenColor 0x6A3A90 #define FO_VAR_dialogue_state 0x518714 #define FO_VAR_dialogue_switch_mode 0x518718 #define FO_VAR_dialog_target 0x518848 @@ -61,6 +59,7 @@ #define FO_VAR_folder_card_title2 0x5705BC #define FO_VAR_frame_time 0x5709C4 #define FO_VAR_free_perk 0x570A29 +#define FO_VAR_frstc_draw1 0x5707D8 #define FO_VAR_game_global_vars 0x5186C0 #define FO_VAR_game_user_wants_to_quit 0x5186CC #define FO_VAR_gcsd 0x51094C @@ -72,8 +71,6 @@ #define FO_VAR_gmouse_current_cursor 0x518C0C #define FO_VAR_gmovie_played_list 0x596C78 #define FO_VAR_GNW_win_init_flag 0x51E3E0 -#define FO_VAR_GoodColor 0x6AB4EF -#define FO_VAR_GreenColor 0x6A3CB0 #define FO_VAR_gsound_initialized 0x518E30 #define FO_VAR_hit_location_penalty 0x510954 #define FO_VAR_holo_flag 0x664529 @@ -132,6 +129,7 @@ #define FO_VAR_name_sort_list 0x56FCB0 #define FO_VAR_num_game_global_vars 0x5186C4 #define FO_VAR_num_map_global_vars 0x519574 +#define FO_VAR_card_old_fid1 0x5709EC #define FO_VAR_obj_dude 0x6610B8 #define FO_VAR_objectTable 0x639DA0 #define FO_VAR_objItemOutlineState 0x519798 @@ -157,7 +155,6 @@ #define FO_VAR_pc_proto 0x51C370 #define FO_VAR_pc_trait 0x66BE40 #define FO_VAR_pc_trait2 0x66BE44 -#define FO_VAR_PeanutButter 0x6A82F3 #define FO_VAR_perk_data 0x519DCC #define FO_VAR_perkLevelDataList 0x51C120 #define FO_VAR_pip_win 0x6644C4 @@ -173,7 +170,6 @@ #define FO_VAR_queue 0x6648C0 #define FO_VAR_quick_done 0x5193BC #define FO_VAR_read_callback 0x51DEEC -#define FO_VAR_RedColor 0x6AB4D0 #define FO_VAR_retvals 0x43EA7C #define FO_VAR_rotation 0x631D34 #define FO_VAR_sad 0x530014 @@ -214,7 +210,6 @@ #define FO_VAR_trait_data 0x51DB84 #define FO_VAR_view_page 0x664520 #define FO_VAR_wd_obj 0x59E98C -#define FO_VAR_WhiteColor 0x6AB8CF #define FO_VAR_wmAreaInfoList 0x51DDF8 #define FO_VAR_wmLastRndTime 0x51DEA0 #define FO_VAR_wmWorldOffsetX 0x51DE2C @@ -222,4 +217,13 @@ #define FO_VAR_world_xpos 0x672E0C #define FO_VAR_world_ypos 0x672E10 #define FO_VAR_WorldMapCurrArea 0x672E08 + +// colors +#define FO_VAR_BlueColor 0x6A38EF +#define FO_VAR_DarkGreenColor 0x6A3A90 +#define FO_VAR_GoodColor 0x6AB4EF +#define FO_VAR_GreenColor 0x6A3CB0 +#define FO_VAR_PeanutButter 0x6A82F3 +#define FO_VAR_RedColor 0x6AB4D0 +#define FO_VAR_WhiteColor 0x6AB8CF #define FO_VAR_YellowColor 0x6AB8BB diff --git a/sfall/Modules/AnimationsAtOnceLimit.cpp b/sfall/Modules/AnimationsAtOnceLimit.cpp index 917253b88..5882e09ff 100644 --- a/sfall/Modules/AnimationsAtOnceLimit.cpp +++ b/sfall/Modules/AnimationsAtOnceLimit.cpp @@ -171,7 +171,7 @@ static void __declspec(naked) anim_set_end_hack() { __asm { call AnimCombatFix; mov [eax][esi], ebx; - push 0x415DF2; + xor dl, dl; // goto 0x415DF2; retn; } } @@ -229,6 +229,8 @@ void ApplyAnimationsAtOncePatches(signed char aniMax) { //divert old animation structure list pointers to newly allocated memory + //struct array 1/////////////////// + //old addr 0x54C1B4 SafeWriteBatch(animSetAddr, { 0x413A9E }); @@ -315,7 +317,7 @@ void AnimationsAtOnce::init() { dlogr(" Done", DL_INIT); } // Fix for calling anim() functions in combat - MakeJump(0x415DE2, anim_set_end_hack); + MakeCall(0x415DE2, anim_set_end_hack, 1); // Fix crash when the critter goes through a door with animation trigger MakeJump(0x41755E, object_move_hack); diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 3330de633..a2d798701 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1730,6 +1730,17 @@ static void __declspec(naked) config_get_values_hack() { } } +static void __declspec(naked) db_freadInt_hook() { + __asm { + call fo::funcoffs::xfread_; + test eax, eax; + jnz skip; + dec eax; +skip: + retn; + } +} + void BugFixes::init() { @@ -2188,6 +2199,9 @@ void BugFixes::init() // Fix for config_get_values_ engine function not getting the last value in a list if the list has less than the requested // number of values (for chem_primary_desire) MakeJump(0x42C12C, config_get_values_hack); + + // Fix returned result value + HookCall(0x4C6162, db_freadInt_hook); // TODO: Resolve conflict in FileSystem.cpp } } diff --git a/sfall/Modules/BurstMods.cpp b/sfall/Modules/BurstMods.cpp index 63d4980d9..790b878eb 100644 --- a/sfall/Modules/BurstMods.cpp +++ b/sfall/Modules/BurstMods.cpp @@ -24,50 +24,45 @@ namespace sfall { -static DWORD compute_spray_center_mult; -static DWORD compute_spray_center_div; -static DWORD compute_spray_target_mult; -static DWORD compute_spray_target_div; +static long compute_spray_center_mult; +static long compute_spray_center_div; +static long compute_spray_target_mult; +static long compute_spray_target_div; + +static long __fastcall ComputeSpray(DWORD* roundsLeftOut, DWORD* roundsRightOut, DWORD totalRounds, DWORD* roundsCenterOut) { + // roundsCenter = totalRounds * mult / div + long result = totalRounds * compute_spray_center_mult; + long roundsCenter = result / compute_spray_center_div; + if (result % compute_spray_center_div) roundsCenter++; // if remainder then round up + + if (roundsCenter == 0) roundsCenter = 1; + *roundsCenterOut = roundsCenter; + + long roundsLeft = (totalRounds - roundsCenter) / 2; + *roundsLeftOut = roundsLeft; + *roundsRightOut = totalRounds - roundsCenter - roundsLeft; + + // roundsMainTarget = roundsCenter * mult / div + result = roundsCenter * compute_spray_target_mult; + long roundsMainTarget = result / compute_spray_target_div; + + return (result % compute_spray_target_div) ? ++roundsMainTarget : roundsMainTarget; // if remainder then round up +} static const DWORD compute_spray_rounds_back = 0x42353A; static void __declspec(naked) compute_spray_rounds_distribution() { __asm { - // ebp - totalRounds - mov eax, ebp; // roundsCenter = totalRounds * mult / div - mov ebx, compute_spray_center_mult; - imul ebx; // multiply eax by ebx and store result in edx:eax - mov ebx, compute_spray_center_div; // divisor - idiv ebx; // divide edx:eax by ebx and store result in eax (edx) - test edx, edx; // if remainder (edx) is not 0 - jz divEnd1; - inc eax; // round up -divEnd1: - mov [esp+16], eax; // roundsCenter - test eax, eax; // if (roundsCenter == 0) - jnz loc_42350F; - mov [esp+16], 1; // roundsCenter = 1; -loc_42350F: - mov eax, ebp; // roundsLeft = (totalRounds - roundsCenter) / 2 - sub eax, [esp+16]; // - roundsCenter - sar eax, 1; // /2 - mov [esp+8], eax; // roundsLeft - mov eax, ebp; // roundsRight = totalRounds - roundsCenter - roundsLeft - sub eax, [esp+16]; - sub eax, [esp+8]; - mov [esp+4], eax; // roundsRight - mov eax, [esp+16]; // roundsMainTarget = roundsCenter * mult / div - mov ebx, compute_spray_target_mult; - imul ebx; - mov ebx, compute_spray_target_div; - idiv ebx; - test edx, edx; // if remainder (edx) is not 0 - jz divEnd2; - inc eax; // round up -divEnd2: - mov ebp, eax; - mov ebx, [esp+16]; - // at this point, eax should contain the same value as ebp (roundsMainTarget); ebx should contain value of roundsCenter - jmp compute_spray_rounds_back; + push ecx; + lea ecx, [esp + 8 + 4]; // roundsLeft - out + lea edx, [esp + 4 + 4]; // roundsRight - out + lea eax, [esp + 16 + 4]; + push eax; // roundsCenter - out + push ebp; // totalRounds + call ComputeSpray; + pop ecx; + mov ebp, eax; + mov ebx, [esp + 16]; + jmp compute_spray_rounds_back; // at this point, eax should contain the same value as ebp (roundsMainTarget); ebx should contain value of roundsCenter } } diff --git a/sfall/Modules/Knockback.cpp b/sfall/Modules/Combat.cpp similarity index 74% rename from sfall/Modules/Knockback.cpp rename to sfall/Modules/Combat.cpp index 32549c7e4..9d6bcac36 100644 --- a/sfall/Modules/Knockback.cpp +++ b/sfall/Modules/Combat.cpp @@ -22,16 +22,17 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" #include "LoadGameHook.h" +#include "Objects.h" -#include "Knockback.h" +#include "Combat.h" namespace sfall { -static std::vector noBursts; +static std::vector noBursts; // object id struct KnockbackModifier { - fo::GameObject* id; + long id; DWORD type; double value; }; @@ -40,26 +41,17 @@ static std::vector mTargets; static std::vector mAttackers; static std::vector mWeapons; -struct ChanceModifier { - fo::GameObject* id; - int maximum { 95 }; - int mod { 0 }; -}; - static std::vector hitChanceMods; -static std::vector pickpocketMods; - static ChanceModifier baseHitChance; -static ChanceModifier basePickpocket; static bool hookedAimedShot; static std::vector disabledAS; static std::vector forcedAS; -static double ApplyModifiers(std::vector* mods, fo::GameObject* id, double val) { +static double ApplyModifiers(std::vector* mods, fo::GameObject* object, double val) { for (DWORD i = 0; i < mods->size(); i++) { KnockbackModifier* mod = &(*mods)[i]; - if (mod->id == id) { + if (mod->id == object->id) { switch (mod->type) { case 0: val = mod->value; @@ -111,31 +103,9 @@ static void __declspec(naked) compute_dmg_damage_hack() { } } -static int __fastcall PickpocketMod(int base, fo::GameObject* critter) { - for (DWORD i = 0; i < pickpocketMods.size(); i++) { - if (critter == pickpocketMods[i].id) { - return min(base + pickpocketMods[i].mod, pickpocketMods[i].maximum); - } - } - return min(base + basePickpocket.mod, basePickpocket.maximum); -} - -static void __declspec(naked) skill_check_stealing_hack() { - __asm { - push edx; - push ecx; - mov edx, esi; // critter - mov ecx, eax; // base (calculated chance) - call PickpocketMod; - pop ecx; - pop edx; - retn; - } -} - static int __fastcall HitChanceMod(int base, fo::GameObject* critter) { for (DWORD i = 0; i < hitChanceMods.size(); i++) { - if (critter == hitChanceMods[i].id) { + if (critter->id == hitChanceMods[i].id) { return min(base + hitChanceMods[i].mod, hitChanceMods[i].maximum); } } @@ -154,7 +124,7 @@ static void __declspec(naked) determine_to_hit_func_hack() { static long __fastcall CheckDisableBurst(fo::GameObject* critter) { for (size_t i = 0; i < noBursts.size(); i++) { - if (noBursts[i] == critter) { + if (noBursts[i] == critter->id) { return 10; // Disable Burst (area_attack_mode - non-existent value) } } @@ -176,7 +146,7 @@ static void __declspec(naked) ai_pick_hit_mode_hack() { } } -void _stdcall KnockbackSetMod(fo::GameObject* id, DWORD type, float val, DWORD on) { +void _stdcall KnockbackSetMod(fo::GameObject* object, DWORD type, float val, DWORD on) { std::vector* mods; switch (on) { case 0: @@ -191,6 +161,8 @@ void _stdcall KnockbackSetMod(fo::GameObject* id, DWORD type, float val, DWORD o default: return; } + + long id = Objects::SetObjectUniqueID(object); KnockbackModifier mod = { id, type, (double)val }; for (DWORD i = 0; i < mods->size(); i++) { if ((*mods)[i].id == id) { @@ -201,7 +173,7 @@ void _stdcall KnockbackSetMod(fo::GameObject* id, DWORD type, float val, DWORD o mods->push_back(mod); } -void _stdcall KnockbackRemoveMod(fo::GameObject* id, DWORD on) { +void _stdcall KnockbackRemoveMod(fo::GameObject* object, DWORD on) { std::vector* mods; switch (on) { case 0: @@ -217,7 +189,7 @@ void _stdcall KnockbackRemoveMod(fo::GameObject* id, DWORD on) { return; } for (DWORD i = 0; i < mods->size(); i++) { - if ((*mods)[i].id == id) { + if ((*mods)[i].id == object->id) { mods->erase(mods->begin() + i); return; } @@ -230,48 +202,29 @@ void _stdcall SetHitChanceMax(fo::GameObject* critter, DWORD maximum, DWORD mod) baseHitChance.mod = mod; return; } + + long id = Objects::SetObjectUniqueID(critter); for (DWORD i = 0; i < hitChanceMods.size(); i++) { - if (critter == hitChanceMods[i].id) { + if (id == hitChanceMods[i].id) { hitChanceMods[i].maximum = maximum; hitChanceMods[i].mod = mod; return; } } - ChanceModifier cm; - cm.id = critter; - cm.maximum = maximum; - cm.mod = mod; - hitChanceMods.push_back(cm); -} - -void _stdcall SetPickpocketMax(fo::GameObject* critter, DWORD maximum, DWORD mod) { - if ((DWORD)critter == -1) { - basePickpocket.maximum = maximum; - basePickpocket.mod = mod; - return; - } - for (DWORD i = 0; i < pickpocketMods.size(); i++) { - if (critter == pickpocketMods[i].id) { - pickpocketMods[i].maximum = maximum; - pickpocketMods[i].mod = mod; - return; - } - } - ChanceModifier cm; - cm.id = critter; - cm.maximum = maximum; - cm.mod = mod; - pickpocketMods.push_back(cm); + hitChanceMods.push_back(ChanceModifier(id, maximum, mod)); } void _stdcall SetNoBurstMode(fo::GameObject* critter, bool on) { + if (critter == fo::var::obj_dude) return; + + long id = Objects::SetObjectUniqueID(critter); for (size_t i = 0; i < noBursts.size(); i++) { - if (noBursts[i] == critter) { + if (noBursts[i] == id) { if (!on) noBursts.erase(noBursts.begin() + i); // off return; } } - if (on) noBursts.push_back(critter); + if (on) noBursts.push_back(id); } static int __fastcall AimedShotTest(DWORD pid) { @@ -334,36 +287,28 @@ void _stdcall ForceAimedShots(DWORD pid) { forcedAS.push_back(pid); } -static void Knockback_OnGameLoad() { - baseHitChance.maximum = 95; - baseHitChance.mod = 0; - basePickpocket.maximum = 95; - basePickpocket.mod = 0; +static void Combat_OnGameLoad() { + baseHitChance.SetDefault(); mTargets.clear(); mAttackers.clear(); mWeapons.clear(); hitChanceMods.clear(); - pickpocketMods.clear(); noBursts.clear(); disabledAS.clear(); forcedAS.clear(); } -void Knockback::init() { - MakeCall(0x424B76, compute_damage_hack, 2); // KnockbackMod +void Combat::init() { + MakeCall(0x424B76, compute_damage_hack, 2); // KnockbackMod MakeJump(0x4136D3, compute_dmg_damage_hack); // for op_critter_dmg MakeCall(0x424791, determine_to_hit_func_hack); // HitChanceMod BlockCall(0x424796); - MakeCall(0x4ABC62, skill_check_stealing_hack); // PickpocketMod - SafeWrite8(0x4ABC67, 0x89); // mov [esp + 0x54], eax - SafeWrite32(0x4ABC6B, 0x90909090); - // Actually disables all secondary attacks for the critter, regardless of whether the weapon has a burst attack - MakeCall(0x429E44, ai_pick_hit_mode_hack, 1); // NoBurst + MakeCall(0x429E44, ai_pick_hit_mode_hack, 1); // NoBurst - LoadGameHook::OnGameReset() += Knockback_OnGameLoad; + LoadGameHook::OnGameReset() += Combat_OnGameLoad; } } diff --git a/sfall/Modules/Knockback.h b/sfall/Modules/Combat.h similarity index 68% rename from sfall/Modules/Knockback.h rename to sfall/Modules/Combat.h index 158aa6ad7..0bd55d488 100644 --- a/sfall/Modules/Knockback.h +++ b/sfall/Modules/Combat.h @@ -23,16 +23,34 @@ namespace sfall { -class Knockback : public Module { +class Combat : public Module { public: - const char* name() { return "Knockback"; } + const char* name() { return "Combat"; } void init(); }; -void _stdcall SetPickpocketMax(fo::GameObject* critter, DWORD maximum, DWORD mod); +struct ChanceModifier { + long id; + int maximum; + int mod; + + ChanceModifier() : id(0), maximum(95), mod(0) {} + + ChanceModifier(long _id, int max, int _mod) { + id = _id; + maximum = max; + mod = _mod; + } + + void SetDefault() { + maximum = 95; + mod = 0; + } +}; + void _stdcall SetHitChanceMax(fo::GameObject* critter, DWORD maximum, DWORD mod); -void _stdcall KnockbackSetMod(fo::GameObject* id, DWORD type, float val, DWORD on); -void _stdcall KnockbackRemoveMod(fo::GameObject* id, DWORD on); +void _stdcall KnockbackSetMod(fo::GameObject* object, DWORD type, float val, DWORD on); +void _stdcall KnockbackRemoveMod(fo::GameObject* object, DWORD on); void _stdcall SetNoBurstMode(fo::GameObject* critter, bool on); void _stdcall DisableAimedShots(DWORD pid); diff --git a/sfall/Modules/Credits.cpp b/sfall/Modules/Credits.cpp index 257da0e2f..31618d9d6 100644 --- a/sfall/Modules/Credits.cpp +++ b/sfall/Modules/Credits.cpp @@ -34,7 +34,7 @@ static const char* ExtraLines[] = { "#SFALL " VERSION_STRING, "", "sfall is free software, licensed under the GPL", - "Copyright 2008-2018 The sfall team", + "Copyright 2008-2019 The sfall team", "", "@Author", "Timeslip", @@ -68,29 +68,29 @@ static const char* ExtraLines[] = { "Rain man", "Continuum", "Drobovik", + "Burn", "Anyone who has used sfall in their own mods", "The bug reporters and feature requesters", - "" + "", "", "", "#FALLOUT 2", "" }; -static DWORD ExtraLineCount = sizeof(ExtraLines)/4; -static const char* creditsFile = "credits.txt"; +static DWORD ExtraLineCount = sizeof(ExtraLines) / 4; +//static const char* creditsFile = "credits.txt"; -static void _stdcall ShowCreditsHook() { +static void __declspec(naked) ShowCreditsHook() { InCredits = 1; CreditsLine = 0; - __asm { - mov eax, creditsFile; - call fo::funcoffs::credits_; - } + //__asm mov eax, creditsFile; + __asm call fo::funcoffs::credits_; InCredits = 0; + __asm retn; } -static DWORD _stdcall CreditsNextLine(char* buf, DWORD* font, DWORD* colour) { +static DWORD __fastcall CreditsNextLine(char* buf, DWORD* font, DWORD* colour) { if (!InCredits || CreditsLine >= ExtraLineCount) return 0; const char* line = ExtraLines[CreditsLine++]; if (strlen(line)) { @@ -114,53 +114,54 @@ static DWORD _stdcall CreditsNextLine(char* buf, DWORD* font, DWORD* colour) { // Additional lines will be at the top of CREDITS.TXT contents static void __declspec(naked) CreditsNextLineHook_Top() { __asm { - pushad; + pushadc; push ebx; - push edx; - push eax; - call CreditsNextLine; + mov ecx, eax; + call CreditsNextLine; // edx - font test eax, eax; - popad; - jz fail; - xor eax, eax; - inc eax; + popadc; + jz fail; + xor eax, eax; + inc eax; retn; fail: - jmp fo::funcoffs::credits_get_next_line_; + jmp fo::funcoffs::credits_get_next_line_; } } // Additional lines will be at the bottom of CREDITS.TXT contents static void __declspec(naked) CreditsNextLineHook_Bottom() { __asm { - pushad; + push eax; + push edx; + push ebx; call fo::funcoffs::credits_get_next_line_; // call default function test eax, eax; - popad; + pop ebx; + pop edx; + pop eax; jnz morelines; // if not the end yet, skip custom code - pushad; + pushadc; push ebx; - push edx; - push eax; + mov ecx, eax; call CreditsNextLine; // otherwise call out function test eax, eax; // if any extra lines left, return 1 (from function), 0 otherwise - popad; + popadc; jnz morelines; - mov eax, 0x0; + xor eax, eax; retn; morelines: - mov eax, 0x1; + mov eax, 1; retn; } } void Credits::init() { - HookCall(0x480C49, &ShowCreditsHook); - HookCall(0x43F881, &ShowCreditsHook); + HookCalls(ShowCreditsHook, {0x480C49, 0x43F881}); if (GetConfigInt("Misc", "CreditsAtBottom", 0)) { - HookCall(0x42CB49, &CreditsNextLineHook_Bottom); + HookCall(0x42CB49, CreditsNextLineHook_Bottom); } else { - HookCall(0x42CB49, &CreditsNextLineHook_Top); + HookCall(0x42CB49, CreditsNextLineHook_Top); } } diff --git a/sfall/Modules/Elevators.cpp b/sfall/Modules/Elevators.cpp index 557248c41..b7464640d 100644 --- a/sfall/Modules/Elevators.cpp +++ b/sfall/Modules/Elevators.cpp @@ -27,70 +27,74 @@ namespace sfall static const int exitsPerElevator = 4; static const int vanillaElevatorCount = 24; static const int elevatorCount = 50; -static char elevFile[MAX_PATH]; -static fo::ElevatorExit elevatorExits[elevatorCount][exitsPerElevator]; -static DWORD menus[elevatorCount]; +static char elevFile[MAX_PATH] = ".\\"; + +static DWORD elevatorType[elevatorCount]; +static fo::ElevatorExit elevatorExits[elevatorCount][exitsPerElevator]; // _retvals +static fo::ElevatorFrms elevatorsFrms[elevatorCount]; // _intotal +static DWORD elevatorsBtnCount[elevatorCount]; // _btncount static void __declspec(naked) GetMenuHook() { __asm { - push ebx; - lea ebx, menus; - shl eax, 2; - mov eax, [ebx+eax]; - call fo::funcoffs::elevator_start_; - pop ebx; - ret; + lea edx, elevatorType; + shl eax, 2; + mov eax, [edx + eax]; + jmp fo::funcoffs::elevator_start_; } } -static void __declspec(naked) UnknownHook() { +static void __declspec(naked) CheckHotKeysHook() { __asm { + cmp eax, vanillaElevatorCount; + jge skip; // skip hotkeys data push ebx; - lea ebx, menus; - shl eax, 2; - mov eax, [ebx+eax]; + lea ebx, elevatorType; + shl eax, 2; + mov eax, [ebx + eax]; call fo::funcoffs::Check4Keys_; - pop ebx; - ret; + pop ebx; + retn; +skip: + xor eax, eax; + retn; } } +/* static void __declspec(naked) UnknownHook2() { __asm { - push ebx; - lea ebx, menus; - shl eax, 2; - mov eax, [ebx+eax]; - call fo::funcoffs::elevator_end_; - pop ebx; - ret; + lea edx, elevatorType; + shl eax, 2; + mov eax, [edx + eax]; + jmp fo::funcoffs::elevator_end_; } } +*/ static void __declspec(naked) GetNumButtonsHook1() { __asm { - lea esi, menus; - mov eax, [esi+edi*4]; - mov eax, [FO_VAR_btncnt + eax*4]; + lea esi, elevatorType; + mov eax, [esi + edi * 4]; + mov eax, [elevatorsBtnCount + eax * 4]; retn; } } static void __declspec(naked) GetNumButtonsHook2() { __asm { - lea edx, menus; - mov eax, [edx+edi*4]; - mov eax, [FO_VAR_btncnt + eax*4]; + lea edx, elevatorType; + mov eax, [edx + edi * 4]; + mov eax, [elevatorsBtnCount + eax * 4]; retn; } } static void __declspec(naked) GetNumButtonsHook3() { __asm { - lea eax, menus; - mov eax, [eax+edi*4]; - mov eax, [FO_VAR_btncnt+eax*4]; + lea eax, elevatorType; + mov eax, [eax + edi * 4]; + mov eax, [elevatorsBtnCount + eax * 4]; retn; } } @@ -98,13 +102,28 @@ static void __declspec(naked) GetNumButtonsHook3() { void ResetElevators() { memcpy(elevatorExits, fo::var::retvals, sizeof(fo::ElevatorExit) * vanillaElevatorCount * exitsPerElevator); memset(&elevatorExits[vanillaElevatorCount], 0, sizeof(fo::ElevatorExit) * (elevatorCount - vanillaElevatorCount) * exitsPerElevator); - for (int i = 0; i < vanillaElevatorCount; i++) menus[i] = i; - for (int i = vanillaElevatorCount; i < elevatorCount; i++) menus[i] = 0; + + memcpy(elevatorsFrms, (void*)FO_VAR_intotal, sizeof(fo::ElevatorFrms) * vanillaElevatorCount); + memset(&elevatorsFrms[vanillaElevatorCount], 0, sizeof(fo::ElevatorFrms) * (elevatorCount - vanillaElevatorCount)); + + memcpy(elevatorsBtnCount, (void*)FO_VAR_btncnt, sizeof(DWORD) * vanillaElevatorCount); + + for (int i = 0; i < vanillaElevatorCount; i++) elevatorType[i] = i; + for (int i = vanillaElevatorCount; i < elevatorCount; i++) elevatorType[i] = 0; + char section[4]; if (elevFile) { for (int i = 0; i < elevatorCount; i++) { _itoa_s(i, section, 10); - menus[i] = GetPrivateProfileIntA(section, "Image", menus[i], elevFile); + int type = GetPrivateProfileIntA(section, "Image", elevatorType[i], elevFile); + elevatorType[i] = min(type, elevatorCount - 1); + if (i >= vanillaElevatorCount) { + int cBtn = GetPrivateProfileIntA(section, "ButtonCount", 2, elevFile); + if (cBtn > exitsPerElevator) cBtn = exitsPerElevator; + elevatorsBtnCount[i] = max(2, cBtn); + } + elevatorsFrms[i].main = GetPrivateProfileIntA(section, "MainFrm", elevatorsFrms[i].main, elevFile); + elevatorsFrms[i].buttons = GetPrivateProfileIntA(section, "ButtonsFrm", elevatorsFrms[i].buttons, elevFile); char setting[32]; for (int j = 0; j < exitsPerElevator; j++) { sprintf_s(setting, "ID%d", j + 1); @@ -119,21 +138,26 @@ void ResetElevators() { } void ElevatorsInit(const char* file) { - strcpy_s(elevFile, ".\\"); strcat_s(elevFile, file); HookCall(0x43EF83, GetMenuHook); - HookCall(0x43F141, UnknownHook); - HookCall(0x43F2D2, UnknownHook2); + HookCall(0x43F141, CheckHotKeysHook); + //HookCall(0x43F2D2, UnknownHook2); // unused SafeWrite8(0x43EF76, (BYTE)elevatorCount); SafeWrite32(0x43EFA4, (DWORD)elevatorExits); SafeWrite32(0x43EFB9, (DWORD)elevatorExits); - SafeWrite32(0x43EFEA, (DWORD)&elevatorExits[0][0].tile); SafeWrite32(0x43F2FC, (DWORD)elevatorExits); - SafeWrite32(0x43F309, (DWORD)&elevatorExits[0][0].elevation); + SafeWrite32(0x43EFEA, (DWORD)&elevatorExits[0][0].tile); SafeWrite32(0x43F315, (DWORD)&elevatorExits[0][0].tile); + SafeWrite32(0x43F309, (DWORD)&elevatorExits[0][0].elevation); + + SafeWrite32(0x43F438, (DWORD)&elevatorsFrms[0].main); + SafeWrite32(0x43F475, (DWORD)&elevatorsFrms[0].buttons); + // _btncnt + SafeWrite32(0x43F65E, (DWORD)elevatorsBtnCount); + SafeWrite32(0x43F6BB, (DWORD)elevatorsBtnCount); MakeCall(0x43F05D, GetNumButtonsHook1, 2); MakeCall(0x43F184, GetNumButtonsHook2, 2); MakeCall(0x43F1E4, GetNumButtonsHook3, 2); diff --git a/sfall/Modules/Explosions.cpp b/sfall/Modules/Explosions.cpp index 8b16ad0bf..359874f30 100644 --- a/sfall/Modules/Explosions.cpp +++ b/sfall/Modules/Explosions.cpp @@ -23,10 +23,10 @@ #include "..\FalloutEngine\Fallout2.h" #include "..\Logging.h" #include "..\SimplePatch.h" +#include "Scripting\Arrays.h" #include "LoadGameHook.h" #include "MainLoopHook.h" #include "ScriptExtender.h" -#include "Scripting\Arrays.h" #include "Explosions.h" @@ -44,7 +44,6 @@ struct explosiveInfo { std::vector explosives; -static bool onlyOnce = false; static bool lightingEnabled = false; static bool explosionsMetaruleReset = false; static bool explosionsDamageReset = false; @@ -55,47 +54,44 @@ static const DWORD ranged_attack_lighting_fix_back = 0x4118F8; // enable lighting for flying projectile, based on projectile PID data (light intensity & radius) static void __declspec(naked) ranged_attack_lighting_fix() { __asm { - mov eax, [esp+40]; // projectile ptr - 1st arg - mov ecx, [eax+0x70]; // check existing light intensity - cmp ecx, 0; // if non-zero, skip obj_set_light call - jnz skip; - mov ecx, [esp+0x1C]; // protoPtr of projectile - mov edx, [ecx+0x0C]; // light radius - 2nd arg - mov ebx, [ecx+0x10]; // light intensity - 4th arg - xor ecx, ecx; // unknown(0) - 3rd argument + mov eax, [esp + 40]; // source projectile ptr - 1st arg + mov ecx, [eax + lightIntensity]; // check existing light intensity + test ecx, ecx; // if non-zero, skip obj_set_light call + jnz skip; + mov ecx, [esp + 0x1C]; // protoPtr of projectile + mov edx, [ecx + 0x0C]; // light radius - 2nd arg + mov ebx, [ecx + 0x10]; // light intensity - 4th arg + xor ecx, ecx; // unknown(0) - 3rd argument call fo::funcoffs::obj_set_light_; skip: - jmp ranged_attack_lighting_fix_back; // jump back + jmp ranged_attack_lighting_fix_back; // jump back } } -// misc functions from the engine.. -//static const DWORD register_object_play_sfx = 0x41541C; - static const DWORD explosion_effect_hook_back = 0x411AB9; static DWORD explosion_effect_starting_dir = 0; static void __declspec(naked) explosion_effect_hook() { __asm { - mov bl, lightingEnabled - test bl, bl - jz usevanilla - xor ebx, ebx - call fo::funcoffs::register_object_animate_ - jmp next + mov bl, lightingEnabled; + test bl, bl; + jz usevanilla; + xor ebx, ebx; + call fo::funcoffs::register_object_animate_; + jmp next; usevanilla: - xor ebx, ebx - call fo::funcoffs::register_object_animate_and_hide_ + xor ebx, ebx; + call fo::funcoffs::register_object_animate_and_hide_; next: - mov al, lightingEnabled - test al, al - jz skiplight - mov eax, [esp+40]; // projectile ptr - 1st arg - mov edx, 0xFFFF0008; // maximum radius + intensity (see anim_set_check__light_fix) - mov ebx, 0 - call fo::funcoffs::register_object_light_; + mov al, lightingEnabled; + test al, al; + jz skiplight; + mov eax, [esp + 40]; // projectile ptr - 1st arg + mov edx, 0xFFFF0008; // maximum radius + intensity (see anim_set_check__light_fix) + xor ebx, ebx; + call fo::funcoffs::register_object_light_; skiplight: - mov edi, explosion_effect_starting_dir; // starting direction - jmp explosion_effect_hook_back; // jump back + mov edi, explosion_effect_starting_dir; // starting direction + jmp explosion_effect_hook_back; // jump back } } @@ -103,22 +99,21 @@ static const DWORD explosion_lighting_fix2_back = 0x412FC7; // enable lighting for central explosion object (action_explode) static void __declspec(naked) explosion_lighting_fix2() { __asm { - mov eax, [esp+24] - mov edx, 1 // turn on - mov ebx, 0 - call fo::funcoffs::register_object_funset_ - - mov eax, [esp+24]; // explosion obj ptr - mov edx, 0xFFFF0008; // maximum radius + intensity (see anim_set_check__light_fix) - mov ebx, 0 - call fo::funcoffs::register_object_light_; - - mov eax, [esp+24] - xor edx, edx - mov ebx, 0 - call fo::funcoffs::register_object_animate_ - - jmp explosion_lighting_fix2_back; // jump back + mov eax, [esp + 24]; + mov edx, 1; // turn on + xor ebx, ebx; + call fo::funcoffs::register_object_funset_; + + mov eax, [esp + 24]; // explosion obj ptr + mov edx, 0xFFFF0008; // maximum radius + intensity (see anim_set_check__light_fix) + xor ebx, ebx; + call fo::funcoffs::register_object_light_; + + mov eax, [esp + 24]; + xor edx, edx; + xor ebx, ebx; + call fo::funcoffs::register_object_animate_; + jmp explosion_lighting_fix2_back; // jump back } } @@ -130,21 +125,21 @@ DWORD _stdcall LogThis(DWORD value1, DWORD value2, DWORD value3) { static const DWORD anim_set_check__light_back = 0x415A4C; static void __declspec(naked) anim_set_check__light_fix() { __asm { - mov eax, [esi+4] // object - lea ecx, [esp+16] // unknown.. something related to next "tile_refresh_rect" call? - mov edx, [esi+12] // radius set in "reg_anim_light" - mov ebx, edx - shr ebx, 16 // take highest 2 bytes - test ebx, ebx - jz nothingspecial + mov eax, [esi + 4]; // object + lea ecx, [esp + 16]; // unknown.. something related to next "tile_refresh_rect" call? + mov edx, [esi + 12]; // radius set in "reg_anim_light" + mov ebx, edx; + shr ebx, 16; // take highest 2 bytes + test ebx, ebx; + jz nothingspecial; // special case - and edx, 0xFF // use lowest byte as radius, highest 2 bytes as intensity - inc ebx - jmp end + and edx, 0xFF; // use lowest byte as radius, highest 2 bytes as intensity + inc ebx; + jmp end; nothingspecial: - mov ebx, [eax+112] // object current light intensity (original behavior) + mov ebx, [eax + 112]; // object current light intensity (original behavior) end: - jmp anim_set_check__light_back; // jump back right to the "obj_set_light" call + jmp anim_set_check__light_back; // jump back right to the "obj_set_light" call } } @@ -154,20 +149,19 @@ static void __declspec(naked) fire_dance_lighting_fix1() { __asm { push edx; push ebx; - mov eax, esi; // projectile ptr - 1st arg - mov edx, 0xFFFF0002; // maximum radius + intensity (see anim_set_check__light_fix) - mov ebx, 0 - call fo::funcoffs::register_object_light_; - mov eax, esi; - pop ebx; - pop edx; - call fo::funcoffs::register_object_animate_; // overwritten call - mov eax, esi; // projectile ptr - 1st arg - mov edx, 0x00010000; // maximum radius + intensity (see anim_set_check__light_fix) - mov ebx, -1 - call fo::funcoffs::register_object_light_; - - jmp fire_dance_lighting_back; // jump back + mov eax, esi; // projectile ptr - 1st arg + mov edx, 0xFFFF0002; // maximum radius + intensity (see anim_set_check__light_fix) + xor ebx, ebx; + call fo::funcoffs::register_object_light_; + mov eax, esi; + pop ebx; + pop edx; + call fo::funcoffs::register_object_animate_; // overwritten call + mov eax, esi; // projectile ptr - 1st arg + mov edx, 0x00010000; // maximum radius + intensity (see anim_set_check__light_fix) + mov ebx, -1; + call fo::funcoffs::register_object_light_; + jmp fire_dance_lighting_back; // jump back } } @@ -208,7 +202,6 @@ static DWORD __fastcall SetQueueExplosionDamage(DWORD register pid) { _asm mov edx, min; _asm mov ecx, max; } - return result; } @@ -218,15 +211,15 @@ static void __declspec(naked) obj_use_explosive_hack() { cmp edx, PID_PLASTIC_EXPLOSIVES; jz end; // end engine code - pushad; + push edx; mov ecx, edx; call CheckExplosives; + pop edx; test eax, eax; - popad; jnz end; retn; end: - mov dword ptr [esp], 0x49BCE0; + mov dword ptr [esp], 0x49BCE0; retn; } } @@ -237,19 +230,13 @@ static void __declspec(naked) obj_use_explosive_active_hack() { cmp eax, PID_PLASTIC_EXPLOSIVES; jz end; // end engine code - push ebx; - push ecx; - push edx; mov ecx, eax; call CheckExplosives; - pop edx; - pop ecx; - pop ebx; test eax, eax; jz skipSet; mov dword ptr [esi + protoId], eax; // change item pid to active; skipSet: - mov dword ptr [esp], 0x49BD62; + mov dword ptr [esp], 0x49BD62; end: retn; } @@ -258,7 +245,7 @@ static void __declspec(naked) obj_use_explosive_active_hack() { static void __declspec(naked) queue_do_explosion_hack() { using namespace fo; __asm { - cmp edx, PID_ACTIVE_DYNAMITE; + cmp edx, PID_ACTIVE_DYNAMITE; jz dynamite; cmp edx, PID_ACTIVE_PLASTIC_EXPLOSIVE; jz end; @@ -285,11 +272,9 @@ static void __declspec(naked) inven_action_cursor_drop_hack() { jz end; // end engine code push eax; - push ecx; mov ecx, ebx; call CheckActiveExplosives; test eax, eax; // check in engine - pop ecx; pop eax; end: retn; @@ -310,7 +295,6 @@ static void __declspec(naked) protinstTestDroppedExplosive_hack() { } static void apply_expl_hack() { - onlyOnce = true; MakeCall(0x49BCC7, obj_use_explosive_hack); // check explosives MakeCall(0x49BD56, obj_use_explosive_active_hack); // set active explosive MakeCall(0x4A2865, queue_do_explosion_hack); // set damage explosive @@ -319,6 +303,7 @@ static void apply_expl_hack() { } void Explosions::AddToExplosives(DWORD pid, DWORD activePid, DWORD minDmg, DWORD maxDmg) { + static bool onlyOnce = false; for (unsigned int i = 0; i < explosives.size(); i++) { if (explosives[i].pid == pid) { explosives.erase(explosives.begin() + i); @@ -327,7 +312,10 @@ void Explosions::AddToExplosives(DWORD pid, DWORD activePid, DWORD minDmg, DWORD } explosives.push_back({ pid, activePid, minDmg, maxDmg }); - if (!onlyOnce) apply_expl_hack(); + if (!onlyOnce) { + apply_expl_hack(); + onlyOnce = true; + } } //----------------------------------------------------------------- diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index 9f57cdc31..ea4be55e4 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -31,24 +31,26 @@ namespace sfall { -bool appModEnabled = false; //check if Appearance mod enabled for script functions +bool HeroAppearance::appModEnabled = false; // check if Appearance mod enabled for script functions -//char scrn surfaces -BYTE *newButt01Surface = nullptr; +const char* appearancePathFmt = "Appearance\\h%cR%02dS%02d%s"; + +// char scrn surfaces +BYTE *newButtonSurface = nullptr; BYTE *charScrnBackSurface = nullptr; -//char scrn critter rotation vars +// char scrn critter rotation vars DWORD charRotTick = 0; DWORD charRotOri = 0; -int currentRaceVal = 0, currentStyleVal = 0; //holds Appearance values to restore after global reset in NewGame2 function in LoadGameHooks.cpp -DWORD critterListSize = 0, critterArraySize = 0; //Critter art list size +int currentRaceVal = 0, currentStyleVal = 0; // holds Appearance values to restore after global reset in NewGame2 function in LoadGameHooks.cpp +DWORD critterListSize = 0, critterArraySize = 0; // Critter art list size fo::PathNode **tempPathPtr = &fo::var::paths; fo::PathNode *heroPathPtr = nullptr; fo::PathNode *racePathPtr = nullptr; -//for word wrapping +// for word wrapping typedef struct LineNode { DWORD offset; LineNode *next; @@ -59,7 +61,7 @@ typedef struct LineNode { } } LineNode; -//structures for loading unlisted frms +// structures for loading unlisted frms struct UnlistedFrm { DWORD version; WORD FPS; @@ -111,55 +113,60 @@ struct UnlistedFrm { } }; - -//-------------------------------------------------- -DWORD BuildFrmId(DWORD LstRef, DWORD LstNum) { - return fo::func::art_id(LstRef, LstNum, 0, 0, 0); -} - /////////////////////////////////////////////////////////////////UNLISTED FRM FUNCTIONS//////////////////////////////////////////////////////////////////////// -//--------------------------------------------------------------- -bool LoadFrmHeader(UnlistedFrm *frmHeader, fo::DbFile* frmStream) { - if (fo::func::db_freadInt(frmStream, &frmHeader->version) == -1) return 0; - else if (fo::func::db_freadShort(frmStream, &frmHeader->FPS) == -1) return 0; - else if (fo::func::db_freadShort(frmStream, &frmHeader->actionFrame) == -1) return 0; - else if (fo::func::db_freadShort(frmStream, &frmHeader->numFrames) == -1) return 0; - - else if (fo::func::db_freadShortCount(frmStream, frmHeader->xCentreShift, 6) == -1) return 0; - else if (fo::func::db_freadShortCount(frmStream, frmHeader->yCentreShift, 6) == -1) return 0; - else if (fo::func::db_freadIntCount(frmStream, frmHeader->oriOffset, 6) == -1) return 0; - else if (fo::func::db_freadInt(frmStream, &frmHeader->frameAreaSize) == -1) return 0; - - return 1; +static bool LoadFrmHeader(UnlistedFrm *frmHeader, fo::DbFile* frmStream) { + if (fo::func::db_freadInt(frmStream, &frmHeader->version) == -1) + return false; + else if (fo::func::db_freadShort(frmStream, &frmHeader->FPS) == -1) + return false; + else if (fo::func::db_freadShort(frmStream, &frmHeader->actionFrame) == -1) + return false; + else if (fo::func::db_freadShort(frmStream, &frmHeader->numFrames) == -1) + return false; + else if (fo::func::db_freadShortCount(frmStream, frmHeader->xCentreShift, 6) == -1) + return false; + else if (fo::func::db_freadShortCount(frmStream, frmHeader->yCentreShift, 6) == -1) + return false; + else if (fo::func::db_freadIntCount(frmStream, frmHeader->oriOffset, 6) == -1) + return false; + else if (fo::func::db_freadInt(frmStream, &frmHeader->frameAreaSize) == -1) + return false; + + return true; } -//------------------------------------------------------------ -bool LoadFrmFrame(UnlistedFrm::Frame *frame, fo::DbFile* frmStream) { +static bool LoadFrmFrame(UnlistedFrm::Frame *frame, fo::DbFile* frmStream) { //FRMframe *frameHeader = (FRMframe*)frameMEM; //BYTE* frameBuff = frame + sizeof(FRMframe); - if (fo::func::db_freadShort(frmStream, &frame->width)==-1) return 0; - else if (fo::func::db_freadShort(frmStream, &frame->height)==-1) return 0; - else if (fo::func::db_freadInt(frmStream, &frame->size)==-1) return 0; - else if (fo::func::db_freadShort(frmStream, &frame->x)==-1) return 0; - else if (fo::func::db_freadShort(frmStream, &frame->y)==-1) return 0; + if (fo::func::db_freadShort(frmStream, &frame->width) == -1) + return false; + else if (fo::func::db_freadShort(frmStream, &frame->height) == -1) + return false; + else if (fo::func::db_freadInt(frmStream, &frame->size) == -1) + return false; + else if (fo::func::db_freadShort(frmStream, &frame->x) == -1) + return false; + else if (fo::func::db_freadShort(frmStream, &frame->y) == -1) + return false; + frame->indexBuff = new BYTE[frame->size]; - if (fo::func::db_fread(frame->indexBuff, frame->size, 1, frmStream) != 1) return 0; + if (fo::func::db_fread(frame->indexBuff, frame->size, 1, frmStream) != 1) + return false; - return 1; + return true; } -//------------------------------------------------------------------- -UnlistedFrm *LoadUnlistedFrm(char *FrmName, unsigned int folderRef) { +UnlistedFrm *LoadUnlistedFrm(char *frmName, unsigned int folderRef) { - if (folderRef > 10) return nullptr; + if (folderRef > fo::OBJ_TYPE_SKILLDEX) return nullptr; - char*artfolder = (char*)(0x51073C + folderRef * 32); //address of art type name + char *artfolder = fo::var::art[folderRef].path; // address of art type name char frmPath[MAX_PATH]; - sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s\0", artfolder, FrmName); + sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName); UnlistedFrm *frm = new UnlistedFrm; @@ -186,7 +193,9 @@ UnlistedFrm *LoadUnlistedFrm(char *FrmName, unsigned int folderRef) { } } oriOffset_new += frm->numFrames; - } else frm->oriOffset[ori] = 0; + } else { + frm->oriOffset[ori] = 0; + } } fo::func::db_fclose(frmStream); @@ -198,90 +207,128 @@ UnlistedFrm *LoadUnlistedFrm(char *FrmName, unsigned int folderRef) { } /////////////////////////////////////////////////////////////////TEXT FUNCTIONS//////////////////////////////////////////////////////////////////////// -//------------------------- -void SetFont(long ref) { + +static void SetFont(long ref) { fo::func::text_font(ref); } -//----------------------- -long GetFont(void) { +static long GetFont() { return fo::var::curr_font_num; } +/* +int WordWrap(char *TextMsg, DWORD lineLength, WORD *lineNum, WORD *lineOffsets) { + int retVal; + __asm { + mov ebx, lineOffsets + mov ecx, lineNum + mov edx, lineLength + mov eax, TextMsg + call fo::funcoffs::_word_wrap_ + mov retVal, eax + } + return retVal; +} +*/ + +static bool CreateWordWrapList(char *TextMsg, DWORD WrapWidth, DWORD *lineNum, LineNode *StartLine) { + *lineNum = 1; + + if (fo::GetMaxCharWidth() >= WrapWidth) return false; + if (fo::GetTextWidth(TextMsg) < WrapWidth) return true; + + DWORD GapWidth = fo::GetCharGapWidth(); + + StartLine->next = new LineNode; + LineNode *NextLine = StartLine->next; + + DWORD lineWidth = 0, wordWidth = 0, i = 0; + char CurrentChar; + + while (TextMsg[i] != '\0') { + CurrentChar = TextMsg[i++]; + + int cWidth = fo::GetCharWidth(CurrentChar) + GapWidth; + lineWidth += cWidth; + wordWidth += cWidth; + + if (lineWidth <= WrapWidth) { + if (isspace(CurrentChar) || CurrentChar == '-') { + NextLine->offset = i; + wordWidth = 0; + } + } else { + if (isspace(CurrentChar)) { + NextLine->offset = i; + wordWidth = 0; + } + lineWidth = wordWidth; + wordWidth = 0; + CurrentChar = '\0'; + *lineNum += 1; + NextLine->next = new LineNode; + NextLine = NextLine->next; + } + if (TextMsg[i] == '\0') NextLine->offset = 0; + } + return true; +} + +static void DeleteWordWrapList(LineNode *CurrentLine) { + LineNode *NextLine = nullptr; + + while (CurrentLine != nullptr) { + NextLine = CurrentLine->next; + delete CurrentLine; + CurrentLine = NextLine; + } +} /////////////////////////////////////////////////////////////////DAT FUNCTIONS//////////////////////////////////////////////////////////////////////// -//----------------------------------------- -void* LoadDat(char*fileName) { +static void* LoadDat(char*fileName) { return fo::func::dbase_open(fileName); } -//----------------------------------------- -void UnloadDat(void *dat) { +static void UnloadDat(void *dat) { fo::func::dbase_close(dat); } /////////////////////////////////////////////////////////////////OTHER FUNCTIONS//////////////////////////////////////////////////////////////////////// -/* -void DrawLineX(int WinRef, DWORD XStartPos, DWORD XEndPos, DWORD Ypos, BYTE ColourIndex) { - __asm { - xor eax, eax - mov al, ColourIndex - push eax - mov ebx, Ypos - push ebx - mov ecx, XEndPos - mov edx, XStartPos - mov eax, WinRef - - call fo::funcoffs::win_line_ - } -} -*/ - -//--------------------------------------------------------- -void PlayAcm(char *AcmName) { - __asm { - mov eax, AcmName - call fo::funcoffs::gsound_play_sfx_file_ - } +static DWORD BuildFrmId(DWORD lstRef, DWORD lstNum) { + return fo::func::art_id(lstRef, lstNum, 0, 0, 0); } -// Check fallout paths for file---------------- -int CheckFile(char*FileName, DWORD *size_out) { - int retVal = 0; +static void PlayAcm(char *acmName) { __asm { - mov edx, size_out - mov eax, FileName - call fo::funcoffs::db_dir_entry_ - mov retVal, eax + mov eax, acmName; + call fo::funcoffs::gsound_play_sfx_file_; } - return retVal; } /////////////////////////////////////////////////////////////////APP MOD FUNCTIONS//////////////////////////////////////////////////////////////////////// -//----------------------------------------- -char _stdcall GetSex(void) { +static char GetSex() { return (fo::HeroIsFemale()) ? 'F' : 'M'; } // functions to load and save appearance globals -void SetAppearanceGlobals(int race, int style) { +static void SetAppearanceGlobals(int race, int style) { SetGlobalVar("HAp_Race", race); SetGlobalVar("HApStyle", style); } -void GetAppearanceGlobals(int *race, int *style) { +static void GetAppearanceGlobals(int *race, int *style) { *race = GetGlobalVar("HAp_Race"); *style = GetGlobalVar("HApStyle"); } -//---------------------------------------------------------------- -int _stdcall LoadHeroDat(unsigned int Race, unsigned int Style) { +static __declspec(noinline) int _stdcall LoadHeroDat(unsigned int race, unsigned int style, bool flush = false) { + + if (flush) fo::func::art_flush(); - if (heroPathPtr->pDat) { //unload previous Dats + if (heroPathPtr->pDat) { // unload previous Dats UnloadDat(heroPathPtr->pDat); heroPathPtr->pDat = nullptr; heroPathPtr->isDat = 0; @@ -292,99 +339,88 @@ int _stdcall LoadHeroDat(unsigned int Race, unsigned int Style) { racePathPtr->isDat = 0; } - sprintf_s(heroPathPtr->path, 64, "Appearance\\h%cR%02dS%02d.dat\0", GetSex(), Race, Style); - if (GetFileAttributes(heroPathPtr->path) != INVALID_FILE_ATTRIBUTES) { //check if Dat exists for selected appearance + const char sex = GetSex(); + + sprintf_s(heroPathPtr->path, 64, appearancePathFmt, sex, race, style, ".dat"); + int result = GetFileAttributes(heroPathPtr->path); + if (result != INVALID_FILE_ATTRIBUTES && !(result & FILE_ATTRIBUTE_DIRECTORY)) { // check if Dat exists for selected appearance heroPathPtr->pDat = LoadDat(heroPathPtr->path); heroPathPtr->isDat = 1; - } - else { - sprintf_s(heroPathPtr->path, 64, "Appearance\\h%cR%02dS%02d\0", GetSex(), Race, Style); - if (GetFileAttributes(heroPathPtr->path) == INVALID_FILE_ATTRIBUTES) //check if folder exists for selected appearance + } else { + sprintf_s(heroPathPtr->path, 64, appearancePathFmt, sex, race, style, ""); + if (GetFileAttributes(heroPathPtr->path) == INVALID_FILE_ATTRIBUTES) // check if folder exists for selected appearance return -1; } - tempPathPtr = &heroPathPtr; //set path for selected appearance + tempPathPtr = &heroPathPtr; // set path for selected appearance heroPathPtr->next = &fo::var::paths[0]; - if (Style != 0) { - sprintf_s(racePathPtr->path, 64, "Appearance\\h%cR%02dS%02d.dat\0", GetSex(), Race, 0); - if (GetFileAttributes(racePathPtr->path) != INVALID_FILE_ATTRIBUTES) { //check if Dat exists for selected race base appearance + if (style != 0) { + sprintf_s(racePathPtr->path, 64, appearancePathFmt, sex, race, 0, ".dat"); + int result = GetFileAttributes(racePathPtr->path); + if (result != INVALID_FILE_ATTRIBUTES && !(result & FILE_ATTRIBUTE_DIRECTORY)) { // check if Dat exists for selected race base appearance racePathPtr->pDat = LoadDat(racePathPtr->path); racePathPtr->isDat = 1; + } else { + sprintf_s(racePathPtr->path, 64, appearancePathFmt, sex, race, 0, ""); } - else - sprintf_s(racePathPtr->path, 64, "Appearance\\h%cR%02dS%02d\0", GetSex(), Race, 0); - if (GetFileAttributes(racePathPtr->path) != INVALID_FILE_ATTRIBUTES) { //check if folder/Dat exists for selected race base appearance - heroPathPtr->next = racePathPtr; //set path for selected race base appearance + if (GetFileAttributes(racePathPtr->path) != INVALID_FILE_ATTRIBUTES) { // check if folder/Dat exists for selected race base appearance + heroPathPtr->next = racePathPtr; // set path for selected race base appearance racePathPtr->next = &fo::var::paths[0]; } } return 0; } -//--------------------------------------------------------- -//insert hero art path in front of main path structure when loading art +// insert hero art path in front of main path structure when loading art static void __declspec(naked) LoadNewHeroArt() { __asm { - cmp byte ptr ds:[esi], 'r' - je isReading - mov ecx, FO_VAR_paths - jmp setPath + cmp byte ptr ds:[esi], 'r'; + je isReading; + mov ecx, FO_VAR_paths; + jmp setPath; isReading: - mov ecx, tempPathPtr + mov ecx, tempPathPtr; setPath: - mov ecx, dword ptr ds:[ecx] - ret + mov ecx, dword ptr ds:[ecx]; + retn; } } -//--------------------------------------------------------- static void __declspec(naked) CheckHeroExist() { __asm { - //pushad - cmp esi, critterArraySize //check if loading hero art - jle EndFunc - - sub esp, 0x4 - lea ebx, [esp] - push ebx - push FO_VAR_art_name //critter art file name address - //call CheckHeroFile//check if art file exists - call CheckFile - add esp, 0xC - cmp eax, -1 - jne EndFunc - - //pop eax//drop func ret address - add esp, 0x4 - //if file not found load regular critter art instead - sub esi, critterArraySize - //popad - mov eax, 0x4194E2 - jmp eax -EndFunc: - //popad - mov eax, FO_VAR_art_name - ret + cmp esi, critterArraySize; // check if loading hero art + jle endFunc; + sub esp, 4; + lea edx, [esp]; // size out + mov eax, FO_VAR_art_name; // critter art file name address (file name) + call fo::funcoffs::db_dir_entry_; // check art file exists + add esp, 4; + cmp eax, -1; + jne endFunc; + + // 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; } } -//--------------------------------------------------------- -//adjust base hero art if num below hero art range +// adjust base hero art if num below hero art range static void __declspec(naked) AdjustHeroBaseArt() { __asm { - //cmp eax, critterListSize - // jg EndFunc - add eax, critterListSize -//EndFunc: - mov dword ptr ds:[FO_VAR_art_vault_guy_num],eax - ret + add eax, critterListSize; + mov dword ptr ds:[FO_VAR_art_vault_guy_num], eax; + retn; } } -//--------------------------------------------------------- -//adjust armor art if num below hero art range +// adjust armor art if num below hero art range static void AdjustHeroArmorArt(DWORD fid) { if (!PartyControl::IsNpcControlled()) { DWORD fidBase = fid & 0xFFF; @@ -394,764 +430,566 @@ static void AdjustHeroArmorArt(DWORD fid) { } } -//----------------------------------------- -void _stdcall SetHeroArt(int NewArtFlag) { - __asm { - mov eax, dword ptr ds:[FO_VAR_obj_dude] //hero state struct - mov eax, dword ptr ds:[eax + 0x20] //get hero FrmID - xor edx, edx - mov dx, ax - and dh, 0x0F //mask out current weapon flag - cmp edx, critterListSize //check if critter LST index is in Hero range - jg IsHero - cmp NewArtFlag, 1 - jne EndFunc - add eax, critterListSize //shift index up into hero range - jmp SetArt -IsHero: - cmp NewArtFlag, 0 - jne EndFunc - sub eax, critterListSize //shift index down into normal critter range -SetArt: - mov ebx, 0 //SomePtr - mov edx, eax - mov eax, dword ptr ds:[FO_VAR_obj_dude] //hero state struct - mov dword ptr ds:[eax + 0x20],edx //copy new FrmID to hero state struct - //call obj_change_fid_ // set critter FrmID function -EndFunc: +static void _stdcall SetHeroArt(bool newArtFlag) { + + fo::GameObject* hero = fo::var::obj_dude; // hero state struct + long heroFID = hero->artFid; // get hero FrmID + DWORD fidBase = heroFID & 0xFFF; // mask out current weapon flag + + if (fidBase > critterListSize) { // check if critter LST index is in Hero range + if (!newArtFlag) { + heroFID -= critterListSize; // shift index down into normal critter range + hero->artFid = heroFID; + } + } else if (newArtFlag) { + heroFID += critterListSize; // shift index up into hero range + hero->artFid = heroFID; // set new FrmID to hero state struct } } -//---------------------------------------------- -//return hero art val to normal before saving +// return hero art val to normal before saving static void __declspec(naked) SavCritNumFix() { __asm { - push eax - push edx - push 0 //set hero FrmID LST index to normal range before saving - call SetHeroArt - pop edx - pop eax - - push ebx - call fo::funcoffs::obj_save_dude_ //save current hero state structure fuction - pop ebx - - push eax - push edx - push 1 //return hero FrmID LST index back to hero art range after saving hero state structure - call SetHeroArt - pop edx - pop eax - - ret + push ecx; + push edx; + push eax; + push 0; // set hero FrmID LST index to normal range before saving + call SetHeroArt; + pop eax; + call fo::funcoffs::obj_save_dude_; // save current hero state structure fuction + push eax; + push 1; // return hero FrmID LST index back to hero art range after saving hero state structure + call SetHeroArt; + pop eax; + pop edx; + pop ecx; + retn; } } -//--------------------------------------------------------- static void __declspec(naked) DoubleArt() { __asm { - cmp dword ptr ss:[esp+0xCC], 0x510774 //check if loading critter lst. 0x510774= addr of critter list size val - jne EndFunc - shl edi, 1 //double critter list size to add room for hero art -EndFunc: - mov eax, ecx - xor ebx, ebx - xor edx, edx - ret + cmp dword ptr ss:[esp + 0xCC], 0x510774; // check if loading critter lst. 0x510774 = addr of critter list size val + jne endFunc; + shl edi, 1; // double critter list size to add room for hero art +endFunc: + jmp fo::funcoffs::db_fseek_; } } -//------------------------------ -void FixCritList() { +// create a duplicate list of critter names at the end with an additional '_' character at the beginning of its name +static long _stdcall AddHeroCritNames() { // art_init_ auto &critterArt = fo::var::art[fo::OBJ_TYPE_CRITTER]; critterListSize = critterArt.total / 2; critterArraySize = critterListSize * 13; - char *CritList = critterArt.names; //critter list offset - char *HeroList = CritList + (critterArraySize); //set start of hero critter list after regular critter list + 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); + 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 + 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); HeroList += 13; CritList += 13; } + return critterArt.total; } -//--------------------------------------------------------- -static void __declspec(naked) AddHeroCritNames() { - __asm { - call FixCritList //insert names for hero critters - mov eax, dword ptr ds:[FO_VAR_art + 0x3C] - ret - } +///////////////////////////////////////////////////////////////GRAPHICS HERO FUNCTIONS////////////////////////////////////////////////////////////////////// + +static void DrawPC() { + fo::RedrawObject(fo::var::obj_dude); } -//----------------------------------------------------------------------------------------------------------------------- -void sub_draw(long subWidth, long subHeight, long fromWidth, long fromHeight, long fromX, long fromY, BYTE *fromBuff, - long toWidth, long toHeight, long toX, long toY, BYTE *toBuff, int maskRef) { +// scan inventory items for armor and weapons currently being worn or wielded and setup matching FrmID for PC +void UpdateHeroArt() { + auto iD = fo::var::inven_dude; + auto iR = fo::var::i_rhand; + auto iL = fo::var::i_lhand; + auto iW = fo::var::i_worn; - fromBuff = fromBuff + fromY*fromWidth + fromX; - toBuff = toBuff + toY*toWidth + toX; + fo::var::i_rhand = 0; + fo::var::i_lhand = 0; + fo::var::i_worn = 0; - for (long h = 0; h < subHeight; h++) { - for (long w = 0; w < subWidth; w++) { - if (fromBuff[w] != maskRef) - toBuff[w] = fromBuff[w]; + fo::var::inven_dude = fo::var::obj_dude; + int invenSize = fo::var::obj_dude->invenSize; + //fo::var::pud = invenSize; + + for (int itemNum = 0; itemNum < invenSize; itemNum++) { + fo::GameObject* item = fo::var::obj_dude->invenTable[itemNum].object; // PC inventory item list + itemListOffset + + if (item->flags & fo::ObjectFlag::Right_Hand) { + fo::var::i_rhand = item; + } + else if (item->flags & fo::ObjectFlag::Left_Hand) { + fo::var::i_lhand = item; + } + else if (item->flags & fo::ObjectFlag::Worn) { + fo::var::i_worn = item; } - fromBuff += fromWidth; - toBuff += toWidth; } -} + // inventory function - setup pc FrmID and store at address _i_fid + fo::var::obj_dude->artFid = Inventory::adjust_fid_replacement(); // adjust_fid_ -//----------------------------------------------- -void DrawPC(void) { - RECT critRect; - __asm { - /* - lea ebx, //-out- RECT* - mov eax, dword ptr ds:[FO_VAR_obj_dude] //critter struct - mov edx, dword ptr ds:[eax + 0x20] // new frmId - call obj_change_fid_ //set new critt FrmID func - */ - lea edx, critRect //out critter RECT* - mov eax, dword ptr ds:[FO_VAR_obj_dude] //dude critter struct - call fo::funcoffs::obj_bound_ //get critter rect func - - mov edx, dword ptr ds:[FO_VAR_obj_dude] //dude critter struct - lea eax, critRect //RECT* - mov edx, dword ptr ds:[edx + 0x28] //map level the dude is on - call fo::funcoffs::tile_refresh_rect_ //draw rect area func - } + fo::var::inven_dude = iD; + fo::var::i_rhand = iR; + fo::var::i_lhand = iL; + fo::var::i_worn = iW; } -//---------------------------------------------------------------------- void _stdcall RefreshPCArt() { -//scan inventory items for armor and weapons currently being worn or wielded -//and setup matching FrmID for PC - __asm { - call fo::funcoffs::proto_dude_update_gender_ //refresh PC base model art - - mov eax, dword ptr ds:[FO_VAR_obj_dude] //PC state struct - mov dword ptr ds:[FO_VAR_inven_dude], eax //inventory temp pointer to PC state struct - mov eax, dword ptr ds:[FO_VAR_inven_dude] - lea edx, dword ptr ds:[eax + 0x2C] - mov dword ptr ds:[FO_VAR_pud], edx //PC inventory - - xor eax, eax - xor edx, edx //itemListOffset - xor ebx, ebx //itemNum - - mov dword ptr ds:[FO_VAR_i_rhand], eax //item2 - mov dword ptr ds:[FO_VAR_i_worn], eax //armor - mov dword ptr ds:[FO_VAR_i_lhand], eax //item1 - jmp LoopStart - -CheckNextItem: - mov eax, dword ptr ds:[eax + 0x8] //PC inventory item list - mov eax, dword ptr ds:[edx + eax] //PC inventory item list + itemListOffset - - test byte ptr ds:[eax + 0x27], 1 //if item in item1 slot - jnz IsItem1 - test byte ptr ds:[eax + 0x27], 2 //if item in item2 slot - jnz IsItem2 - test byte ptr ds:[eax + 0x27], 4 //if item in armor slot - jnz IsArmor - jmp SetNextItem - -IsItem1: - mov dword ptr ds:[FO_VAR_i_lhand], eax //set item1 - test byte ptr ds:[eax + 0x27], 2 //check if same item type also in item2 slot - jz SetNextItem - -IsItem2: - mov dword ptr ds:[FO_VAR_i_rhand], eax //set item2 - jmp SetNextItem - -IsArmor: - mov dword ptr ds:[FO_VAR_i_worn], eax //set armor - -SetNextItem: - inc ebx //itemNum++ - add edx, 0x8 //itemListOffset + itemsize -LoopStart: - mov eax, dword ptr ds:[FO_VAR_pud] //PC inventory - cmp ebx, dword ptr ds:[eax] //size of item list - jl CheckNextItem - - //inventory function - setup pc FrmID and store at address _i_fid - call fo::funcoffs::adjust_fid_ - - //copy new FrmID to hero state struct - mov edx, dword ptr ds:[FO_VAR_i_fid] - mov eax, dword ptr ds:[FO_VAR_inven_dude] - mov dword ptr ds:[eax + 0x20], edx - //call fo::funcoffs::obj_change_fid_ - - xor eax,eax - mov dword ptr ds:[FO_VAR_i_rhand], eax //item2 - mov dword ptr ds:[FO_VAR_i_worn], eax //armor - mov dword ptr ds:[FO_VAR_i_lhand], eax //item1 - } - - if (!appModEnabled) return; + fo::func::proto_dude_update_gender(); // refresh PC base model art - if (LoadHeroDat(currentRaceVal, currentStyleVal) != 0) { //if load fails - currentStyleVal = 0; //set style to default - if (LoadHeroDat(currentRaceVal, currentStyleVal) != 0) { //if race fails with style at default - currentRaceVal = 0; //set race to default - LoadHeroDat(currentRaceVal, currentStyleVal); - } - } - fo::func::art_flush(); + UpdateHeroArt(); DrawPC(); } -//---------------------------------------------------------------------- -void _stdcall LoadHeroAppearance(void) { - if (!appModEnabled) return; +void _stdcall LoadHeroAppearance() { + if (!HeroAppearance::appModEnabled) return; GetAppearanceGlobals(¤tRaceVal, ¤tStyleVal); - fo::func::art_flush(); - LoadHeroDat(currentRaceVal, currentStyleVal); - SetHeroArt(1); + LoadHeroDat(currentRaceVal, currentStyleVal, true); + SetHeroArt(true); DrawPC(); } -//--------------------------------------- -void _stdcall SetNewCharAppearanceGlobals(void) { - if (!appModEnabled) return; +void _stdcall SetNewCharAppearanceGlobals() { + if (!HeroAppearance::appModEnabled) return; - if (currentRaceVal > 0 || currentStyleVal > 0) + if (currentRaceVal > 0 || currentStyleVal > 0) { SetAppearanceGlobals(currentRaceVal, currentStyleVal); + } } -//---------------------------------------------------------------------- +// op_set_hero_style void _stdcall SetHeroStyle(int newStyleVal) { - if (!appModEnabled) return; + if (!HeroAppearance::appModEnabled || newStyleVal == currentStyleVal) return; - if (newStyleVal == currentStyleVal) return; - - fo::func::art_flush(); - - if (LoadHeroDat(currentRaceVal, newStyleVal) != 0) { //if new style cannot be set + if (LoadHeroDat(currentRaceVal, newStyleVal, true) != 0) { // if new style cannot be set if (currentRaceVal == 0 && newStyleVal == 0) { - currentStyleVal = 0; //ignore error if appearance = default + currentStyleVal = 0; // ignore error if appearance = default } else { - LoadHeroDat(currentRaceVal, currentStyleVal); //reload original style + LoadHeroDat(currentRaceVal, currentStyleVal); // reload original style } } else { currentStyleVal = newStyleVal; } - SetAppearanceGlobals(currentRaceVal, currentStyleVal); DrawPC(); } -//---------------------------------------------------------------------- +// op_set_hero_race void _stdcall SetHeroRace(int newRaceVal) { - if (!appModEnabled) return; + if (!HeroAppearance::appModEnabled || newRaceVal == currentRaceVal) return; - if (newRaceVal == currentRaceVal) return; - - fo::func::art_flush(); - - if (LoadHeroDat(newRaceVal, 0) != 0) { //if new race fails with style at 0 - if (newRaceVal == 0) currentRaceVal = 0, currentStyleVal = 0; //ignore if appearance = default - else LoadHeroDat(currentRaceVal, currentStyleVal); //reload original race & style + if (LoadHeroDat(newRaceVal, 0, true) != 0) { // if new race fails with style at 0 + if (newRaceVal == 0) { + currentRaceVal = 0; + currentStyleVal = 0; // ignore if appearance = default + } else { + LoadHeroDat(currentRaceVal, currentStyleVal); // reload original race & style + } + } else { + currentRaceVal = newRaceVal; + currentStyleVal = 0; } - else - currentRaceVal=newRaceVal, currentStyleVal=0; - - SetAppearanceGlobals(currentRaceVal, currentStyleVal); //store new globals + SetAppearanceGlobals(currentRaceVal, currentStyleVal); // store new globals DrawPC(); } -//-------------------------------------------------------------------------------------- -bool CreateWordWrapList(char *TextMsg, DWORD WrapWidth, DWORD *lineNum, LineNode *StartLine) { - *lineNum = 1; - - if (fo::GetMaxCharWidth() >= WrapWidth) return FALSE; - - if (fo::GetTextWidth(TextMsg) < WrapWidth) return TRUE; - - DWORD GapWidth = fo::GetCharGapWidth(); - - StartLine->next = new LineNode; - LineNode *NextLine = StartLine->next; - - DWORD lineWidth = 0, wordWidth = 0; - - char CurrentChar = '\0'; - DWORD i = 0; +// Reset Appearance when selecting "Create Character" from the New Char screen +static void __declspec(naked) CreateCharReset() { + __asm { + cmp currentStyleVal, 0; + jnz reset; + cmp currentRaceVal, 0; + jz endFunc; +reset: // set race and style to defaults + push edx; + push ecx; + xor eax, eax; + mov currentRaceVal, eax; + mov currentStyleVal, eax; + push eax; // flush + push eax; + push eax; + call LoadHeroDat; + pop ecx; + pop edx; + call fo::funcoffs::proto_dude_update_gender_; +endFunc: + mov eax, 1; + retn; + } +} - while (TextMsg[i] != '\0') { - CurrentChar = TextMsg[i]; +/////////////////////////////////////////////////////////////////INTERFACE FUNCTIONS/////////////////////////////////////////////////////////////////////// - lineWidth = lineWidth + fo::GetCharWidth(CurrentChar) + GapWidth; - wordWidth = wordWidth + fo::GetCharWidth(CurrentChar) + GapWidth; +static void sub_draw(long subWidth, long subHeight, long fromWidth, long fromHeight, long fromX, long fromY, BYTE *fromBuff, + long toWidth, long toHeight, long toX, long toY, BYTE *toBuff, int maskRef) { - if (lineWidth <= WrapWidth) { - if (isspace(CurrentChar) || CurrentChar == '-') - NextLine->offset = i + 1, wordWidth = 0; - } - else { - if (isspace(CurrentChar)) - NextLine->offset = i + 1, wordWidth = 0; + fromBuff += fromY * fromWidth + fromX; + toBuff += toY * toWidth + toX; - lineWidth = wordWidth; - wordWidth = 0; - CurrentChar = '\0'; - *lineNum = *lineNum + 1; - NextLine->next = new LineNode; - NextLine = NextLine->next; + for (long h = 0; h < subHeight; h++) { + for (long w = 0; w < subWidth; w++) { + if (fromBuff[w] != maskRef) + toBuff[w] = fromBuff[w]; } - i++; - - if (TextMsg[i] == '\0') - NextLine->offset = 0; + fromBuff += fromWidth; + toBuff += toWidth; } - - return TRUE; } -/* -int WordWrap(char *TextMsg, DWORD lineLength, WORD *lineNum, WORD *lineOffsets) { - int retVal; - __asm { - mov ebx, lineOffsets - mov ecx, lineNum - mov edx, lineLength - mov eax, TextMsg - call fo::funcoffs::_word_wrap_ - mov retVal, eax - } - return retVal; -} -*/ +static void DrawBody(DWORD critNum, BYTE* surface) { + DWORD critFrmLock; -void DeleteWordWrapList(LineNode *CurrentLine) { - LineNode *NextLine = nullptr; + fo::FrmFrameData *critFrm = fo::func::art_ptr_lock(BuildFrmId(1, critNum), &critFrmLock); + DWORD critWidth = fo::func::art_frame_width(critFrm, 0, charRotOri); + DWORD critHeight = fo::func::art_frame_length(critFrm, 0, charRotOri); + BYTE *critSurface = fo::func::art_frame_data(critFrm, 0, charRotOri); + sub_draw(critWidth, critHeight, critWidth, critHeight, 0, 0, critSurface, 70, 102, 35 - critWidth / 2, 51 - critHeight / 2, surface, 0); - while (CurrentLine != nullptr) { - NextLine = CurrentLine->next; - delete CurrentLine; - CurrentLine = NextLine; - } + fo::func::art_ptr_unlock(critFrmLock); + critSurface = nullptr; } -//---------------------------------------------------------------- -void DrawPCConsole() { +static void DrawPCConsole() { - DWORD NewTick = *(DWORD*)0x5709C4; //char scrn gettickcount ret - DWORD RotSpeed = *(DWORD*)0x47066B; //get rotation speed -inventory rotation speed + DWORD NewTick = *(DWORD*)0x5709C4; // char scrn gettickcount ret + DWORD RotSpeed = *(DWORD*)0x47066B; // get rotation speed - inventory rotation speed - if (charRotTick > NewTick) - charRotTick = NewTick; + if (charRotTick > NewTick) charRotTick = NewTick; if (NewTick - charRotTick > RotSpeed) { charRotTick = NewTick; - if (charRotOri < 5) + if (charRotOri < 5) { charRotOri++; - else charRotOri = 0; - + } else { + charRotOri = 0; + } - int WinRef = fo::var::edit_win; //char screen window ref + int WinRef = fo::var::edit_win; // char screen window ref //BYTE *WinSurface = GetWinSurface(WinRef); - fo::Window *WinInfo = fo::func::GNW_find(WinRef); - BYTE *ConSurface = new BYTE [70*102]; + fo::Window *WinInfo = fo::func::GNW_find(WinRef); + BYTE *ConSurface = new BYTE [70 * 102]; sub_draw(70, 102, 640, 480, 338, 78, charScrnBackSurface, 70, 102, 0, 0, ConSurface, 0); - //sub_draw(70, 102, widthBG, heightBG, xPosBG, yPosBG, BGSurface, 70, 102, 0, 0, ConSurface, 0); - - //DWORD CritNum = fo::var::art_vault_guy_num; //pointer to current base hero critter FrmId - DWORD critNum = fo::var::obj_dude->artFid; //pointer to current armored hero critter FrmId - DWORD critFrmLock; - fo::FrmFrameData *critFrm; - //DWORD PcCritOri = 0; - DWORD critWidth; - DWORD critHeight; - BYTE *critSurface; - - critFrm = fo::func::art_ptr_lock(BuildFrmId(1, critNum), &critFrmLock); - critWidth = fo::func::art_frame_width(critFrm, 0, charRotOri); - critHeight = fo::func::art_frame_length(critFrm, 0, charRotOri); - critSurface = fo::func::art_frame_data(critFrm, 0, charRotOri); - - sub_draw(critWidth, critHeight, critWidth, critHeight, 0, 0, critSurface, 70, 102, 35-critWidth/2, 51-critHeight/2, ConSurface, 0); - - BYTE ConsoleGreen = fo::var::GreenColor; //palette offset stored in mem - text colour - BYTE ConsoleGold = fo::var::YellowColor; //palette offset stored in mem - text colour - - BYTE styleColour = ConsoleGreen, raceColour = ConsoleGreen; - if (fo::var::info_line == 0x501) - raceColour = ConsoleGold; - else if (fo::var::info_line == 0x502) - styleColour = ConsoleGold; -/* - int oldFont = GetFont(); //store current font - SetFont(0x65); //set font for consol text - char TextBuf[12]; - sprintf_s(TextBuf, 12, "%2d\0", CurrentRaceVal); - PrintText(TextBuf, raceColour, 2, 2, 64, 70, ConSurface); + //DWORD critNum = fo::var::art_vault_guy_num; // pointer to current base hero critter FrmId + DWORD critNum = fo::var::obj_dude->artFid; // pointer to current armored hero critter FrmId + DrawBody(critNum, ConSurface); - sprintf_s(TextBuf, 12, "%2d\0", currentStyleVal); - PrintText(TextBuf, styleColour, 5, 88, 64, 70, ConSurface); - - SetFont(oldFont); //restore previous font -*/ - - //sub_draw(70, 102, 70, 102, 0, 0, ConSurface, 640, 480, 338, 78, WinSurface, 0); sub_draw(70, 102, 70, 102, 0, 0, ConSurface, WinInfo->width, WinInfo->height, 338, 78, WinInfo->surface, 0); - fo::func::art_ptr_unlock(critFrmLock); - critSurface = nullptr; delete[] ConSurface; WinInfo = nullptr; + fo::func::win_draw(WinRef); } } -//------------------------------------------------------------------------------------------------------------------------------------------------ -void DrawCharNote(bool Style, int WinRef, DWORD xPosWin, DWORD yPosWin, BYTE *BGSurface, DWORD xPosBG, DWORD yPosBG, DWORD widthBG, DWORD heightBG) { +/* +void DrawCharNote(DWORD LstNum, char *TitleTxt, char *AltTitleTxt, char *Message) { + __asm { + mov ecx, message //dword ptr ds:[FO_VAR_folder_card_desc] + mov ebx, alttitletxt //dword ptr ds:[FO_VAR_folder_card_title2] + mov edx, titletxt //dword ptr ds:[FO_VAR_folder_card_title] + mov eax, lstnum //dword ptr ds:[FO_VAR_folder_card_fid] + call fo::funcoffs::drawcard_ + } +} +*/ + +static void DrawCharNote(bool style, int winRef, DWORD xPosWin, DWORD yPosWin, BYTE *BGSurface, DWORD xPosBG, DWORD yPosBG, DWORD widthBG, DWORD heightBG) { fo::MessageList MsgList; char *TitleMsg = nullptr; char *InfoMsg = nullptr; - char *MsgFileName = nullptr; - - if (!Style) MsgFileName = "game\\AppRace.msg"; - else MsgFileName = "game\\AppStyle.msg"; + char *MsgFileName = (style) ? "game\\AppStyle.msg" : "game\\AppRace.msg"; if (fo::func::message_load(&MsgList, MsgFileName) == 1) { TitleMsg = GetMsg(&MsgList, 100, 2); InfoMsg = GetMsg(&MsgList, 101, 2); } - BYTE colour = *(BYTE*)0x6A38D0; //brown + fo::Window *winInfo = fo::func::GNW_find(winRef); - fo::Window *WinInfo = fo::func::GNW_find(WinRef); - - BYTE *PadSurface; - PadSurface = new BYTE [280*168]; + BYTE *PadSurface = new BYTE [280 * 168]; sub_draw(280, 168, widthBG, heightBG, xPosBG, yPosBG, BGSurface, 280, 168, 0, 0, PadSurface, 0); - UnlistedFrm *frm; - if (Style) frm = LoadUnlistedFrm("AppStyle.frm", 10); - else frm = LoadUnlistedFrm("AppRace.frm", 10); - + UnlistedFrm *frm = LoadUnlistedFrm((style) ? "AppStyle.frm" : "AppRace.frm", fo::OBJ_TYPE_SKILLDEX); if (frm) { - sub_draw(frm->frames[0].width, frm->frames[0].height, frm->frames[0].width, frm->frames[0].height, 0, 0, frm->frames[0].indexBuff, 280, 168, 136, 37, PadSurface, 0); //cover buttons pics bottom - //sub_draw(frm->width, frm->height, frm->width, frm->height, 0, 0, frm->surface, 280, 168, 136, 37, PadSurface, 0); //cover buttons pics bottom - //sub_draw(frm->width, frm->height, frm->width, frm->height, 0, 0, frm->surface, 280, 168, 135, 36, PadSurface, 0); //cover buttons pics bottom + sub_draw(frm->frames[0].width, frm->frames[0].height, frm->frames[0].width, frm->frames[0].height, 0, 0, frm->frames[0].indexBuff, 280, 168, 136, 37, PadSurface, 0); // cover buttons pics bottom delete frm; } - int oldFont = GetFont(); //store current font - SetFont(0x66); //set font for title + int oldFont = GetFont(); // store current font + SetFont(0x66); // set font for title - DWORD textHeight = fo::GetTextHeight(); + DWORD textHeight; + BYTE colour = *(BYTE*)FO_VAR_colorTable; // black color if (TitleMsg != nullptr) { + textHeight = fo::GetTextHeight(); fo::PrintText(TitleMsg, colour, 0, 0, 265, 280, PadSurface); - //DrawLineX(WinRef, 348, 613, 272+textHeight, colour); - //DrawLineX(WinRef, 348, 613, 273+textHeight, colour); - memset(PadSurface + 280*textHeight, colour, 265); - memset(PadSurface + 280*(textHeight + 1), colour, 265); + // draw line + memset(PadSurface + 280 * textHeight, colour, 265); + memset(PadSurface + 280 * (textHeight + 1), colour, 265); } - SetFont(0x65); //set font for info - - textHeight = fo::GetTextHeight(); - DWORD lineNum = 0; - LineNode *StartLine = new LineNode; LineNode *CurrentLine, *NextLine; if (InfoMsg != nullptr) { + SetFont(0x65); // set font for info + textHeight = fo::GetTextHeight(); + if (CreateWordWrapList(InfoMsg, 160, &lineNum, StartLine)) { int lineHeight = 43; - char TempChar = 0; - if (lineNum == 1) fo::PrintText(InfoMsg, colour, 0, lineHeight, 280, 280, PadSurface); - else{ + if (lineNum == 1) { + fo::PrintText(InfoMsg, colour, 0, lineHeight, 280, 280, PadSurface); + } else { if (lineNum > 11) lineNum = 11; CurrentLine = StartLine; for (DWORD line = 0; line < lineNum; line++) { NextLine = CurrentLine->next; - TempChar = InfoMsg[NextLine->offset]; //[line+1]]; + char TempChar = InfoMsg[NextLine->offset]; //[line+1]]; InfoMsg[NextLine->offset] = '\0'; - fo::PrintText(InfoMsg+CurrentLine->offset, colour, 0, lineHeight, 280, 280, PadSurface); + fo::PrintText(InfoMsg + CurrentLine->offset, colour, 0, lineHeight, 280, 280, PadSurface); InfoMsg[NextLine->offset] = TempChar; - lineHeight = lineHeight + textHeight + 1; + lineHeight += textHeight + 1; CurrentLine = NextLine; } } } } + sub_draw(280, 168, 280, 168, 0, 0, PadSurface, winInfo->width, winInfo->height, xPosWin, yPosWin, winInfo->surface, 0); + + SetFont(oldFont); // restore previous font + fo::func::message_exit(&MsgList); - sub_draw(280, 168, 280, 168, 0, 0, PadSurface, WinInfo->width, WinInfo->height, xPosWin, yPosWin, WinInfo->surface, 0); + *(long*)FO_VAR_card_old_fid1 = -1; // reset fid DeleteWordWrapList(StartLine); + delete[]PadSurface; CurrentLine = nullptr; NextLine = nullptr; - delete[]PadSurface; - WinInfo = nullptr; - SetFont(oldFont); //restore previous font - fo::func::message_exit(&MsgList); - //RedrawWin(fo::var::edit_win); + winInfo = nullptr; } -/* -void DrawCharNote(DWORD LstNum, char *TitleTxt, char *AltTitleTxt, char *Message) { - __asm { - MOV ECX,Message//100//DWORD PTR ds:[FO_VAR_folder_card_desc] - MOV EBX,AltTitleTxt//DWORD PTR ds:[FO_VAR_folder_card_title2] - MOV EDX,TitleTxt//DWORD PTR ds:[FO_VAR_folder_card_title] - MOV EAX,LstNum//11//LstNum//DWORD PTR ds:[FO_VAR_folder_card_fid] - CALL fo::funcoffs::DrawCard_ - } +static void _stdcall DrawCharNoteNewChar(bool type) { + DrawCharNote(type, fo::var::edit_win, 348, 272, charScrnBackSurface, 348, 272, 640, 480); } -*/ -//---------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void _stdcall HeroSelectWindow(int RaceStyleFlag) { - - if (!appModEnabled) return; - - bool isStyle = TRUE; - if (RaceStyleFlag == 0) isStyle = FALSE; +// op_hero_select_win +void _stdcall HeroSelectWindow(int raceStyleFlag) { + if (!HeroAppearance::appModEnabled) return; + bool isStyle = (raceStyleFlag != 0); DWORD resWidth = *(DWORD*)0x4CAD6B; DWORD resHeight = *(DWORD*)0x4CAD66; - int winRef = fo::func::win_add(resWidth/2 - 242, (resHeight - 100)/2 - 65, 484, 230, 100, 0x4); + int winRef = fo::func::win_add(resWidth / 2 - 242, (resHeight - 100) / 2 - 65, 484, 230, 100, 0x4); if (winRef == -1) return; int mouseWasHidden = fo::var::mouse_is_hidden; - if (mouseWasHidden) { - fo::func::mouse_show(); - } + if (mouseWasHidden) fo::func::mouse_show(); int oldMouse = fo::var::gmouse_current_cursor; fo::func::gmouse_set_cursor(1); BYTE *winSurface = fo::func::win_get_buf(winRef); - - BYTE *mainSurface; - mainSurface = new BYTE [484*230]; + BYTE *mainSurface = new BYTE [484 * 230]; DWORD tempObj; BYTE *tempSurface; - //perkwin + // perkwin tempSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 86), 0, 0, &tempObj); sub_draw(484, 230, 573, 230, 89, 0, tempSurface, 484, 230, 0, 0, mainSurface, 0); sub_draw(13, 230, 573, 230, 0, 0, tempSurface, 484, 230, 0, 0, mainSurface, 0); fo::func::art_ptr_unlock(tempObj); - //opbase + // opbase tempSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 220), 0, 0, &tempObj); sub_draw(164, 217, 164, 217, 0, 0, tempSurface, 484, 230, 12, 4, mainSurface, 0); fo::func::art_ptr_unlock(tempObj); - //use + // use tempSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 113), 0, 0, &tempObj); sub_draw(138, 132, 292, 376, 128, 20, tempSurface, 484, 230, 25, 38, mainSurface, 0); sub_draw(2, 132, 292, 376, 23, 224, tempSurface, 484, 230, 25, 38, mainSurface, 0); sub_draw(12, 4, 292, 376, 135, 148, tempSurface, 484, 230, 25, 166, mainSurface, 0); fo::func::art_ptr_unlock(tempObj); - //barter + // barter tempSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 111), 0, 0, &tempObj); - sub_draw(25, 52, 640, 191, 190, 54, tempSurface, 484, 230, 27, 57, mainSurface, 0); //button background up down + sub_draw(25, 52, 640, 191, 190, 54, tempSurface, 484, 230, 27, 57, mainSurface, 0); // button background up down fo::func::art_ptr_unlock(tempObj); - //loot + // loot tempSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 114), 0, 0, &tempObj); - sub_draw(116, 27, 537, 376, 392, 325, tempSurface, 484, 230, 36, 180, mainSurface, 0); //button background "done" + sub_draw(116, 27, 537, 376, 392, 325, tempSurface, 484, 230, 36, 180, mainSurface, 0); // button background "done" fo::func::art_ptr_unlock(tempObj); DWORD MenuUObj, MenuDObj; - BYTE *MenuUSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 299), 0, 0, &MenuUObj); //MENUUP Frm - BYTE *MenuDSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 300), 0, 0, &MenuDObj); //MENUDOWN Frm + BYTE *MenuUSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 299), 0, 0, &MenuUObj); // MENUUP Frm + BYTE *MenuDSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 300), 0, 0, &MenuDObj); // MENUDOWN Frm fo::func::win_register_button(winRef, 116, 181, 26, 26, -1, -1, -1, 0x0D, MenuUSurface, MenuDSurface, 0, 0x20); DWORD DidownUObj, DidownDObj; - BYTE *DidownUSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 93), 0, 0, &DidownUObj); //MENUUP Frm - BYTE *DidownDSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 94), 0, 0, &DidownDObj); //MENUDOWN Frm + BYTE *DidownUSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 93), 0, 0, &DidownUObj); // MENUUP Frm + BYTE *DidownDSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 94), 0, 0, &DidownDObj); // MENUDOWN Frm fo::func::win_register_button(winRef, 28, 84, 24, 25, -1, -1, -1, 0x150, DidownUSurface, DidownDSurface, 0, 0x20); DWORD DiupUObj, DiupDObj; - BYTE *DiupUSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 100), 0, 0, &DiupUObj); //MENUUP Frm - BYTE *DiupDSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 101), 0, 0, &DiupDObj); //MENUDOWN Frm + BYTE *DiupUSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 100), 0, 0, &DiupUObj); // MENUUP Frm + BYTE *DiupDSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 101), 0, 0, &DiupDObj); // MENUDOWN Frm fo::func::win_register_button(winRef, 28, 59, 23, 24, -1, -1, -1, 0x148, DiupUSurface, DiupDSurface, 0, 0x20); - int oldFont; - oldFont = GetFont(); + int oldFont = GetFont(); SetFont(0x67); - BYTE textColour = *(BYTE*)0x6A82F3; //PeanutButter colour -palette offset stored in mem char titleText[16]; - DWORD titleTextWidth; - //Get alternate text from ini if available + // Get alternate text from ini if available if (isStyle) { Translate("AppearanceMod", "StyleText", "Style", titleText, 16); } else { Translate("AppearanceMod", "RaceText", "Race", titleText, 16); } - titleTextWidth = fo::GetTextWidth(titleText); - - fo::PrintText(titleText, textColour, 94 - titleTextWidth/2, 10, titleTextWidth, 484, mainSurface); + BYTE textColour = fo::var::PeanutButter; // PeanutButter colour - palette offset stored in mem + DWORD titleTextWidth = fo::GetTextWidth(titleText); + fo::PrintText(titleText, textColour, 94 - titleTextWidth / 2, 10, titleTextWidth, 484, mainSurface); DWORD titleTextHeight = fo::GetTextHeight(); - //Title underline - memset(mainSurface + 484*(10 + titleTextHeight) + 94 - titleTextWidth/2, textColour, titleTextWidth ); - memset(mainSurface + 484*(10 + titleTextHeight + 1) + 94 - titleTextWidth/2, textColour, titleTextWidth ); + // Title underline + memset(mainSurface + 484 * (10 + titleTextHeight) + 94 - titleTextWidth / 2, textColour, titleTextWidth); + memset(mainSurface + 484 * (10 + titleTextHeight + 1) + 94 - titleTextWidth / 2, textColour, titleTextWidth); sub_draw(484, 230, 484, 230, 0, 0, mainSurface, 484, 230, 0, 0, winSurface, 0); - fo::func::win_show(winRef); - int raceVal = currentRaceVal, styleVal = currentStyleVal; //show default style when setting race - if (!isStyle) styleVal = 0; - LoadHeroDat(raceVal, styleVal); - - BYTE *ConDraw; - ConDraw = new BYTE [70*102]; - - //char TextBuf[12]; - - DWORD NewTick = 0, OldTick = 0; - - textColour = fo::var::GreenColor; //ConsoleGreen colour -palette offset stored in mem SetFont(0x65); - DWORD CritNum = fo::var::art_vault_guy_num; //pointer to current base hero critter FrmID - //DWORD CritNum = fo::var::obj_dude->artFID; //pointer to current armored hero critter FrmID - fo::FrmFrameData *CritFrm; - DWORD CritFrmObj = 0, CritOri = 0, CritWidth = 0, CritHeight = 0; - BYTE *CritSurface = nullptr; + BYTE *ConDraw = new BYTE [70 * 102]; - int button = 0, exitMenu = 0; + int button = 0; + bool drawFlag = true; // redraw flag for char note pad - bool drawFlag = TRUE; //redraw flag for char note pad + DWORD RotSpeed = *(DWORD*)0x47066B; // get rotation speed - inventory rotation speed + DWORD RedrawTick = 0, NewTick = 0, OldTick = 0; - DWORD RotSpeed = *(DWORD*)0x47066B; //get rotation speed -inventory rotation speed + DWORD critNum = fo::var::art_vault_guy_num; // pointer to current base hero critter FrmID + //DWORD critNum = fo::var::obj_dude->artFID; // pointer to current armored hero critter FrmID - DWORD RedrawTick = 0; + int raceVal = currentRaceVal, styleVal = currentStyleVal; // show default style when setting race + if (!isStyle) styleVal = 0; + LoadHeroDat(raceVal, styleVal, true); SetLoopFlag(LoopFlag::HEROWIN); - while (!exitMenu) { //main loop - NewTick = GetTickCount(); //timer for redraw - if (OldTick > NewTick) - OldTick = NewTick; + while (true) { // main loop + NewTick = GetTickCount(); // timer for redraw + if (OldTick > NewTick) OldTick = NewTick; - if (NewTick - OldTick > RotSpeed) { //time to rotate critter + if (NewTick - OldTick > RotSpeed) { // time to rotate critter OldTick = NewTick; - if (CritOri < 5) - CritOri++; - else CritOri = 0; + if (charRotOri < 5) + charRotOri++; + else + charRotOri = 0; } + if (RedrawTick > NewTick) RedrawTick = NewTick; - if (RedrawTick > NewTick) + if (NewTick - RedrawTick > 60) { // time to redraw RedrawTick = NewTick; - if (NewTick - RedrawTick > 60) { //time to redraw - RedrawTick = NewTick; sub_draw(70, 102, 484, 230, 66, 53, mainSurface, 70, 102, 0, 0, ConDraw, 0); - - CritFrm = fo::func::art_ptr_lock(BuildFrmId(1, CritNum), &CritFrmObj); - CritWidth = fo::func::art_frame_width(CritFrm, 0, CritOri); - CritHeight = fo::func::art_frame_length(CritFrm, 0, CritOri); - CritSurface = fo::func::art_frame_data(CritFrm, 0, CritOri); - sub_draw(CritWidth, CritHeight, CritWidth, CritHeight, 0, 0, CritSurface, 70, 102, 35 - CritWidth / 2, 51 - CritHeight / 2, ConDraw, 0); - fo::func::art_ptr_unlock(CritFrmObj); - CritSurface = nullptr; -/* - if (isStyle) sprintf_s(TextBuf, 12, "%2d\0", styleVal); - else sprintf_s(TextBuf, 12, "%2d\0", raceVal); - - PrintText(TextBuf, textColour, 2, 2, 64, 70, ConDraw); -*/ + DrawBody(critNum, ConDraw); sub_draw(70, 102, 70, 102, 0, 0, ConDraw, 484, 230, 66, 53, winSurface, 0); - if (drawFlag == TRUE) + if (drawFlag) { DrawCharNote(isStyle, winRef, 190, 29, mainSurface, 190, 29, 484, 230); - drawFlag = FALSE; - + drawFlag = false; + } fo::func::win_draw(winRef); } button = fo::func::get_input(); - if (button == 0x148) { //previous style/race -up arrow button pushed - drawFlag = TRUE; + if (button == 0x148) { // previous style/race -up arrow button pushed PlayAcm("ib1p1xx1"); - fo::func::art_flush(); if (isStyle) { - if (styleVal > 0) styleVal--; - if (LoadHeroDat(raceVal, styleVal) != 0) { + if (styleVal == 0) continue; + styleVal--; + if (LoadHeroDat(raceVal, styleVal, true) != 0) { styleVal = 0; LoadHeroDat(raceVal, styleVal); } - } else { //Race - if (raceVal > 0) styleVal = 0, raceVal--; - if (LoadHeroDat(raceVal, styleVal) != 0) { + drawFlag = true; + } else { // Race + if (raceVal == 0) continue; + raceVal--; + styleVal = 0; + if (LoadHeroDat(raceVal, styleVal, true) != 0) { raceVal = 0; LoadHeroDat(raceVal, styleVal); } - + drawFlag = true; } - } else if (button == 0x150) { //Next style/race -down arrow button pushed - drawFlag = TRUE; + } else if (button == 0x150) { // Next style/race - down arrow button pushed PlayAcm("ib1p1xx1"); - fo::func::art_flush(); if (isStyle) { styleVal++; - if (LoadHeroDat(raceVal, styleVal) != 0) { + if (LoadHeroDat(raceVal, styleVal, true) != 0) { styleVal--; LoadHeroDat(raceVal, styleVal); + } else { + drawFlag = true; } - } else { //Race - styleVal = 0, raceVal++; - if (LoadHeroDat(raceVal, styleVal) != 0) { + } else { // Race + raceVal++; + if (LoadHeroDat(raceVal, 0, true) != 0) { raceVal--; LoadHeroDat(raceVal, styleVal); + } else { + styleVal = 0; + drawFlag = true; } } - } else if (button == 0x0D) { //save and exit -Enter button pushed - exitMenu = -1; - if (!isStyle && currentRaceVal == raceVal) { //return style to previous value if no race change + } else if (button == 0x0D) { // save and exit - Enter button pushed + PlayAcm("ib1p1xx1"); + if (!isStyle && currentRaceVal == raceVal) { // return style to previous value if no race change styleVal = currentStyleVal; } currentRaceVal = raceVal; currentStyleVal = styleVal; - } else if (button == 0x1B) {//exit -ESC button pushed - exitMenu = -1; + break; + } else if (button == 0x1B) { // exit - ESC button pushed + break; } } ClearLoopFlag(LoopFlag::HEROWIN); - fo::func::art_flush(); - LoadHeroDat(currentRaceVal, currentStyleVal); + LoadHeroDat(currentRaceVal, currentStyleVal, true); SetAppearanceGlobals(currentRaceVal, currentStyleVal); fo::func::win_delete(winRef); delete[]mainSurface; delete[]ConDraw; + fo::func::art_ptr_unlock(MenuUObj); fo::func::art_ptr_unlock(MenuDObj); MenuUSurface = nullptr; @@ -1170,469 +1008,458 @@ void _stdcall HeroSelectWindow(int RaceStyleFlag) { SetFont(oldFont); fo::func::gmouse_set_cursor(oldMouse); - if (mouseWasHidden) { - fo::func::mouse_hide(); - } + if (mouseWasHidden) fo::func::mouse_hide(); } -void FixTextHighLight() { +static void FixTextHighLight() { __asm { - //redraw special text - mov eax, 7 - xor ebx, ebx - xor edx, edx - call fo::funcoffs::PrintBasicStat_ - //redraw trait options text - call fo::funcoffs::ListTraits_ - //redraw skills text - xor eax, eax - call fo::funcoffs::ListSkills_ - //redraw level text - call fo::funcoffs::PrintLevelWin_ - //redraw perks, karma, kill text - call fo::funcoffs::DrawFolder_ - //redraw hit points to crit chance text - call fo::funcoffs::ListDrvdStats_ - //redraw note pad area text - //call fo::funcoffs::DrawInfoWin_ + // redraw special text + mov eax, 7; + xor ebx, ebx; + xor edx, edx; + call fo::funcoffs::PrintBasicStat_; + // redraw trait options text + call fo::funcoffs::ListTraits_; + // redraw skills text + xor eax, eax; + call fo::funcoffs::ListSkills_; + // redraw level text + call fo::funcoffs::PrintLevelWin_; + // redraw perks, karma, kill text + call fo::funcoffs::DrawFolder_; + // redraw hit points to crit chance text + call fo::funcoffs::ListDrvdStats_; + // redraw note pad area text + //call fo::funcoffs::DrawInfoWin_; } } -//------------------------------------------- -void _stdcall DrawCharNoteNewChar(bool Style) { - DrawCharNote(Style, fo::var::edit_win, 348, 272, charScrnBackSurface, 348, 272, 640, 480); -} - -//------------------------------------------------------------------- -int _stdcall CheckCharButtons() { +static int _stdcall CheckCharButtons() { int button = fo::func::get_input(); - int raceVal = currentRaceVal; - int styleVal = currentStyleVal; - int drawFlag = -1; - if (fo::var::info_line == 0x503) { - button = 0x501; - } else if (fo::var::info_line == 0x504) { - button = 0x502; - } else if (fo::var::info_line == 0x501 || fo::var::info_line == 0x502) { + int infoLine = fo::var::info_line; + if (infoLine == 0x503 || infoLine == 0x504) { + fo::var::info_line -= 2; + *(DWORD*)FO_VAR_frstc_draw1 = 1; + DrawCharNoteNewChar(infoLine != 0x503); + } else if (infoLine == 0x501 || infoLine == 0x502) { switch (button) { - case 0x14B: //button =left - case 0x14D: //button =right - if (fo::var::glblmode == 1) { //if in char creation scrn - if (fo::var::info_line == 0x501) { - button = button + 0x3C6; - } else if (fo::var::info_line == 0x502) { - button = button + 0x3C6 + 1; - } - } - break; - case 0x148: //button =up - case 0x150: //button =down - if (fo::var::info_line == 0x501) { - button = 0x502; - } else if (fo::var::info_line == 0x502) { - button = 0x501; + case 0x14B: // button left + case 0x14D: // button right + if (fo::var::glblmode == 1) { // if in char creation scrn + if (infoLine == 0x501) { + button = button + 0x3C6; + } else if (infoLine == 0x502) { + button = button + 0x3C6 + 1; } + } break; - case 0x0D: //button =return - case 0x1B: //button =esc - case 0x1F4: //button =done - case 'd': //button =done - case 'D': //button =done - case 0x1F6: //button =cancel - case 'c': //button =cancel - case 'C': //button =cancel - if (fo::var::info_line == 0x501) { //for redrawing note when reentering char screen - fo::var::info_line = 0x503; - } else { - fo::var::info_line = 0x504; + case 0x148: // button up + case 0x150: // button down + if (infoLine == 0x501) { + button = 0x502; + } else if (infoLine == 0x502) { + button = 0x501; } break; - - default: + case 0x0D: // button return + case 0x1B: // button esc + case 0x1F4: // button done + case 'd': // button done + case 'D': // button done + case 0x1F6: // button cancel + case 'c': // button cancel + case 'C': // button cancel + fo::var::info_line += 2; // 0x503/0x504 for redrawing note when reentering char screen break; } } switch (button) { - case 0x9: //tab button pushed - if (fo::var::info_line >= 0x3D && fo::var::info_line < 0x4F) { //if menu ref in last menu go to race - button = 0x501, drawFlag = 0; - } - break; - case 0x501: //race button pushed - drawFlag = 0; - break; - case 0x502: //style button pushed - drawFlag = 1; + case 0x9: // tab button pushed + if (infoLine < 0x3D || infoLine >= 0x4F) { // if menu ref in last menu go to race + break; + } + button = 0x501; + case 0x501: // race title button pushed + if (infoLine != 0x501) { + fo::var::info_line = 0x501; + drawFlag = 3; + } break; - case 0x511: //race left button pushed - fo::func::art_flush(); - - if (raceVal > 0) styleVal = 0, raceVal--; - - if (LoadHeroDat(raceVal, styleVal) != 0) { - raceVal = 0; - LoadHeroDat(raceVal, styleVal); - } - drawFlag = 0; + case 0x502: // style title button pushed + if (infoLine != 0x502) { + fo::var::info_line = 0x502; + drawFlag = 2; + } break; - case 0x513: //race right button pushed - fo::func::art_flush(); - - styleVal = 0, raceVal++; - if (LoadHeroDat(raceVal, styleVal) != 0) { - raceVal--; - LoadHeroDat(raceVal, styleVal); - } - drawFlag = 0; + case 0x511: // race left button pushed + if (currentRaceVal == 0) { + drawFlag = 4; + break; + } + currentStyleVal = 0; // reset style + currentRaceVal--; + if (LoadHeroDat(currentRaceVal, currentStyleVal, true) != 0) { + currentRaceVal = 0; + LoadHeroDat(currentRaceVal, currentStyleVal); + } + drawFlag = 1; break; - case 0x512: //style left button pushed - fo::func::art_flush(); - - if (styleVal > 0) styleVal--; + case 0x513: // race right button pushed + currentRaceVal++; - if (LoadHeroDat(raceVal, styleVal) != 0) { - styleVal = 0; - LoadHeroDat(raceVal, styleVal); - } + if (LoadHeroDat(currentRaceVal, 0, true) != 0) { + currentRaceVal--; + LoadHeroDat(currentRaceVal, currentStyleVal); + drawFlag = 4; + } else { + currentStyleVal = 0; // reset style drawFlag = 1; + } break; - case 0x514: //style right button pushed - fo::func::art_flush(); - - styleVal++; - if (LoadHeroDat(raceVal, styleVal) != 0) { - styleVal--; - LoadHeroDat(raceVal, styleVal); - } - drawFlag = 1; + case 0x512: // style left button pushed + if (currentStyleVal == 0) { + drawFlag = 4; + break; + } + currentStyleVal--; + if (LoadHeroDat(currentRaceVal, currentStyleVal, true)) { + currentStyleVal = 0; + LoadHeroDat(currentRaceVal, currentStyleVal, true); + } + drawFlag = 0; break; - default: + case 0x514: // style right button pushed + currentStyleVal++; + + if (LoadHeroDat(currentRaceVal, currentStyleVal, true) != 0) { + currentStyleVal--; + LoadHeroDat(currentRaceVal, currentStyleVal); + drawFlag = 4; + } else { + drawFlag = 0; + } break; } - currentRaceVal = raceVal; - currentStyleVal = styleVal; - if (drawFlag == 1) { - PlayAcm("ib3p1xx1"); - fo::var::info_line = 0x502; - FixTextHighLight(); - DrawCharNoteNewChar(1); - //DrawCharNote(1, fo::var::edit_win, 348, 272, CharScrnBackSurface, 348, 272, 640, 480); - } - else if (drawFlag == 0) { - PlayAcm("ib3p1xx1"); - fo::var::info_line = 0x501; + if (drawFlag != -1) { + bool style = false; // Race; + switch (drawFlag) { + case 0: + fo::var::info_line = 0x502; + style = true; + goto play; + case 1: + fo::var::info_line = 0x501; + play: + PlayAcm("ib3p1xx1"); + break; + case 2: + style = true; + case 3: + PlayAcm("ISDXXXX1"); + break; + default: + PlayAcm("IB3LU1X1"); + return button; + } FixTextHighLight(); - DrawCharNoteNewChar(0); - //DrawCharNote(0, fo::var::edit_win, 348, 272, CharScrnBackSurface, 348, 272, 640, 480); + DrawCharNoteNewChar(style); } - - DrawPCConsole(); //(fo::var::edit_win, 338, 78, CharScrnBackSurface, 338, 78, 640, 480); + DrawPCConsole(); return button; } -//------------------------------------------ -static void __declspec(naked) CheckCharScrnButtons(void) { +static void __declspec(naked) CheckCharScrnButtons() { __asm { - call CheckCharButtons - cmp eax, 0x500 - jl EndFunc - cmp eax, 0x515 - jg EndFunc - pop eax //ditch old ret addr - push 0x431E8A //recheck buttons if app mod button -EndFunc: - ret + call CheckCharButtons; + cmp eax, 0x500; + jl endFunc; + cmp eax, 0x515; + jg endFunc; + add esp, 4; // ditch old ret addr + push 0x431E8A; // recheck buttons if app mod button +endFunc: + retn; } } -//------------------------------- -void DeleteCharSurfaces() { - delete[] newButt01Surface; - newButt01Surface = nullptr; - delete[] charScrnBackSurface; - charScrnBackSurface = nullptr; -} +static void __fastcall HeroGenderChange(long gender) { + // get PC stat current gender + long newGender = fo::func::stat_level(fo::var::obj_dude, fo::STAT_gender); + if (newGender == gender) return; // check if gender has been changed -//------------------------------------------ -static void __declspec(naked) CharScrnEnd(void) { - __asm { - pushad - call DeleteCharSurfaces - popad - mov ebp, dword ptr ds:[FO_VAR_info_line] - retn + long baseModel = (newGender) // check if male 0 + ? *(DWORD*)0x5108AC // base female model + : fo::var::art_vault_person_nums; // base male model + + // adjust base hero art + baseModel += critterListSize; + fo::var::art_vault_guy_num = baseModel; + + // reset race and style to defaults + currentRaceVal = 0; + currentStyleVal = 0; + LoadHeroDat(0, 0); + + fo::func::proto_dude_update_gender(); + + // Check If Race or Style selected to redraw info note + int infoLine = fo::var::info_line; + if (infoLine == 0x501 || infoLine == 0x502) { + DrawCharNoteNewChar(infoLine != 0x501); } } -//------------------------------------------ -static void __declspec(naked) SexScrnEnd(void) { +static void __declspec(naked) SexScrnEnd() { using namespace fo; __asm { - pushad - mov edx, STAT_gender - mov eax, dword ptr ds:[FO_VAR_obj_dude] - call fo::funcoffs::stat_level_ //get PC stat current gender - mov ecx, eax - call fo::funcoffs::SexWindow_ //call gender selection window - mov edx, STAT_gender - mov eax, dword ptr ds:[FO_VAR_obj_dude] - call fo::funcoffs::stat_level_ //get PC stat current gender - cmp ecx, eax //check if gender has been changed - je EndFunc - - xor ebx, ebx - //cmp byte ptr ds:[FO_VAR_gmovie_played_list + 0x3],1 //check if wearing vault suit - //jne NoVaultSuit - //mov ebx, 0x8 -//NoVaultSuit: - test eax, eax //check if male 0 - jnz IsFemale - mov eax, dword ptr ds:[ebx + FO_VAR_art_vault_person_nums] //base male model - jmp ChangeSex -IsFemale: - mov eax, dword ptr ds:[ebx + 0x5108AC] //base female model -ChangeSex: - call AdjustHeroBaseArt - //mov dword ptr ds:[FO_VAR_art_vault_guy_num], eax //current base dude model - mov eax, dword ptr ds:[FO_VAR_obj_dude] //dude state structure - call fo::funcoffs::inven_worn_ - mov currentRaceVal, 0 //reset race and style to defaults - mov currentStyleVal, 0 - - push currentStyleVal - push currentRaceVal - call LoadHeroDat - call RefreshPCArt - //Check If Race or Style selected to redraw info note - cmp dword ptr ds:[FO_VAR_info_line], 0x501 - jne CheckIfStyle - push 0 - call DrawCharNoteNewChar -CheckIfStyle: - cmp dword ptr ds:[FO_VAR_info_line], 0x502 - jne EndFunc - push 1 - call DrawCharNoteNewChar - -EndFunc: - popad - retn + push edx; + mov edx, STAT_gender; + mov eax, dword ptr ds:[FO_VAR_obj_dude]; + call fo::funcoffs::stat_level_; // get PC stat current gender + mov ecx, eax; // gender + call fo::funcoffs::SexWindow_; // call gender selection window +/* + xor ebx, ebx; + cmp byte ptr ds:[FO_VAR_gmovie_played_list + 0x3], 1 // check if wearing vault suit + jne NoVaultSuit + mov ebx, 0x8 +NoVaultSuit: + mov eax, dword ptr ds:[ebx + FO_VAR_art_vault_person_nums]; // base male model +*/ + call HeroGenderChange; + pop edx; + retn; } } -//------------------------------------------ -static void __declspec(naked) AddCharScrnButtons(void) { +// Create race and style selection buttons when creating a character +static void __declspec(naked) AddCharScrnButtons() { __asm { - push ebp //prolog - mov ebp, esp - sub esp, __LOCAL_SIZE - pushad + pushad; // prolog + mov ebp, esp; + sub esp, __LOCAL_SIZE; } int WinRef; - WinRef = fo::var::edit_win; //char screen window ref + WinRef = fo::var::edit_win; // char screen window ref - //race and style buttons + // race and style title buttons fo::func::win_register_button(WinRef, 332, 0, 82, 32, -1, -1, 0x501, -1, 0, 0, 0, 0); fo::func::win_register_button(WinRef, 332, 226, 82, 32, -1, -1, 0x502, -1, 0, 0, 0, 0); - if (fo::var::glblmode == 1) { //equals 1 if new char screen - equals 0 if ingame char screen - if (newButt01Surface == nullptr) { - newButt01Surface = new BYTE [20*18*4]; + if (fo::var::glblmode == 1) { // equals 1 if new char screen - equals 0 if ingame char screen + if (newButtonSurface == nullptr) { + newButtonSurface = new BYTE [20 * 18 * 4]; - DWORD frmLock; //frm objects for char screen Appearance button + DWORD frmLock; // frm objects for char screen Appearance button BYTE *frmSurface; - frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 122), 0, 0, &frmLock); //SLUFrm - sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18*4, 0, 0, newButt01Surface, 0x0); + frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 122), 0, 0, &frmLock); // SLUFrm + sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18 * 4, 0, 0, newButtonSurface, 0); fo::func::art_ptr_unlock(frmLock); - frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 123), 0, 0, &frmLock); //SLDFrm - sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18*4, 0, 18, newButt01Surface, 0x0); + + frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 123), 0, 0, &frmLock); // SLDFrm + sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18 * 4, 0, 18, newButtonSurface, 0); fo::func::art_ptr_unlock(frmLock); - frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 124), 0, 0, &frmLock); //SRUFrm - sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18*4, 0, 18*2, newButt01Surface, 0x0); + + frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 124), 0, 0, &frmLock); // SRUFrm + sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18 * 4, 0, 18 * 2, newButtonSurface, 0); fo::func::art_ptr_unlock(frmLock); - frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 125), 0, 0, &frmLock); //SRDFrm - sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18*4, 0, 18*3, newButt01Surface, 0x0); + + frmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 125), 0, 0, &frmLock); // SRDFrm + sub_draw(20, 18, 20, 18, 0, 0, frmSurface, 20, 18 * 4, 0, 18 * 3, newButtonSurface, 0); fo::func::art_ptr_unlock(frmLock); + frmSurface = nullptr; } - //check if Data exists for other races male or female, and if so enable race selection buttons. - if (GetFileAttributes("Appearance\\hmR01S00\0") != INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR01S00\0") != INVALID_FILE_ATTRIBUTES || - GetFileAttributes("Appearance\\hmR01S00.dat\0") != INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR01S00.dat\0") != INVALID_FILE_ATTRIBUTES) { - //race selection buttons - fo::func::win_register_button(WinRef, 348, 37, 20, 18, -1, -1, -1, 0x511, newButt01Surface, newButt01Surface + (20*18), 0, 0x20); - fo::func::win_register_button(WinRef, 373, 37, 20, 18, -1, -1, -1, 0x513, newButt01Surface + (20*18*2), newButt01Surface + (20*18*3), 0, 0x20); + // check if Data exists for other races male or female, and if so enable race selection buttons + if (GetFileAttributes("Appearance\\hmR01S00") != INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR01S00") != INVALID_FILE_ATTRIBUTES || + GetFileAttributes("Appearance\\hmR01S00.dat") != INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR01S00.dat") != INVALID_FILE_ATTRIBUTES) { + // race selection buttons + fo::func::win_register_button(WinRef, 348, 37, 20, 18, -1, -1, -1, 0x511, newButtonSurface, newButtonSurface + (20 * 18), 0, 0x20); + fo::func::win_register_button(WinRef, 373, 37, 20, 18, -1, -1, -1, 0x513, newButtonSurface + (20 * 18 * 2), newButtonSurface + (20 * 18 * 3), 0, 0x20); + } + // check if Data exists for other styles male or female, and if so enable style selection buttons + if (GetFileAttributes("Appearance\\hmR00S01") != INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR00S01") != INVALID_FILE_ATTRIBUTES || + GetFileAttributes("Appearance\\hmR00S01.dat") != INVALID_FILE_ATTRIBUTES || GetFileAttributes("Appearance\\hfR00S01.dat") != INVALID_FILE_ATTRIBUTES) { + // style selection buttons + fo::func::win_register_button(WinRef, 348, 199, 20, 18, -1, -1, -1, 0x512, newButtonSurface, newButtonSurface + (20 * 18), 0, 0x20); + fo::func::win_register_button(WinRef, 373, 199, 20, 18, -1, -1, -1, 0x514, newButtonSurface + (20 * 18 * 2), newButtonSurface + (20 * 18 * 3), 0, 0x20); } - //style selection buttons - fo::func::win_register_button(WinRef, 348, 199, 20, 18, -1, -1, -1, 0x512, newButt01Surface, newButt01Surface+(20*18), 0, 0x20); - fo::func::win_register_button(WinRef, 373, 199, 20, 18, -1, -1, -1, 0x514, newButt01Surface + (20*18*2), newButt01Surface + (20*18*3), 0, 0x20); } __asm { - popad - mov esp, ebp //epilog - pop ebp - //move tag skills button to fit Appearance interface - mov edx, 0x1AA; //0x18C+36 was 0x18C tag/skills button xpos - retn + mov esp, ebp; // epilog + popad; + // move tag skills button to fit Appearance interface + mov edx, 396 + 30; // tag/skills button xpos offset + retn; } } -//------------------------------------------ -static void __declspec(naked) FixCharScrnBack(void) { -//00432B92 |. A3 A4075700 MOV DWORD PTR DS:[5707A4],EAX +// Loading or creating a background image for the character creation/editing interface +static void __declspec(naked) FixCharScrnBack() { __asm { - mov dword ptr ds:[FO_VAR_bckgnd], eax //surface ptr for char scrn back - test eax, eax //check if frm loaded ok - je EndFunc - - push ebp // prolog - mov ebp, esp - sub esp, __LOCAL_SIZE - pushad + mov dword ptr ds:[FO_VAR_bckgnd], eax; // surface ptr for char scrn back + test eax, eax; // check if frm loaded ok + je endFunc; + // prolog + pushad; + mov ebp, esp; + sub esp, __LOCAL_SIZE; } if (charScrnBackSurface == nullptr) { - charScrnBackSurface = new BYTE [640*480]; + charScrnBackSurface = new BYTE [640 * 480]; - UnlistedFrm *frm; - frm = LoadUnlistedFrm((*(long*)FO_VAR_glblmode) ? "AppChCrt.frm" : "AppChEdt.frm", 6); + UnlistedFrm *frm = LoadUnlistedFrm((fo::var::glblmode) ? "AppChCrt.frm" : "AppChEdt.frm", fo::OBJ_TYPE_INTRFACE); if (frm != nullptr) { sub_draw(640, 480, 640, 480, 0, 0, frm->frames[0].indexBuff, 640, 480, 0, 0, charScrnBackSurface, 0); delete frm; } else { - BYTE *OldCharScrnBackSurface = fo::var::bckgnd; //char screen background frm surface + BYTE *OldCharScrnBackSurface = fo::var::bckgnd; // char screen background frm surface - //copy old charscrn surface to new + // copy old charscrn surface to new sub_draw(640, 480, 640, 480, 0, 0, OldCharScrnBackSurface, 640, 480, 0, 0, charScrnBackSurface, 0); - //copy Tag Skill Counter background to the right - sub_draw(38, 26, 640, 480, 519, 228, OldCharScrnBackSurface, 640, 480, 519+36, 228, charScrnBackSurface, 0); - //copy a blank part of the Tag Skill Bar hiding the old counter - sub_draw(38, 26, 640, 480, 460, 228, OldCharScrnBackSurface, 640, 480, 519, 228, charScrnBackSurface, 0); + // copy Tag Skill Counter background to the right + sub_draw(38, 26, 640, 480, 519, 228, OldCharScrnBackSurface, 640, 480, 519 + 36, 228, charScrnBackSurface, 0); - sub_draw(36, 258, 640, 480, 332, 0, OldCharScrnBackSurface, 640, 480, 408, 0, charScrnBackSurface, 0); //shift behind button rail - sub_draw(6, 32, 640, 480, 331, 233, OldCharScrnBackSurface, 640, 480, 330, 6, charScrnBackSurface, 0); //shadow for style/race button + // copy a blank part of the Tag Skill Bar hiding the old counter + sub_draw(38, 26, 640, 480, 460, 228, OldCharScrnBackSurface, 640, 480, 519, 228, charScrnBackSurface, 0); + sub_draw(36, 258, 640, 480, 332, 0, OldCharScrnBackSurface, 640, 480, 408, 0, charScrnBackSurface, 0); // shift behind button rail + sub_draw(6, 32, 640, 480, 331, 233, OldCharScrnBackSurface, 640, 480, 330, 6, charScrnBackSurface, 0); // shadow for style/race button - DWORD FrmObj, FrmMaskObj; //frm objects for char screen Appearance button - BYTE *FrmSurface,*FrmMaskSurface; + DWORD FrmObj, FrmMaskObj; // frm objects for char screen Appearance button + BYTE *FrmSurface, *FrmMaskSurface; FrmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 113), 0, 0, &FrmObj); - sub_draw(81, 132, 292, 376, 163, 20, FrmSurface, 640, 480, 331, 63, charScrnBackSurface, 0); //char view win - sub_draw(79, 31, 292, 376, 154, 228, FrmSurface, 640, 480, 331, 32, charScrnBackSurface, 0); //upper char view win - sub_draw(79, 30, 292, 376, 158, 236, FrmSurface, 640, 480, 331, 195, charScrnBackSurface, 0); //lower char view win + sub_draw(81, 132, 292, 376, 163, 20, FrmSurface, 640, 480, 331, 63, charScrnBackSurface, 0); // char view win + sub_draw(79, 31, 292, 376, 154, 228, FrmSurface, 640, 480, 331, 32, charScrnBackSurface, 0); // upper char view win + sub_draw(79, 30, 292, 376, 158, 236, FrmSurface, 640, 480, 331, 195, charScrnBackSurface, 0); // lower char view win fo::func::art_ptr_unlock(FrmObj); - //Sexoff Frm + // Sexoff Frm FrmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 188), 0, 0, &FrmObj); - //Sex button mask frm + // Sex button mask frm FrmMaskSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 187), 0, 0, &FrmMaskObj); - sub_draw(80, 28, 80, 32, 0, 0, FrmMaskSurface, 80, 32, 0, 0, FrmSurface, 0x39); //mask for style and race buttons + sub_draw(80, 28, 80, 32, 0, 0, FrmMaskSurface, 80, 32, 0, 0, FrmSurface, 0x39); // mask for style and race buttons fo::func::art_ptr_unlock(FrmMaskObj); FrmMaskSurface = nullptr; - FrmSurface[80*32 - 1] = 0; - FrmSurface[80*31 - 1] = 0; - FrmSurface[80*30 - 1] = 0; + FrmSurface[80 * 32 - 1] = 0; + FrmSurface[80 * 31 - 1] = 0; + FrmSurface[80 * 30 - 1] = 0; - FrmSurface[80*32 - 2] = 0; - FrmSurface[80*31 - 2] = 0; - FrmSurface[80*30 - 2] = 0; + FrmSurface[80 * 32 - 2] = 0; + FrmSurface[80 * 31 - 2] = 0; + FrmSurface[80 * 30 - 2] = 0; - FrmSurface[80*32 - 3] = 0; - FrmSurface[80*31 - 3] = 0; - FrmSurface[80*30 - 3] = 0; + FrmSurface[80 * 32 - 3] = 0; + FrmSurface[80 * 31 - 3] = 0; + FrmSurface[80 * 30 - 3] = 0; - FrmSurface[80*32 - 4] = 0; - FrmSurface[80*31 - 4] = 0; - FrmSurface[80*30 - 4] = 0; + FrmSurface[80 * 32 - 4] = 0; + FrmSurface[80 * 31 - 4] = 0; + FrmSurface[80 * 30 - 4] = 0; - sub_draw(80, 32, 80, 32, 0, 0, FrmSurface, 640, 480, 332, 0, charScrnBackSurface, 0); //style and race buttons - sub_draw(80, 32, 80, 32, 0, 0, FrmSurface, 640, 480, 332, 225, charScrnBackSurface, 0); //style and race buttons + sub_draw(80, 32, 80, 32, 0, 0, FrmSurface, 640, 480, 332, 0, charScrnBackSurface, 0); // style and race buttons + sub_draw(80, 32, 80, 32, 0, 0, FrmSurface, 640, 480, 332, 225, charScrnBackSurface, 0); // style and race buttons fo::func::art_ptr_unlock(FrmObj); - //frm background for char screen Appearance button - FrmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 174), 0, 0, &FrmObj); //Pickchar frm - sub_draw(69, 20, 640, 480, 282, 320, FrmSurface, 640, 480, 337, 37, charScrnBackSurface, 0); //button backround top - sub_draw(69, 20, 640, 480, 282, 320, FrmSurface, 640, 480, 337, 199, charScrnBackSurface, 0); //button backround bottom - sub_draw(47, 16, 640, 480, 94, 394, FrmSurface, 640, 480, 347, 39, charScrnBackSurface, 0); //cover buttons pics top - sub_draw(47, 16, 640, 480, 94, 394, FrmSurface, 640, 480, 347, 201, charScrnBackSurface, 0); //cover buttons pics bottom + // frm background for char screen Appearance button + FrmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 174), 0, 0, &FrmObj); // Pickchar frm + sub_draw(69, 20, 640, 480, 282, 320, FrmSurface, 640, 480, 337, 37, charScrnBackSurface, 0); // button backround top + sub_draw(69, 20, 640, 480, 282, 320, FrmSurface, 640, 480, 337, 199, charScrnBackSurface, 0); // button backround bottom + sub_draw(47, 16, 640, 480, 94, 394, FrmSurface, 640, 480, 347, 39, charScrnBackSurface, 0); // cover buttons pics top + sub_draw(47, 16, 640, 480, 94, 394, FrmSurface, 640, 480, 347, 201, charScrnBackSurface, 0); // cover buttons pics bottom fo::func::art_ptr_unlock(FrmObj); FrmSurface = nullptr; } - int oldFont; - oldFont = GetFont(); + int oldFont = GetFont(); SetFont(0x67); - BYTE PeanutButter; - PeanutButter = *(BYTE*)0x6A82F3; //palette offset stored in mem - char RaceText[8], StyleText[8]; - DWORD raceTextWidth, styleTextWidth; - //Get alternate text from ini if available + char RaceText[8], StyleText[8]; + // Get alternate text from ini if available Translate("AppearanceMod", "RaceText", "Race", RaceText, 8); Translate("AppearanceMod", "StyleText", "Style", StyleText, 8); - raceTextWidth = fo::GetTextWidth(RaceText); - styleTextWidth = fo::GetTextWidth(StyleText); + DWORD raceTextWidth = fo::GetTextWidth(RaceText); + DWORD styleTextWidth = fo::GetTextWidth(StyleText); + + BYTE PeanutButter = fo::var::PeanutButter; // palette offset stored in mem - fo::PrintText(RaceText, PeanutButter, 372 - raceTextWidth/2, 6, raceTextWidth, 640, charScrnBackSurface); - fo::PrintText(StyleText, PeanutButter, 372 - styleTextWidth/2, 231, styleTextWidth, 640, charScrnBackSurface); + fo::PrintText(RaceText, PeanutButter, 372 - raceTextWidth / 2, 6, raceTextWidth, 640, charScrnBackSurface); + fo::PrintText(StyleText, PeanutButter, 372 - styleTextWidth / 2, 231, styleTextWidth, 640, charScrnBackSurface); SetFont(oldFont); } + fo::var::bckgnd = charScrnBackSurface; // surface ptr for char scrn back + __asm { - popad - mov esp, ebp //epilog - pop ebp - mov eax, charScrnBackSurface - mov dword ptr ds:[FO_VAR_bckgnd], eax //surface ptr for char scrn back -EndFunc: - retn + mov esp, ebp; // epilog + popad; +endFunc: + retn; } } +static void DeleteCharSurfaces() { + delete[] newButtonSurface; + newButtonSurface = nullptr; -//Adjust PC SFX Name--------------------------------------------- + delete[] charScrnBackSurface; + charScrnBackSurface = nullptr; +} + +static void __declspec(naked) CharScrnEnd() { + __asm { + push eax; + call DeleteCharSurfaces; + pop eax; + mov ebp, dword ptr ds:[FO_VAR_info_line]; + retn; + } +} + +//////////////////////////////////////////////////////////////////////FIX FUNCTIONS//////////////////////////////////////////////////////////////////////// + +// Adjust PC SFX acm name. Skip Underscore char at the start of PC App Name static void __declspec(naked) FixPcSFX() { -//Skip Underscore char at the start of PC App Name __asm { - mov ah, byte ptr ds:[ebx] - cmp ah, 0x5F //check if Name begins with an '_' character - jne ExitFunc - add ebx, 1 //shift address to next char -ExitFunc: - //restore original code - mov eax, ebx - // TODO: use constant - cmp dword ptr ds:[0x518E30], 0 - ret + cmp byte ptr ds:[ebx], 0x5F; // check if Name begins with an '_' character + jne endFunc; + inc ebx; // shift address to next char +endFunc: + // restore original code + mov eax, ebx; + cmp dword ptr ds:[FO_VAR_gsound_initialized], 0; + retn; } } /* -//Set path to normal before printing or saving character details------------ +// Set path to normal before printing or saving character details static void __declspec(naked) FixCharScrnSaveNPrint() { -//00432359 |> E8 AA580000 |CALL fallout2.00437C08 __asm { push TempPathPtr //store current path - mov eax, _paths - mov TempPathPtr, eax //set path to normal + mov eax, _paths + mov TempPathPtr, eax //set path to normal push esi call OptionWindow_ //call char-scrn menu function - pop esi - pop TempPathPtr //restore stored path + pop esi + pop TempPathPtr //restore stored path call RefreshPCArt ret @@ -1640,82 +1467,54 @@ static void __declspec(naked) FixCharScrnSaveNPrint() { } */ -// Load Appearance data from GCD file------------ -void _stdcall LoadGCDAppearance(fo::DbFile* fileStream) { +static void __declspec(naked) FixPcCriticalHitMsg() { + __asm { + cmp eax, critterListSize; // check if critter art in PC range + jle endFunc; + sub eax, critterListSize; // shift critter art index down out of hero range +endFunc: + jmp fo::funcoffs::art_alias_num_; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Load Appearance data from GCD file +static void __fastcall LoadGCDAppearance(fo::DbFile* fileStream) { currentRaceVal = 0; currentStyleVal = 0; DWORD temp; - if (fo::func::db_freadInt(fileStream, &temp) != -1) { + if (fo::func::db_freadInt(fileStream, &temp) != -1 && temp < 100) { currentRaceVal = (int)temp; - if (fo::func::db_freadInt(fileStream, &temp) != -1) { + if (fo::func::db_freadInt(fileStream, &temp) != -1 && temp < 100) { currentStyleVal = (int)temp; } } - - //reset hero appearance - fo::func::art_flush(); - LoadHeroDat(currentRaceVal, currentStyleVal); - RefreshPCArt(); - fo::func::db_fclose(fileStream); + + // load hero appearance + if (LoadHeroDat(currentRaceVal, currentStyleVal, true) != 0) { // if load fails + currentStyleVal = 0; // set style to default + if (LoadHeroDat(currentRaceVal, currentStyleVal) != 0) { // if race fails with style at default + currentRaceVal = 0; // set race to default + LoadHeroDat(currentRaceVal, currentStyleVal); + } + } + fo::func::proto_dude_update_gender(); } -// Save Appearance data to GCD file-------------- -void _stdcall SaveGCDAppearance(fo::DbFile *FileStream) { +// Save Appearance data to GCD file +static void __fastcall SaveGCDAppearance(fo::DbFile *FileStream) { if (fo::func::db_fwriteInt(FileStream, (DWORD)currentRaceVal) != -1) { fo::func::db_fwriteInt(FileStream, (DWORD)currentStyleVal); } - fo::func::db_fclose(FileStream); } -// Reset Appearance when selecting "Create Character" from the New Char screen------ -static void __declspec(naked) CreateCharReset() { - __asm { - mov currentRaceVal, 0 //reset race and style to defaults - mov currentStyleVal, 0 - - push currentStyleVal - push currentRaceVal - call LoadHeroDat - call RefreshPCArt - - mov eax, 1 - ret - } -} - -//--------------------------------- -void HeroAppearanceModExit() { - if (!appModEnabled) return; +static void EnableHeroAppearanceMod() { + HeroAppearance::appModEnabled = true; - if (heroPathPtr) { - delete[] heroPathPtr->path; - delete heroPathPtr; - } - if (racePathPtr) { - delete[] racePathPtr->path; - delete racePathPtr; - } -} - -//------------------------------------------------- -static void __declspec(naked) FixPcCriticalHitMsg() { - __asm { - and eax, 0x00000FFF - cmp eax, critterListSize //check if critter art in PC range - jle EndFunc - sub eax, critterListSize //shift critter art index down out of hero range -EndFunc: - ret - } -} - -//-------------------------------------------------------------------------- -void EnableHeroAppearanceMod() { - appModEnabled = true; - - //setup paths + // setup paths heroPathPtr = new fo::PathNode; racePathPtr = new fo::PathNode; heroPathPtr->path = new char[64]; @@ -1726,111 +1525,119 @@ void EnableHeroAppearanceMod() { heroPathPtr->pDat = nullptr; racePathPtr->pDat = nullptr; - //Check if new Appearance char scrn button pushed + // Check if new Appearance char scrn button pushed (editor_design_) HookCall(0x431E9D, CheckCharScrnButtons); - //Destroy new Appearance button mem after use + // Destroy new Appearance button mem after use (editor_design_) MakeCall(0x4329D8, CharScrnEnd, 1); - //Check if sex has changed and reset char appearance + // Check if sex has changed and reset char appearance (editor_design_) HookCall(0x4322E8, SexScrnEnd); - //Load New Hero Art + // Load New Hero Art (xfopen_) MakeCall(0x4DEEE5, LoadNewHeroArt, 1); - //Divert critter frm file name function exit for file checking - SafeWrite8(0x419520, 0xEB); //divert func exit + // Divert critter frm file name function exit for file checking (art_get_name_) + SafeWrite8(0x419520, 0xEB); // divert func exit SafeWrite32(0x419521, 0x9090903E); - //Check if new hero art exists otherwise use regular art + // Check if new hero art exists otherwise use regular art (art_get_name_) MakeCall(0x419560, CheckHeroExist); - //Double size of critter art index creating a new area for hero art - MakeCall(0x4196AA, DoubleArt, 1); + // Double size of critter art index creating a new area for hero art (art_read_lst_) + HookCall(0x4196B0, DoubleArt); - //Add new hero critter names at end of critter list + // Add new hero critter names at end of critter list (art_init_) MakeCall(0x418B39, AddHeroCritNames); - //Shift base hero critter art offset up into hero section + // Shift base hero critter art offset up into hero section (proto_dude_update_gender_) MakeCall(0x49F9DA, AdjustHeroBaseArt); - //Adjust hero art index offset when changing armor - //MakeCall(0x4717D6, AdjustHeroArmorArt); + // Adjust hero art index offset when changing armor (adjust_fid_) HookCall(0x4717D1, AdjustHeroArmorArt); - //Hijack Save Hero State Structure fuction address 9CD54800 - //Return hero art index offset back to normal before saving + // Hijack Save Hero State Structure fuction address 9CD54800 + // Return hero art index offset back to normal before saving SafeWrite32(0x519400, (DWORD)&SavCritNumFix); - - - //Tag Skills text x pos - SafeWrite32(0x433372, 0x24826 + 36); //Tag Skills text x pos1 - SafeWrite32(0x4362BE, 0x24826 + 36); //Tag Skills text x pos2 - SafeWrite32(0x4362F2, 0x20A + 36); //Tag Skills num counter2 x pos1 - SafeWrite32(0x43631E, 0x20A + 36); //Tag Skills num counter2 x pos2 - - //Add new Appearance mod buttons + // Add new Appearance mod buttons (RegInfoAreas_) MakeCall(0x43A788, AddCharScrnButtons); - //Mod char scrn background and add new app mod graphics. also adjust tag/skill button x pos + // Mod char scrn background and add new app mod graphics. also adjust tag/skill button x pos (CharEditStart_) MakeCall(0x432B92, FixCharScrnBack); - //skill points - SafeWrite32(0x436262, 0x24810 + 36); //Skill Points text x pos - SafeWrite32(0x43628A, 0x20A + 36); //Skill Points num counter x pos1 - SafeWrite32(0x43B5B2, 0x20A + 36); //Skill Points num counter x pos2 - - //make room for char view window - SafeWrite32(0x433678, 347 + 76); //shift skill buttons right 80 - SafeWrite32(0x4363CE, 380 + 68); //shift skill name text right 80 - SafeWrite32(0x43641C, 573 + 10); //shift skill % num text right 80 + // Tag Skills text x pos + SafeWrite32(0x433372, 0x24826 + 36); // Tag Skills text x pos1 + SafeWrite32(0x4362BE, 0x24826 + 36); // Tag Skills text x pos2 + SafeWrite32(0x4362F2, 522 + 36); // Tag Skills num counter2 x pos1 + SafeWrite32(0x43631E, 522 + 36); // Tag Skills num counter2 x pos2 + + // skill points + SafeWrite32(0x436262, 0x24810 + 36); // Skill Points text x pos + SafeWrite32(0x43628A, 522 + 36); // Skill Points num counter x pos1 + SafeWrite32(0x43B5B2, 522 + 36); // Skill Points num counter x pos2 + + // make room for char view window + SafeWrite32(0x433678, 347 + 76); // shift skill buttons right 80 + SafeWrite32(0x4363CE, 380 + 68); // shift skill name text right 80 + SafeWrite32(0x43641C, 573 + 10); // shift skill % num text right 80 SafeWrite32(0x43A74C, 223 - 76 + 10); // skill list mouse area button width - SafeWrite32(0x43A75B, 370 + 76); // skill list mouse area button xpos - SafeWrite32(0x436220, 0x0DFC + 68); //"skill" text xpos - SafeWrite32(0x43A71E, 0xDF - 68); //skill button width - SafeWrite32(0x43A72A, 0x178 + 68); //skill button xpos + SafeWrite32(0x43A75B, 370 + 76); // skill list mouse area button xpos + SafeWrite32(0x436220, 3580 + 68); // skill text xpos + SafeWrite32(0x43A71E, 223 - 68); // skill button width + SafeWrite32(0x43A72A, 376 + 68); // skill button xpos - //redraw area for skill list - SafeWrite32(0x4361C4, 370 + 76); //xpos - SafeWrite32(0x4361D9, 270 - 76); //width - SafeWrite32(0x4361DE, 370 + 76); //xpos + // redraw area for skill list + SafeWrite32(0x4361C4, 370 + 76); // xpos + SafeWrite32(0x4361D9, 270 - 76); // width + SafeWrite32(0x4361DE, 370 + 76); // xpos - //skill slider thingy - SafeWrite32(0x43647C, 592 + 3); //xpos - SafeWrite32(0x4364FA, 614 + 3); //plus button xpos - SafeWrite32(0x436567, 614 + 3); //minus button xpos + // skill slider thingy + SafeWrite32(0x43647C, 592 + 3); // xpos + SafeWrite32(0x4364FA, 614 + 3); // plus button xpos + SafeWrite32(0x436567, 614 + 3); // minus button xpos - //fix for Char Screen note position was x484 y309 now x383 y308 - //SafeWrite32(0x43AB55, 308 * 640 + 483); //minus button xpos + // fix for Char Screen note position was x484 y309 now x383 y308 + //SafeWrite32(0x43AB55, 308 * 640 + 483); // minus button xpos - //Adjust PC SFX Name + // Adjust PC SFX Name (gsound_load_sound_) MakeCall(0x4510EB, FixPcSFX, 2); - //Set path to normal before printing or saving character details + // Set path to normal before printing or saving character details //HookCall(0x432359, FixCharScrnSaveNPrint); - //Load Appearance data from GCD file------------ - SafeWrite16(0x42DF5D, 0x9056); //push esi "*FileStream" + // Load Appearance data from GCD file (pc_load_data_) + SafeWrite8(0x42DF5E, 0xF1); // mov ecx, esi "*FileStream" HookCall(0x42DF5F, LoadGCDAppearance); - //Save Appearance data to GCD file------------ - SafeWrite16(0x42E161, 0x9056); //push esi "*FileStream" + // Save Appearance data to GCD file (pc_save_data_) + SafeWrite8(0x42E162, 0xF1); // mov ecx, esi "*FileStream" HookCall(0x42E163, SaveGCDAppearance); - //Reset Appearance when selecting "Create Character" from the New Char screen------ + // Reset Appearance when selecting "Create Character" from the New Char screen (select_character_) MakeCall(0x4A7405, CreateCharReset); - //Fixes missing console critical hit messages when PC is attacked. - //00426135 |. 25 FF0F0000 AND EAX,00000FFF - MakeCall(0x426135, FixPcCriticalHitMsg); + // Fixes missing console critical hit messages when PC is attacked. (combat_get_loc_name_) + HookCall(0x42613A, FixPcCriticalHitMsg); - //Force Criticals For Testing - //00423A8F |. /2E:FF249D 7C374200 JMP DWORD PTR CS:[EBX*4+42377C] + // Force Criticals For Testing //SafeWrite32(0x423A8F, 0x90909090); //SafeWrite32(0x423A93, 0x90909090); } +static void HeroAppearanceModExit() { + if (!HeroAppearance::appModEnabled) return; + + if (heroPathPtr) { + delete[] heroPathPtr->path; + delete heroPathPtr; + } + if (racePathPtr) { + delete[] racePathPtr->path; + delete racePathPtr; + } +} + void HeroAppearance::init() { if (GetConfigInt("Misc", "EnableHeroAppearanceMod", 0)) { dlog("Setting up Appearance Char Screen buttons.", DL_INIT); diff --git a/sfall/Modules/HeroAppearance.h b/sfall/Modules/HeroAppearance.h index 77249727c..515d994a0 100644 --- a/sfall/Modules/HeroAppearance.h +++ b/sfall/Modules/HeroAppearance.h @@ -28,11 +28,12 @@ class HeroAppearance : public Module { const char* name() { return "HeroAppearance"; } void init(); - void exit() override; + + static bool appModEnabled; }; -void _stdcall HeroSelectWindow(int RaceStyleFlag); +void _stdcall HeroSelectWindow(int raceStyleFlag); void _stdcall SetHeroStyle(int newStyleVal); void _stdcall SetHeroRace(int newRaceVal); void _stdcall LoadHeroAppearance(void); diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 783f1f698..6a098dbd6 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -82,6 +82,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_USESKILLON, Inject_UseSkillOnHook, false}, {HOOK_ONEXPLOSION, Inject_OnExplosionHook, false}, {HOOK_SUBCOMBATDAMAGE, Inject_SubCombatDamageHook, false}, // replace the code logic + {HOOK_SETLIGHTING, Inject_SetLightingHook, false}, }; bool HookScripts::injectAllHooks; @@ -221,7 +222,7 @@ void LoadHookScripts() { initingHookScripts = 1; for (int i = 0; i < numHooks; i++) { if (hooks[i].size()) { - InitScriptProgram(hooks[i][0].prog);// zero hook is always hs_*.int script because Hook scripts are loaded BEFORE global scripts + InitScriptProgram(hooks[i][0].prog); // zero hook is always hs_*.int script because Hook scripts are loaded BEFORE global scripts } } isGlobalScriptLoading = 0; @@ -242,7 +243,8 @@ void HookScripts::init() { LoadGameHook::OnGameModeChange() += GameModeChangeHook; LoadGameHook::OnAfterGameStarted() += SourceUseSkillOnInit; - HookScripts::injectAllHooks = (isDebug && (GetConfigInt("Debugging", "InjectAllGameHooks", 0) != 0)); + HookScripts::injectAllHooks = isDebug && (GetPrivateProfileIntA("Debugging", "InjectAllGameHooks", 0, ::sfall::ddrawIni) != 0); + if (HookScripts::injectAllHooks) dlogr("Injecting all game hooks", DL_HOOK); } } diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 6e609789e..e0eea7fd8 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -64,6 +64,7 @@ enum HookType HOOK_USESKILLON = 35, HOOK_ONEXPLOSION = 36, HOOK_SUBCOMBATDAMAGE = 37, + HOOK_SETLIGHTING = 38, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/Common.cpp b/sfall/Modules/HookScripts/Common.cpp index d53cdd996..efe1d543a 100644 --- a/sfall/Modules/HookScripts/Common.cpp +++ b/sfall/Modules/HookScripts/Common.cpp @@ -65,6 +65,7 @@ static bool CheckRecursiveHooks(DWORD hook) { if (hook == currentRunHook) { switch (hook) { case HOOK_SETGLOBALVAR: + case HOOK_SETLIGHTING: return true; } } diff --git a/sfall/Modules/HookScripts/Common.h b/sfall/Modules/HookScripts/Common.h index 4384f26b4..5934039fb 100644 --- a/sfall/Modules/HookScripts/Common.h +++ b/sfall/Modules/HookScripts/Common.h @@ -39,7 +39,7 @@ void _stdcall BeginHook(); void _stdcall RunHookScript(DWORD hook); void _stdcall EndHook(); -#define HookBegin __asm pushad __asm call BeginHook __asm popad -#define HookEnd __asm pushad __asm call EndHook __asm popad +#define HookBegin pushadc __asm call BeginHook popadc +#define HookEnd pushadc __asm call EndHook popadc } diff --git a/sfall/Modules/HookScripts/InventoryHs.cpp b/sfall/Modules/HookScripts/InventoryHs.cpp index af3d36578..49932755a 100644 --- a/sfall/Modules/HookScripts/InventoryHs.cpp +++ b/sfall/Modules/HookScripts/InventoryHs.cpp @@ -174,12 +174,12 @@ static void __declspec(naked) InvenActionCursorObjDropHook() { goto skipHook; } else { __asm { - pushad; + pushadc; xor ecx, ecx; // no itemReplace push 6; // event: item drop ground call InventoryMoveHook_Script; // edx - item mov dropResult, eax; // ret value - popad; + popadc; cmp dword ptr [esp], 0x47379A + 5; // caps call address jz capsMultiDrop; } @@ -204,19 +204,19 @@ static void __declspec(naked) InvenActionCursorObjDropHook() { static void __declspec(naked) InvenActionExplosiveDropHack() { __asm { - pushad; + pushadc; xor ecx, ecx; // no itemReplace push 6; // event: item drop ground call InventoryMoveHook_Script; // edx - item cmp eax, -1; // ret value - popad; - jnz noDrop; - mov dword ptr ds:[FO_VAR_dropped_explosive], ebp; // overwritten engine code (ebp = 1) - mov nextHookDropSkip, ebp; + popadc; + jnz noDrop; + mov dword ptr ds:[FO_VAR_dropped_explosive], ebp; // overwritten engine code (ebp = 1) + mov nextHookDropSkip, ebp; retn; noDrop: - add esp, 4; - jmp InvenActionObjDropRet; // no drop + add esp, 4; + jmp InvenActionObjDropRet; // no drop } } @@ -234,19 +234,19 @@ static const DWORD DropIntoContainer_back = 0x47649D; // normal operation static const DWORD DropIntoContainer_skip = 0x476503; static void __declspec(naked) DropIntoContainerHack() { __asm { - pushad; - mov ecx, ebp; // contaner ptr - mov edx, esi; // item - mov eax, [esp + 0x10 + 32]; // call address + pushadc; + mov ecx, ebp; // contaner ptr + mov edx, esi; // item + mov eax, [esp + 0x10 + 12]; // call address push eax; call DropIntoContainer; - cmp eax, -1; // ret value - popad; + cmp eax, -1; // ret value + popadc; jne skipdrop; - jmp DropIntoContainer_back; + jmp DropIntoContainer_back; skipdrop: - mov eax, -1; - jmp DropIntoContainer_skip; + mov eax, -1; + jmp DropIntoContainer_skip; } } @@ -254,27 +254,29 @@ static const DWORD DropIntoContainerRet = 0x471481; static void __declspec(naked) DropIntoContainerHandSlotHack() { __asm { call fo::funcoffs::drop_into_container_; - jmp DropIntoContainerRet; + jmp DropIntoContainerRet; } } -static const DWORD DropAmmoIntoWeaponHack_back = 0x47658D; // proceed with reloading +//static const DWORD DropAmmoIntoWeaponHack_back = 0x47658D; // proceed with reloading static const DWORD DropAmmoIntoWeaponHack_return = 0x476643; -static void _declspec(naked) DropAmmoIntoWeaponHack() { +static void _declspec(naked) DropAmmoIntoWeaponHook() { __asm { - pushad; - mov ecx, ebp; // weapon ptr - mov edx, [esp + 32]; // item var: ammo_ - push 4; // event: weapon reloading + pushadc; + mov ecx, ebp; // weapon ptr + mov edx, [esp + 16]; // item var: ammo_ + push 4; // event: weapon reloading call InventoryMoveHook_Script; - cmp eax, -1; // ret value - popad; + cmp eax, -1; // ret value + popadc; jne donothing; - mov ebx, 1; // overwritten code - jmp DropAmmoIntoWeaponHack_back; + jmp fo::funcoffs::item_w_can_reload_; + //mov ebx, 1; // overwritten code + //jmp DropAmmoIntoWeaponHack_back; donothing: - xor eax, eax; // result 0 - jmp DropAmmoIntoWeaponHack_return; + add esp, 4; // destroy return address + xor eax, eax; // result 0 + jmp DropAmmoIntoWeaponHack_return; } } @@ -288,7 +290,7 @@ static bool InvenWieldHook_Script(int flag) { bool result = (cRet == 0 || rets[0] == -1); EndHook(); - return result; + return result; // True - use engine handler } static void _declspec(naked) InvenWieldFuncHook() { @@ -314,6 +316,7 @@ static void _declspec(naked) InvenWieldFuncHook() { jz skip; jmp funcoffs::invenWieldFunc_; skip: + mov eax, -1; retn; } } @@ -341,6 +344,7 @@ static void _declspec(naked) InvenUnwieldFuncHook() { jz skip; jmp fo::funcoffs::invenUnwieldFunc_; skip: + mov eax, -1; retn; } } @@ -371,6 +375,7 @@ static void _declspec(naked) CorrectFidForRemovedItemHook() { jz skip; jmp fo::funcoffs::correctFidForRemovedItem_; skip: + mov eax, -1; retn; } } @@ -410,7 +415,7 @@ void Inject_InventoryMoveHook() { MakeJump(0x471338, DropIntoContainerHandSlotHack); MakeJump(0x4712AB, DropIntoContainerHandSlotHack); HookCall(0x471200, MoveInventoryHook); - MakeJump(0x476588, DropAmmoIntoWeaponHack); + HookCall(0x476549, DropAmmoIntoWeaponHook); // old 0x476588 HookCalls(InvenActionCursorObjDropHook, { 0x473851, 0x47386F, 0x47379A // caps multi drop @@ -419,9 +424,19 @@ void Inject_InventoryMoveHook() { } void Inject_InvenWieldHook() { - HookCalls(InvenWieldFuncHook, { 0x47275E, 0x495FDF }); - HookCalls(InvenUnwieldFuncHook, { 0x45967D, 0x472A5A, 0x495F0B }); - HookCalls(CorrectFidForRemovedItemHook, { 0x45680C, 0x45C4EA }); + HookCalls(InvenWieldFuncHook, { + 0x47275E, // inven_wield_ + 0x495FDF // partyMemberCopyLevelInfo_ + }); + HookCalls(InvenUnwieldFuncHook, { + 0x45967D, // op_metarule_ + 0x472A5A, // inven_unwield_ + 0x495F0B // partyMemberCopyLevelInfo_ + }); + HookCalls(CorrectFidForRemovedItemHook, { + 0x45680C, // op_rm_obj_from_inven_ + 0x45C4EA // op_move_obj_inven_to_obj_ + }); } void InitInventoryHookScripts() { diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp index 6c4e5d04f..60820fe22 100644 --- a/sfall/Modules/HookScripts/MiscHs.cpp +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -369,50 +369,48 @@ static void __declspec(naked) RestTimerEscapeHook() { } } -static int __fastcall ExplosiveTimerHook_Script(DWORD type, DWORD item, DWORD time) { +static int __fastcall ExplosiveTimerHook_Script(DWORD* type, DWORD item, DWORD time) { BeginHook(); argCount = 3; args[0] = time; args[1] = item; - args[2] = (type == 11) ? fo::ROLL_FAILURE : fo::ROLL_SUCCESS; + args[2] = (*type == 11) ? fo::ROLL_FAILURE : fo::ROLL_SUCCESS; RunHookScript(HOOK_EXPLOSIVETIMER); - int result = 0; - if (cRet > 0 && rets[0] >= 0) { - if (rets[0] > 18000) rets[0] = 18000; // max 30 minutes - if (cRet < 2 || (rets[1] < fo::ROLL_CRITICAL_FAILURE || rets[1] > fo::ROLL_CRITICAL_SUCCESS)) { - result--; // use vanilla type - } else { - result++; // use returned type + time = -1; + if (cRet > 0 && (long)rets[0] >= 0) { + time = min(rets[0], 18000); // max 30 minutes + if (cRet > 1) { + int typeRet = rets[1]; + if (typeRet >= fo::ROLL_CRITICAL_FAILURE && typeRet <= fo::ROLL_CRITICAL_SUCCESS) { + *type = (typeRet > fo::ROLL_FAILURE) ? 8 : 11; // returned new type (8 = SUCCESS, 11 = FAILURE) + } } } - return result; + EndHook(); + return time; } static void _declspec(naked) ExplosiveTimerHook() { - using namespace fo; __asm { - push eax; + push eax; // time in ticks for queue_add_ push edx; - push ecx; //------- - push edi; // time in ticks + push ecx; + mov ecx, esp; // ptr to type + push edi; // time in ticks (w/o failure penalty) call ExplosiveTimerHook_Script; // ecx - type, edx - item - cmp eax, 0; pop ecx; pop edx; + cmp eax, -1; // return new time in ticks + jne skip; pop eax; - jz end; - mov eax, rets[0]; // time in ticks - jl end; - mov ecx, 8; // type SUCCESS - cmp rets[4], ROLL_FAILURE; - jg end; - add ecx, 3; // type FAILURE (11) + jmp end; +skip: + add esp, 4; end: - HookEnd; jmp fo::funcoffs::queue_add_; } } diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp index b9e34eb4b..62268cecd 100644 --- a/sfall/Modules/HookScripts/ObjectHs.cpp +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -102,10 +102,8 @@ static DWORD __fastcall UseAnimateObjHook_Script(DWORD critter, DWORD animCode, RunHookScript(HOOK_USEANIMOBJ); - if (cRet > 0) { - if (static_cast(rets[0]) <= 64) { - animCode = rets[0]; // new anim code - } + if (cRet > 0 && static_cast(rets[0]) <= 64) { + animCode = rets[0]; // new anim code } EndHook(); @@ -171,6 +169,72 @@ static void __declspec(naked) DescriptionObjHook() { } } +static bool __fastcall SetLightingHook_Script(DWORD* intensity, DWORD* radius, DWORD object) { + BeginHook(); + argCount = 3; + + args[0] = object; + args[1] = *intensity; + args[2] = *radius; + RunHookScript(HOOK_SETLIGHTING); + + bool result = false; + if (cRet > 0) { + int light = rets[0]; + if (light < 0) light = 0; + *intensity = light; + if (cRet > 1 && object != -1) { + int dist = rets[1]; + if (dist < 0) dist = 0; + *radius = dist; + } + result = true; + } + EndHook(); + return result; +} + +static void __declspec(naked) SetObjectLightHook() { + __asm { + pushadc; + push ebp; + mov edx, esp; // &radius + push ebx; + mov ecx, esp; // &intensity + push esi; // object + call SetLightingHook_Script; + test al, al; + jz skip; + pop ebx; // return intensity value + pop ebp; // return radius value + jmp end; +skip: + add esp, 8; +end: + popadc; + jmp fo::funcoffs::obj_turn_off_light_; + } +} + +static void __declspec(naked) SetMapLightHook() { + __asm { + push ecx; + push ebx; + mov ecx, esp; // &intensity + push -1; // no object (it's a map) + mov edx, esp; // no radius + call SetLightingHook_Script; + add esp, 4; + test al, al; + jz skip; + mov ebx, [esp - 4]; // return intensity value +skip: + pop ecx; + cmp ebx, dword ptr ds:[0x47A93D]; // get miminal ambient light intensity (16384) + retn; + } +} + void Inject_UseObjOnHook() { HookCalls(UseObjOnHook, { 0x49C606, 0x473619 }); @@ -195,12 +259,18 @@ void Inject_DescriptionObjHook() { HookCall(0x48C925, DescriptionObjHook); } +void Inject_SetLightingHook() { + HookCall(0x48ACA0, SetObjectLightHook); + MakeCall(0x47A934, SetMapLightHook, 1); +} + void InitObjectHookScripts() { LoadHookScript("hs_useobjon", HOOK_USEOBJON); LoadHookScript("hs_useobj", HOOK_USEOBJ); LoadHookScript("hs_useanimobj", HOOK_USEANIMOBJ); LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ); + LoadHookScript("hs_setlighting", HOOK_SETLIGHTING); } } diff --git a/sfall/Modules/HookScripts/ObjectHs.h b/sfall/Modules/HookScripts/ObjectHs.h index 9bc5396a6..020cc0667 100644 --- a/sfall/Modules/HookScripts/ObjectHs.h +++ b/sfall/Modules/HookScripts/ObjectHs.h @@ -8,4 +8,5 @@ namespace sfall void Inject_UseObjHook(); void Inject_UseAnimateObjHook(); void Inject_DescriptionObjHook(); + void Inject_SetLightingHook(); } diff --git a/sfall/Modules/Inventory.cpp b/sfall/Modules/Inventory.cpp index c01f660df..7a9cb4b1e 100644 --- a/sfall/Modules/Inventory.cpp +++ b/sfall/Modules/Inventory.cpp @@ -345,7 +345,7 @@ static int __fastcall SuperStimFix2(fo::GameObject* item, fo::GameObject* target return 0; } - DWORD curr_hp, max_hp; + long curr_hp, max_hp; curr_hp = fo::func::stat_level(target, fo::STAT_current_hp); max_hp = fo::func::stat_level(target, fo::STAT_max_hit_points); if (curr_hp < max_hp) return 0; @@ -495,150 +495,132 @@ static void __declspec(naked) item_add_mult_hook() { } } -static void __declspec(naked) inven_pickup_hook() { +static void __declspec(naked) inven_pickup_hack() { __asm { - mov eax, ds:[FO_VAR_i_wid] - call fo::funcoffs::GNW_find_ - mov ebx, [eax+0x8+0x0] // ebx = _i_wid.rect.x - mov ecx, [eax+0x8+0x4] // ecx = _i_wid.rect.y - mov eax, 176 - add eax, ebx // x_start - add ebx, 176+60 // x_end - mov edx, 37 - add edx, ecx // y_start - add ecx, 37+100 // y_end - call fo::funcoffs::mouse_click_in_ - test eax, eax - jz end - mov edx, ds:[FO_VAR_curr_stack] - test edx, edx - jnz end - cmp edi, 1006 // Hands? - jae skip // Yes -skip: - xor eax, eax -end: - retn + mov eax, ds:[FO_VAR_i_wid]; + call fo::funcoffs::GNW_find_; + mov ebx, [eax + 8 + 0]; // ebx = _i_wid.rect.x + mov ecx, [eax + 8 + 4]; // ecx = _i_wid.rect.y + lea eax, [ebx + 176]; // x_start + add ebx, 176 + 60; // x_end + lea edx, [ecx + 37]; // y_start + add ecx, 37 + 100; // y_end + retn; } } static void __declspec(naked) loot_container_hack_scroll() { __asm { - cmp esi, 0x150 // source_down - je scroll - cmp esi, 0x148 // source_up - jne end + cmp esi, 0x150; // source_down + je scroll; + cmp esi, 0x148; // source_up + jne end; scroll: - push edx - push ecx - push ebx - mov eax, ds:[FO_VAR_i_wid] - call fo::funcoffs::GNW_find_ - mov ebx, [eax+0x8+0x0] // ebx = _i_wid.rect.x - mov ecx, [eax+0x8+0x4] // ecx = _i_wid.rect.y - mov eax, 297 - add eax, ebx // x_start - add ebx, 297+64 // x_end - mov edx, 37 - add edx, ecx // y_start - add ecx, 37+6*48 // y_end - call fo::funcoffs::mouse_click_in_ - pop ebx - pop ecx - pop edx - test eax, eax - jz end - cmp esi, 0x150 // source_down - je targetDown - mov esi, 0x18D // target_up - jmp end + mov eax, ds:[FO_VAR_i_wid]; + call fo::funcoffs::GNW_find_; + push edx; + push ecx; + push ebx; + mov ebx, [eax + 8 + 0]; // ebx = _i_wid.rect.x + mov ecx, [eax + 8 + 4]; // ecx = _i_wid.rect.y + lea eax, [ebx + 297]; // x_start + add ebx, 297 + 64; // x_end + lea edx, [ecx + 37]; // y_start + add ecx, 37 + 6 * 48; // y_end + call fo::funcoffs::mouse_click_in_; + pop ebx; + pop ecx; + pop edx; + test eax, eax; + jz end; + cmp esi, 0x150; // source_down + je targetDown; + mov esi, 0x18D; // target_up + jmp end; targetDown: - mov esi, 0x191 // target_down + mov esi, 0x191; // target_down end: - mov eax, ds:[FO_VAR_curr_stack] - retn + mov eax, ds:[FO_VAR_curr_stack]; + retn; } } static void __declspec(naked) barter_inventory_hack_scroll() { __asm { - push edx - push ecx - push ebx - xchg esi, eax - cmp esi, 0x150 // source_down - je scroll - cmp esi, 0x148 // source_up - jne end + mov esi, eax; + cmp esi, 0x150; // source_down + je scroll; + cmp esi, 0x148; // source_up + jne skip; scroll: - mov eax, ds:[FO_VAR_i_wid] - call fo::funcoffs::GNW_find_ - mov ebx, [eax+0x8+0x0] // ebx = _i_wid.rect.x - mov ecx, [eax+0x8+0x4] // ecx = _i_wid.rect.y - push ebx - push ecx - mov eax, 395 - add eax, ebx // x_start - add ebx, 395+64 // x_end - mov edx, 35 - add edx, ecx // y_start - add ecx, 35+3*48 // y_end - call fo::funcoffs::mouse_click_in_ - pop ecx - pop ebx - test eax, eax - jz notTargetScroll - cmp esi, 0x150 // source_down - je targetDown - mov esi, 0x18D // target_up - jmp end + mov eax, ds:[FO_VAR_i_wid]; + call fo::funcoffs::GNW_find_; + push edx; + push ecx; + push ebx; + push ebp; + push edi; + mov ebp, [eax + 8 + 0]; + mov edi, [eax + 8 + 4]; + mov ebx, ebp; // ebx = _i_wid.rect.x + mov ecx, edi; // ecx = _i_wid.rect.y + lea eax, [ebp + 395]; // x_start + add ebx, 395 + 64; // x_end + lea edx, [edi + 35]; // y_start + add ecx, 35 + 3 * 48; // y_end + call fo::funcoffs::mouse_click_in_; + test eax, eax; + jz notTargetScroll; + cmp esi, 0x150; // source_down + je targetDown; + mov esi, 0x18D; // target_up + jmp end; targetDown: - mov esi, 0x191 // target_down - jmp end + mov esi, 0x191; // target_down + jmp end; notTargetScroll: - push ebx - push ecx - mov eax, 250 - add eax, ebx // x_start - add ebx, 250+64 // x_end - mov edx, 20 - add edx, ecx // y_start - add ecx, 20+3*48 // y_end - call fo::funcoffs::mouse_click_in_ - pop ecx - pop ebx - test eax, eax - jz notTargetBarter - cmp esi, 0x150 // source_down - je barterTargetDown - mov esi, 0x184 // target_barter_up - jmp end + mov ebx, ebp; + mov ecx, edi; + lea eax, [ebp + 250]; // x_start + add ebx, 250 + 64; // x_end + lea edx, [edi + 20]; // y_start + add ecx, 20 + 3 * 48; // y_end + call fo::funcoffs::mouse_click_in_; + test eax, eax; + jz notTargetBarter; + cmp esi, 0x150; // source_down + je barterTargetDown; + mov esi, 0x184; // target_barter_up + jmp end; barterTargetDown: - mov esi, 0x176 // target_barter_down - jmp end + mov esi, 0x176; // target_barter_down + jmp end; notTargetBarter: - mov eax, 165 - add eax, ebx // x_start - add ebx, 165+64 // x_end - mov edx, 20 - add edx, ecx // y_start - add ecx, 20+3*48 // y_end - call fo::funcoffs::mouse_click_in_ - test eax, eax - jz end - cmp esi, 0x150 // source_down - je barterSourceDown - mov esi, 0x149 // source_barter_up - jmp end + mov ebx, ebp; + mov ecx, edi; + lea eax, [ebp + 165]; // x_start + add ebx, 165 + 64; // x_end + lea edx, [edi + 20]; // y_start + add ecx, 20 + 3 * 48; // y_end + call fo::funcoffs::mouse_click_in_; + test eax, eax; + jz end; + cmp esi, 0x150; // source_down + je barterSourceDown; + mov esi, 0x149; // source_barter_up + jmp end; barterSourceDown: - mov esi, 0x151 // source_barter_down + mov esi, 0x151; // source_barter_down end: - pop ebx - pop ecx - pop edx - mov eax, esi - cmp eax, 0x11 - retn + pop edi; + pop ebp; + pop ebx; + pop ecx; + pop edx; + mov eax, esi; +skip: + cmp eax, 0x11; + retn; } } @@ -646,7 +628,7 @@ static void __declspec(naked) barter_inventory_hack_scroll() { // Differences from vanilla: // - doesn't use art_vault_guy_num as default art, uses current critter FID instead // - invokes onAdjustFid delegate that allows to hook into FID calculation -DWORD __stdcall adjust_fid_replacement2() { +DWORD __stdcall Inventory::adjust_fid_replacement() { using namespace fo; DWORD fid; @@ -693,11 +675,11 @@ DWORD __stdcall adjust_fid_replacement2() { return var::i_fid; } -static void __declspec(naked) adjust_fid_replacement() { +static void __declspec(naked) adjust_fid_hack_replacement() { __asm { push ecx; push edx; - call adjust_fid_replacement2; // return fid + call Inventory::adjust_fid_replacement; // return fid pop edx; pop ecx; retn; @@ -709,7 +691,7 @@ static void __declspec(naked) do_move_timer_hook() { __asm { cmp eax, 4; jnz end; - pushad; + pushadc; } KeyDown(itemFastMoveKey); // check pressed @@ -717,13 +699,13 @@ static void __declspec(naked) do_move_timer_hook() { __asm { cmp skipFromContainer, 0; jz noSkip; - cmp dword ptr [esp + 0x14 + 36], 0x474A43; + cmp dword ptr [esp + 0x14 + 16], 0x474A43; jnz noSkip; test eax, eax; setz al; noSkip: test eax, eax; // set if pressed - popad; + popadc; jz end; add esp, 4; // destroy ret jmp DoMoveTimer_Ret; @@ -740,7 +722,7 @@ void Inventory::init() { OnKeyPressed() += InventoryKeyPressedHook; LoadGameHook::OnGameReset() += InventoryReset; - MakeJump(fo::funcoffs::adjust_fid_, adjust_fid_replacement); + MakeJump(fo::funcoffs::adjust_fid_, adjust_fid_hack_replacement); sizeLimitMode = GetConfigInt("Misc", "CritterInvSizeLimitMode", 0); if (sizeLimitMode > 0 && sizeLimitMode <= 7) { @@ -833,9 +815,8 @@ void Inventory::init() { BlockCall(0x4768A3); // mov ebx, 1 } - // Move items out of bag/backpack and back into the main inventory list by dragging them to character's image - // (similar to Fallout 1 behavior) - HookCall(0x471457, inven_pickup_hook); + // Move items out of bag/backpack and back into the main inventory list by dragging them to character's image (similar to Fallout 1 behavior) + MakeCall(0x471452, inven_pickup_hack); // Move items to player's main inventory instead of the opened bag/backpack when confirming a trade SafeWrite32(0x475CF2, FO_VAR_stack); diff --git a/sfall/Modules/Inventory.h b/sfall/Modules/Inventory.h index ca89fd972..c7ce19e14 100644 --- a/sfall/Modules/Inventory.h +++ b/sfall/Modules/Inventory.h @@ -31,6 +31,8 @@ class Inventory : public Module { // Called after game calculated dude FID for displaying on inventory screen static Delegate& OnAdjustFid(); + + static DWORD __stdcall adjust_fid_replacement(); }; void _stdcall SetInvenApCost(int cost); diff --git a/sfall/Modules/Karma.cpp b/sfall/Modules/Karma.cpp index 92cb47d2c..2dcbc8746 100644 --- a/sfall/Modules/Karma.cpp +++ b/sfall/Modules/Karma.cpp @@ -41,7 +41,7 @@ static std::string karmaGainMsg; static std::string karmaLossMsg; bool displayKarmaChanges; -static DWORD _stdcall DrawCardHook2() { +static DWORD _stdcall DrawCard() { int reputation = fo::var::game_global_vars[fo::GVAR_PLAYER_REPUTATION]; for (auto& info : karmaFrms) { if (reputation < info.points) { @@ -51,19 +51,19 @@ static DWORD _stdcall DrawCardHook2() { return karmaFrms.end()->frm; } -static void __declspec(naked) DrawCardHook() { +static void __declspec(naked) DrawInfoWin_hook() { __asm { - cmp ds : [FO_VAR_info_line], 10; - jne skip; - cmp eax, 0x30; - jne skip; + cmp ds:[FO_VAR_info_line], 10; + jne skip; + cmp eax, 0x30; + jne skip; push ecx; push edx; - call DrawCardHook2; - pop edx; - pop ecx; + call DrawCard; + pop edx; + pop ecx; skip: - jmp fo::funcoffs::DrawCard_; + jmp fo::funcoffs::DrawCard_; } } @@ -101,7 +101,7 @@ void ApplyKarmaFRMsPatch() { ? atoi(karmaPointsList[i].c_str()) : INT_MAX; } - HookCall(0x4367A9, DrawCardHook); + HookCall(0x4367A9, DrawInfoWin_hook); dlogr(" Done", DL_INIT); } diff --git a/sfall/Modules/KillCounter.cpp b/sfall/Modules/KillCounter.cpp index bf4be57ea..c460a1ace 100644 --- a/sfall/Modules/KillCounter.cpp +++ b/sfall/Modules/KillCounter.cpp @@ -25,83 +25,58 @@ namespace sfall { -static int usingExtraKillTypes; -bool UsingExtraKillTypes() { return usingExtraKillTypes != 0; } +static const DWORD extraKillTypesCountAddr[] = { + 0x42D8AF, // critter_kill_count_ + 0x42D881, // critter_kill_count_inc_ + 0x42D980, // GetKillTypeName + 0x42D990, + 0x42D9C0, // GetKillTypeDesc + 0x42D9D0, + 0x4344E4, // Change char sheet to loop through the extra kill types +}; -//Fallout's idea of _fastcall seems to be different to VS2005's. -//Might as well do this in asm, or the custom prolog code would end up being longer than the function -static DWORD __declspec(naked) ReadKillCounter(DWORD killtype) { - //if(killtype>38) return 0; - //return ((WORD*)_pc_kill_counts)[killtype]; +static int usingExtraKillTypes = 0; + +bool UsingExtraKillTypes() { + return usingExtraKillTypes != 0; +} + +static DWORD __declspec(naked) ReadKillCounter() { __asm { - cmp eax, 38; - jle func; - xor eax, eax; - ret; -func: - push ebx; - lea ebx, ds:[FO_VAR_pc_kill_counts+eax*2]; - xor eax,eax; - mov ax, word ptr [ebx] - pop ebx; - ret; + movzx eax, word ptr ds:[FO_VAR_pc_kill_counts][eax * 2]; + retn; } } -static void __declspec(naked) IncKillCounter(DWORD killtype) { - //if(killtype>38) return; - //((WORD*)_pc_kill_counts)[killtype]++; +static void __declspec(naked) IncKillCounter() { __asm { - cmp eax, 38; - jle func; - ret; -func: - push ebx; - lea ebx, ds:[FO_VAR_pc_kill_counts+eax*2]; - xor eax, eax; - mov ax, word ptr [ebx]; - inc ax; - mov word ptr [ebx], ax; - pop ebx; - ret; - } + inc bx; + mov word ptr ds:[FO_VAR_pc_kill_counts][edx], bx; + retn; + } } -void KillCounterInit(bool use) { - if (!use) { - usingExtraKillTypes = 0; - return; - } +static void KillCounterInit() { usingExtraKillTypes = 1; - //Overwrite the function that reads the kill counter with my own - HookCall(0x4344BF, &ReadKillCounter); - HookCall(0x43A162, &ReadKillCounter); - HookCall(0x4571D8, &ReadKillCounter); - - //Overwrite the function that increments the kill counter with my own - HookCall(0x425144, &IncKillCounter); + // Overwrite the critter_kill_count_ function that reads the kill counter + MakeCall(0x42D8B5, ReadKillCounter, 2); - //Edit the GetKillTypeName function to accept kill types over 0x13 - SafeWrite8(0x42D980, 38); - SafeWrite8(0x42D990, 38); + // Overwrite the critter_kill_count_inc_ function that increments the kill counter + MakeCall(0x42D89C, IncKillCounter, 1); + SafeWrite8(0x42D88E, 0x45); // lea edx, [eax * 2] + SafeWrite8(0x42D899, 0x90); // inc ebx > nop - //And the same for GetKillTypeDesc - SafeWrite8(0x42D9C0, 38); - SafeWrite8(0x42D9D0, 38); + // Edit the functions to accept kill types over 19 + SafeWriteBatch(38, extraKillTypesCountAddr); SafeWrite32(0x42D9DD, 1488); - - //Change char sheet to loop through the extra kill types - SafeWrite8(0x4344E4, 38); } void KillCounter::init() { if (GetConfigInt("Misc", "ExtraKillTypes", 0)) { dlog("Applying extra kill types patch.", DL_INIT); - KillCounterInit(true); + KillCounterInit(); dlogr(" Done", DL_INIT); - } else { - KillCounterInit(false); } } diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index aa787e0fe..522fa6fbc 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -24,10 +24,11 @@ #include "..\version.h" #include "AI.h" -#include "Bugfixes.h" +#include "BugFixes.h" #include "FileSystem.h" #include "HeroAppearance.h" #include "HookScripts.h" +#include "Objects.h" #include "PartyControl.h" #include "Perks.h" #include "ScriptExtender.h" @@ -40,16 +41,14 @@ namespace sfall { -#define _InLoop(type, flag) __asm { \ - _asm pushad \ - _asm push flag \ - _asm push type \ - _asm call SetInLoop \ - _asm popad } #define _InLoop2(type, flag) __asm { \ _asm push flag \ _asm push type \ _asm call SetInLoop } +#define _InLoop(type, flag) __asm { \ + pushadc \ + _InLoop2(type, flag) \ + popadc } static Delegate<> onGameInit; static Delegate<> onGameExit; @@ -124,7 +123,7 @@ static void _stdcall SaveGame2() { HANDLE h = CreateFileA(buf, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); if (h != INVALID_HANDLE_VALUE) { SaveGlobals(h); - WriteFile(h, &unused, 4, &size, 0); + WriteFile(h, &Objects::uniqueID, 4, &size, 0); // save unique id counter unused++; WriteFile(h, &unused, 4, &size, 0); Perks::save(h); @@ -172,28 +171,27 @@ static DWORD _stdcall CombatSaveTest() { static void __declspec(naked) SaveGame_hook() { __asm { - push ebx; push ecx; push edx; push eax; // save Mode parameter call CombatSaveTest; test eax, eax; - pop edx; // recall Mode parameter (pop eax) - jz end; - mov eax, edx; - - _InLoop(1, SAVEGAME); + pop edx; // recall Mode parameter (pop eax) + jz end; + push edx; + _InLoop2(1, SAVEGAME); + pop eax; call fo::funcoffs::SaveGame_; - _InLoop(0, SAVEGAME); - cmp eax, 1; - jne end; - // save sfall.sav - call SaveGame2; - mov eax, 1; + push eax; + _InLoop2(0, SAVEGAME); + pop eax; + cmp eax, 1; + jne end; + call SaveGame2; // save sfall.sav + mov eax, 1; end: - pop edx; - pop ecx; - pop ebx; + pop edx; + pop ecx; retn; } } @@ -209,10 +207,13 @@ static void _stdcall LoadGame_Before() { HANDLE h = CreateFileA(buf, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); if (h != INVALID_HANDLE_VALUE) { - DWORD size, unused[2]; + DWORD size, unused; LoadGlobals(h); - ReadFile(h, &unused, 8, &size, 0); - if (size == 8) { + long uID = 0; + ReadFile(h, &uID, 4, &size, 0); + if (uID > UniqueID::Start) Objects::uniqueID = uID; + ReadFile(h, &unused, 4, &size, 0); + if (size == 4) { Perks::load(h); script::LoadArrays(h); } @@ -235,18 +236,19 @@ static void _stdcall LoadGame_Before() { // called whenever game is being reset (prior to loading a save or when returning to main menu) static void _stdcall GameReset(DWORD isGameLoad) { - onGameReset.invoke(); + if (mapLoaded) { // prevent resetting when a new game has not been started (loading saved game from main menu) + onGameReset.invoke(); + if (isDebug) { + char* str = (isGameLoad) ? "on Load" : "on Exit"; + fo::func::debug_printf("\n[SFALL: State reset %s]\n", str); + } + } inLoop = 0; mapLoaded = false; if (isGameLoad) { LoadGame_Before(); } - - if (isDebug) { - char* str = (isGameLoad) ? "on Load" : "on Exit"; - fo::func::debug_printf("\n[SFALL: State reset %s]\n", str); - } } // Called after game was loaded from a save @@ -260,17 +262,15 @@ static void __declspec(naked) LoadGame_hook() { _InLoop(1, LOADGAME); call fo::funcoffs::LoadGame_; _InLoop(0, LOADGAME); - cmp eax, 1; - jne end; + cmp eax, 1; + jne end; // Invoked - push ebx; push ecx; push edx; call LoadGame_After; - mov eax, 1; - pop edx; - pop ecx; - pop ebx; + mov eax, 1; + pop edx; + pop ecx; end: retn; } @@ -279,9 +279,9 @@ static void __declspec(naked) LoadGame_hook() { static void __declspec(naked) EndLoadHook() { __asm { call fo::funcoffs::EndLoad_; - pushad; + pushadc; call LoadHeroAppearance; - popad; + popadc; retn; } } @@ -301,14 +301,12 @@ static void __stdcall NewGame_After() { static void __declspec(naked) main_load_new_hook() { __asm { - pushad; - mov esi, eax; // keep + push eax; call NewGame_Before; - mov eax, esi; // restore + pop eax; call fo::funcoffs::main_load_new_; - call NewGame_After; - popad; - retn; + jmp NewGame_After; + //retn; } } @@ -326,57 +324,57 @@ static void __stdcall GameClose() { static void __declspec(naked) main_init_system_hook() { __asm { - pushad; + pushadc; call GameInitialized; - popad; + popadc; jmp fo::funcoffs::main_init_system_; } } static void __declspec(naked) game_reset_hook() { __asm { - pushad; + pushadc; push 0; call GameReset; // reset all sfall modules before resetting the game data - popad; + popadc; jmp fo::funcoffs::game_reset_; } } static void __declspec(naked) game_reset_on_load_hook() { __asm { - pushad; + pushadc; push 1; call GameReset; // reset all sfall modules before resetting the game data - popad; + popadc; jmp fo::funcoffs::game_reset_; } } static void __declspec(naked) before_game_exit_hook() { __asm { - pushad; + pushadc; push 1; call GameModeChange; - popad; + popadc; jmp fo::funcoffs::map_exit_; } } static void __declspec(naked) after_game_exit_hook() { __asm { - pushad; + pushadc; call GameExit; - popad; + popadc; jmp fo::funcoffs::main_menu_create_; } } static void __declspec(naked) game_close_hook() { __asm { - pushad; + pushadc; call GameClose; - popad; + popadc; jmp fo::funcoffs::game_exit_; } } @@ -384,7 +382,7 @@ static void __declspec(naked) game_close_hook() { static void __declspec(naked) WorldMapHook() { __asm { _InLoop(1, WORLDMAP); - xor eax, eax; + xor eax, eax; call fo::funcoffs::wmWorldMapFunc_; _InLoop(0, WORLDMAP); retn; @@ -402,15 +400,15 @@ static void __declspec(naked) WorldMapHook2() { static void __declspec(naked) CombatHook() { __asm { - pushad; + pushadc; call AICombatStart; _InLoop2(1, COMBAT); - popad; + popadc; call fo::funcoffs::combat_; - pushad; + pushadc; call AICombatEnd; _InLoop2(0, COMBAT); - popad; + popadc; retn; } } @@ -463,12 +461,12 @@ static void __declspec(naked) HelpMenuHook() { static void __declspec(naked) CharacterHook() { __asm { - pushad; + pushadc; _InLoop2(1, CHARSCREEN); call PerksEnterCharScreen; - popad; + popadc; call fo::funcoffs::editor_design_; - pushad; + pushadc; test eax, eax; jz success; call PerksCancelCharScreen; @@ -478,7 +476,7 @@ static void __declspec(naked) CharacterHook() { end: _InLoop2(0, CHARSCREEN); mov tagSkill4LevelBase, -1; // for fixing Tag! perk exploit - popad; + popadc; retn; } } diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 1d73b0f9c..640b575d7 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -60,6 +60,10 @@ static const DWORD script_dialog_msgs[] = { 0x4A50C2, 0x4A5169, 0x4A52FA, 0x4A5302, 0x4A6B86, 0x4A6BE0, 0x4A6C37, }; +static const DWORD walkDistanceAddr[] = { + 0x411FF0, 0x4121C4, 0x412475, 0x412906, +}; + static void __declspec(naked) Combat_p_procFix() { __asm { push eax; @@ -475,7 +479,7 @@ static void __declspec(naked) win_debug_hook() { void DebugModePatch() { if (isDebug) { - DWORD dbgMode = GetPrivateProfileIntA("Debugging", "DebugMode", 0, ".\\ddraw.ini"); + DWORD dbgMode = GetPrivateProfileIntA("Debugging", "DebugMode", 0, ::sfall::ddrawIni); if (dbgMode) { dlog("Applying debugmode patch.", DL_INIT); //If the player is using an exe with the debug patch already applied, just skip this block without erroring @@ -792,7 +796,7 @@ void DialogueFix() { } void DontDeleteProtosPatch() { - if (isDebug && GetPrivateProfileIntA("Debugging", "DontDeleteProtos", 0, ".\\ddraw.ini")) { + if (isDebug && GetPrivateProfileIntA("Debugging", "DontDeleteProtos", 0, ::sfall::ddrawIni)) { dlog("Applying permanent protos patch.", DL_INIT); SafeWrite8(0x48007E, 0xEB); dlogr(" Done", DL_INIT); @@ -912,6 +916,15 @@ void InterfaceDontMoveOnTopPatch() { } } +void UseWalkDistancePatch() { + int distance = GetConfigInt("Misc", "UseWalkDistance", 3) + 2; + if (distance > 1 && distance < 5) { + dlog("Applying walk distance for using objects patch.", DL_INIT); + SafeWriteBatch(distance, walkDistanceAddr); // default is 5 + dlogr(" Done", DL_INIT); + } +} + void BodypartHitChances() { using fo::var::hit_location_penalty; hit_location_penalty[0] = static_cast(GetConfigInt("Misc", "BodyHit_Head", -40)); @@ -1005,6 +1018,8 @@ void MiscPatches::init() { SkipLoadingGameSettingsPatch(); InterfaceDontMoveOnTopPatch(); + + UseWalkDistancePatch(); } void MiscPatches::exit() { diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index c00295dc4..dbe9c9eb6 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -28,6 +28,18 @@ namespace sfall static int unjamTimeState; static int maxCountProto = 512; +long Objects::uniqueID = UniqueID::Start; // saving to sfallgv.sav + +// Assigns a new unique identifier to an object if it has not been previously assigned +// the identifier is saved with the object in the saved game and this can used in various script +long Objects::SetObjectUniqueID(fo::GameObject* obj) { + if (obj->id > UniqueID::Start || obj == fo::var::obj_dude) return obj->id; // dude id = 1800. TODO: perhaps his id needs to be changed to 0x10000000 + + if ((DWORD)uniqueID >= UniqueID::End) uniqueID = UniqueID::Start; + obj->id = ++uniqueID; + return uniqueID; +} + void Objects::SetAutoUnjamLockTime(DWORD time) { if (!unjamTimeState) { BlockCall(0x4A364A); // disable auto unjam at midnight diff --git a/sfall/Modules/Objects.h b/sfall/Modules/Objects.h index cc4b5f941..ac28749a9 100644 --- a/sfall/Modules/Objects.h +++ b/sfall/Modules/Objects.h @@ -5,11 +5,19 @@ namespace sfall { +enum UniqueID { + Start = 0x10000000, + End = 0x7FFFFFFF +}; + class Objects : public Module { public: const char* name() { return "Objects"; } void init(); + static long uniqueID; + + static long SetObjectUniqueID(fo::GameObject* obj); static void SetAutoUnjamLockTime(DWORD time); static void LoadProtoAutoMaxLimit(); }; diff --git a/sfall/Modules/Perks.cpp b/sfall/Modules/Perks.cpp index 24543e552..ac2133956 100644 --- a/sfall/Modules/Perks.cpp +++ b/sfall/Modules/Perks.cpp @@ -28,14 +28,17 @@ namespace sfall { using namespace fo; -static char Name[64 * PERK_count]; -static char Desc[1024 * PERK_count]; -static char tName[64 * TRAIT_count]; -static char tDesc[1024 * TRAIT_count]; +constexpr int maxNameLen = 64; // don't change size +constexpr int maxDescLen = 1024; // don't change size + +static char Name[maxNameLen * PERK_count]; +static char Desc[maxDescLen * PERK_count]; +static char tName[maxNameLen * TRAIT_count]; +static char tDesc[maxDescLen * TRAIT_count]; static char perksFile[MAX_PATH]; -static BYTE disableTraits[TRAIT_count]; +static bool disableTraits[TRAIT_count]; -#define check_trait(a) !disableTraits[a] && (var::pc_trait[0] == a || var::pc_trait[1] == a) +#define check_trait(id) !disableTraits[id] && (var::pc_trait[0] == id || var::pc_trait[1] == id) static DWORD addPerkMode = 2; @@ -46,8 +49,17 @@ static TraitInfo traits[TRAIT_count]; struct FakePerk { int Level; int Image; - char Name[64]; - char Desc[1024]; + char Name[maxNameLen]; + char Desc[maxDescLen]; + + FakePerk() {} + + FakePerk(char* _name, int _level, int _image, char* _desc) : Name {0}, Desc {0} { + Level = _level; + Image = _image; + strncpy_s(Name, _name, _TRUNCATE); + strncpy_s(Desc, _desc, _TRUNCATE); + } }; std::vector fakeTraits; @@ -58,11 +70,11 @@ static DWORD RemoveTraitID; static DWORD RemovePerkID; static DWORD RemoveSelectableID; -static DWORD TraitSkillBonuses[TRAIT_count*18]; -static DWORD TraitStatBonuses[TRAIT_count*(STAT_max_derived+1)]; +static DWORD TraitSkillBonuses[TRAIT_count * 18]; +static DWORD TraitStatBonuses[TRAIT_count * (STAT_max_derived + 1)]; static DWORD IgnoringDefaultPerks = 0; -static char PerkBoxTitle[64]; +static char PerkBoxTitle[33]; static DWORD PerkFreqOverride = 0; @@ -80,49 +92,40 @@ void _stdcall SetPerkFreq(int i) { PerkFreqOverride = i; } -static DWORD _stdcall IsTraitDisabled(int id) { +static bool IsTraitDisabled(int id) { return disableTraits[id]; } -static void __declspec(naked) LevelUpHook() { +static long _stdcall LevelUp() { + int eachLevel = PerkFreqOverride; + + if (!eachLevel) { + if (!IsTraitDisabled(TRAIT_skilled) && fo::func::trait_level(TRAIT_skilled)) { // Check if the player has the skilled trait + eachLevel = 4; + } else { + eachLevel = 3; + } + } + + int level = fo::var::Level_; // Get players level + if (!((level + 1) % eachLevel)) fo::var::free_perk++; // Increment the number of perks owed + return level; +} + +static void __declspec(naked) LevelUpHack() { __asm { push ecx; - push ebx; - mov ecx, PerkFreqOverride; - test ecx, ecx; - jnz afterskilled; - mov eax, TRAIT_skilled; - call fo::funcoffs::trait_level_; //Check if the player has the skilled trait - test eax, eax; - jz notskilled; - push TRAIT_skilled; - call IsTraitDisabled; - test eax, eax; - jnz notskilled; - mov ecx, 4; - jmp afterskilled; -notskilled: - mov ecx, 3; -afterskilled: - mov eax, ds:[FO_VAR_Level_]; //Get players level - inc eax; - xor edx, edx; - div ecx; - test edx, edx; - jnz end; - inc byte ptr ds:[FO_VAR_free_perk]; //Increment the number of perks owed -end: - pop ebx; - pop ecx; - mov edx, ds:[FO_VAR_Level_]; + call LevelUp; + mov edx, eax; // player level + pop ecx; retn; } } static void __declspec(naked) GetPerkBoxTitleHook() { __asm { - lea eax, PerkBoxTitle; - ret; + lea eax, PerkBoxTitle; + retn; } } @@ -136,16 +139,17 @@ void _stdcall RestoreDefaultPerks() { void _stdcall SetPerkboxTitle(char* name) { if (name[0] == '\0') { + PerkBoxTitle[0] = 0; SafeWrite32(0x43C77D, 0x488CB); } else { - strcpy_s(PerkBoxTitle, name); + strncpy_s(PerkBoxTitle, name, _TRUNCATE); HookCall(0x43C77C, GetPerkBoxTitleHook); } } -void _stdcall SetSelectablePerk(char* name, int level, int image, char* desc) { - if (level < 0 || level > 1) return; - if (level == 0) { +void _stdcall SetSelectablePerk(char* name, int active, int image, char* desc) { + if (active < 0 || active > 1) return; + if (active == 0) { for (DWORD i = 0; i < fakeSelectablePerks.size(); i++) { if (!strcmp(name, fakeSelectablePerks[i].Name)) { fakeSelectablePerks.erase(fakeSelectablePerks.begin() + i); @@ -155,19 +159,13 @@ void _stdcall SetSelectablePerk(char* name, int level, int image, char* desc) { } else { for (DWORD i = 0; i < fakeSelectablePerks.size(); i++) { if (!strcmp(name, fakeSelectablePerks[i].Name)) { - fakeSelectablePerks[i].Level = level; + fakeSelectablePerks[i].Level = active; fakeSelectablePerks[i].Image = image; - strcpy_s(fakeSelectablePerks[i].Desc, desc); + strncpy_s(fakeSelectablePerks[i].Desc, desc, _TRUNCATE); return; } } - FakePerk fp; - memset(&fp, 0, sizeof(FakePerk)); - fp.Level = level; - fp.Image = image; - strcpy_s(fp.Name, name); - strcpy_s(fp.Desc, desc); - fakeSelectablePerks.push_back(fp); + fakeSelectablePerks.push_back(FakePerk(name, active, image, desc)); } } @@ -185,23 +183,17 @@ void _stdcall SetFakePerk(char* name, int level, int image, char* desc) { if (!strcmp(name, fakePerks[i].Name)) { fakePerks[i].Level = level; fakePerks[i].Image = image; - strcpy_s(fakePerks[i].Desc, desc); + strncpy_s(fakePerks[i].Desc, desc, _TRUNCATE); return; } } - FakePerk fp; - memset(&fp, 0, sizeof(FakePerk)); - fp.Level = level; - fp.Image = image; - strcpy_s(fp.Name, name); - strcpy_s(fp.Desc, desc); - fakePerks.push_back(fp); + fakePerks.push_back(FakePerk(name, level, image, desc)); } } -void _stdcall SetFakeTrait(char* name, int level, int image, char* desc) { - if (level < 0 || level > 1) return; - if (level == 0) { +void _stdcall SetFakeTrait(char* name, int active, int image, char* desc) { + if (active < 0 || active > 1) return; + if (active == 0) { for (DWORD i = 0; i < fakeTraits.size(); i++) { if (!strcmp(name, fakeTraits[i].Name)) { fakeTraits.erase(fakeTraits.begin() + i); @@ -211,237 +203,197 @@ void _stdcall SetFakeTrait(char* name, int level, int image, char* desc) { } else { for (DWORD i = 0; i < fakeTraits.size(); i++) { if (!strcmp(name, fakeTraits[i].Name)) { - fakeTraits[i].Level = level; + fakeTraits[i].Level = active; fakeTraits[i].Image = image; - strcpy_s(fakeTraits[i].Desc, desc); + strncpy_s(fakeTraits[i].Desc, desc, _TRUNCATE); return; } } - FakePerk fp; - memset(&fp, 0, sizeof(FakePerk)); - fp.Level = level; - fp.Image = image; - strcpy_s(fp.Name, name); - strcpy_s(fp.Desc, desc); - fakeTraits.push_back(fp); + fakeTraits.push_back(FakePerk(name, active, image, desc)); } } -static DWORD _stdcall HaveFakeTraits2() { +static DWORD _stdcall HaveFakeTraits() { return fakeTraits.size(); } -static void __declspec(naked) HaveFakeTraits() { - __asm { - push ebx; - push ecx; - push edx; - call HaveFakeTraits2; - pop edx; - pop ecx; - pop ebx; - ret; - } -} - -static DWORD _stdcall HaveFakePerks2() { +static DWORD _stdcall HaveFakePerks() { return fakePerks.size(); } -static void __declspec(naked) HaveFakePerks() { - __asm { - push ebx; - push ecx; - push edx; - call HaveFakePerks2; - pop edx; - pop ecx; - pop ebx; - ret; - } -} - static FakePerk* _stdcall GetFakePerk2(int id) { - return &fakePerks[id-PERK_count]; + return &fakePerks[id - PERK_count]; } static void __declspec(naked) GetFakePerk() { __asm { - mov eax, [esp+4]; - push ebx; + mov eax, [esp + 4]; push ecx; push edx; push eax; call GetFakePerk2; - pop edx; - pop ecx; - pop ebx; - ret 4; + pop edx; + pop ecx; + retn 4; } } static FakePerk* _stdcall GetFakeSPerk2(int id) { - return &fakeSelectablePerks[id-PERK_count]; + return &fakeSelectablePerks[id - PERK_count]; } static void __declspec(naked) GetFakeSPerk() { __asm { - mov eax, [esp+4]; - push ebx; + mov eax, [esp + 4]; push ecx; push edx; push eax; call GetFakeSPerk2; - pop edx; - pop ecx; - pop ebx; - ret 4; + pop edx; + pop ecx; + retn 4; } } static DWORD _stdcall GetFakeSPerkLevel2(int id) { - char* c = fakeSelectablePerks[id - PERK_count].Name; + char* name = fakeSelectablePerks[id - PERK_count].Name; for (DWORD i = 0; i < fakePerks.size(); i++) { - if (!strcmp(c, fakePerks[i].Name)) return fakePerks[i].Level; + if (!strcmp(name, fakePerks[i].Name)) return fakePerks[i].Level; } return 0; } static void __declspec(naked) GetFakeSPerkLevel() { __asm { - mov eax, [esp+4]; - push ebx; + mov eax, [esp + 4]; push ecx; push edx; push eax; call GetFakeSPerkLevel2; - pop edx; - pop ecx; - pop ebx; - ret 4; + pop edx; + pop ecx; + retn 4; } } -static DWORD _stdcall HandleFakeTraits(int i2) { +static DWORD HandleFakeTraits(int value) { for (DWORD i = 0; i < fakeTraits.size(); i++) { - DWORD a = (DWORD)fakeTraits[i].Name; - __asm { - mov eax, a; - call fo::funcoffs::folder_print_line_; - mov a, eax; - } - if (a && !i2) { - i2 = 1; + if (fo::func::folder_print_line(fakeTraits[i].Name) && !value) { + value = 1; var::folder_card_fid = fakeTraits[i].Image; var::folder_card_title = (DWORD)fakeTraits[i].Name; var::folder_card_title2 = 0; var::folder_card_desc = (DWORD)fakeTraits[i].Desc; } } - return i2; + return value; } -static void __declspec(naked) PlayerHasPerkHook() { +static long _fastcall PlayerHasPerk(int* value) { + *value = HandleFakeTraits(*value); + + for (int i = 0; i < PERK_count; i++) { + if (fo::func::perk_level(fo::var::obj_dude, i)) return 1; + } + return fakePerks.size(); +} + +static void __declspec(naked) PlayerHasPerkHack() { __asm { - push ecx; - call HandleFakeTraits; - mov ecx, eax; - xor ebx, ebx; -oloop: - mov eax, ds:[FO_VAR_obj_dude]; - mov edx, ebx; - call fo::funcoffs::perk_level_; - test eax, eax; - jnz win; - inc ebx; - cmp ebx, PERK_count; - jl oloop; - call HaveFakePerks; + push ecx; // value + mov ecx, esp; // ptr to value + call PlayerHasPerk; + pop ecx; // value from HandleFakeTraits test eax, eax; jnz win; - push 0x434446; - retn; + mov eax, 0x434446; + jmp eax; win: - push 0x43438A; - retn; + mov eax, 0x43438A; + jmp eax; } } static void __declspec(naked) PlayerHasTraitHook() { __asm { + push ecx; call HaveFakeTraits; + pop ecx; test eax, eax; jz end; - push 0x43425B; - retn; + mov eax, 0x43425B; + jmp eax; end: - jmp PlayerHasPerkHook; + jmp PlayerHasPerkHack; } } static void __declspec(naked) GetPerkLevelHook() { __asm { - cmp edx, PERK_count; - jl end; + cmp edx, PERK_count; + jl end; push edx; call GetFakePerk; - mov eax, ds:[eax]; - ret; + mov eax, ds:[eax]; + retn; end: - jmp fo::funcoffs::perk_level_; + jmp fo::funcoffs::perk_level_; } } static void __declspec(naked) GetPerkImageHook() { __asm { - cmp eax, PERK_count; - jl end; + cmp eax, PERK_count; + jl end; push eax; call GetFakePerk; - mov eax, ds:[eax+4]; - ret; + mov eax, ds:[eax + 4]; + retn; end: - jmp fo::funcoffs::perk_skilldex_fid_; + jmp fo::funcoffs::perk_skilldex_fid_; } } static void __declspec(naked) GetPerkNameHook() { __asm { - cmp eax, PERK_count; - jl end; + cmp eax, PERK_count; + jl end; push eax; call GetFakePerk; - lea eax, ds:[eax+8]; - ret; + lea eax, ds:[eax + 8]; + retn; end: - jmp fo::funcoffs::perk_name_; + jmp fo::funcoffs::perk_name_; } } static void __declspec(naked) GetPerkDescHook() { __asm { - cmp eax, PERK_count; - jl end; + cmp eax, PERK_count; + jl end; push eax; call GetFakePerk; - lea eax, ds:[eax+72]; - ret; + lea eax, ds:[eax + 72]; + retn; end: - jmp fo::funcoffs::perk_description_ + jmp fo::funcoffs::perk_description_ } } -static void __declspec(naked) EndPerkLoopHook() { +static void __declspec(naked) EndPerkLoopHack() { __asm { + jl cLoop; // if ebx < 119 + push ecx; call HaveFakePerks; + pop ecx; add eax, PERK_count; cmp ebx, eax; - jl end; - push 0x434446; - retn; -end: - push 0x4343A5; - retn; + jl cLoop; + mov eax, 0x434446; // exit loop + jmp eax; +cLoop: + mov eax, 0x4343A5; // continue loop + jmp eax; } } @@ -456,72 +408,70 @@ static DWORD _stdcall HandleExtraSelectablePerks(DWORD offset, DWORD* data) { static void __declspec(naked) GetAvailablePerksHook() { __asm { push ecx; - push ebx; - push edx; - mov ebx, IgnoringDefaultPerks; - test ebx, ebx; - jnz skipdefaults; + push edx; // arg data + mov ecx, IgnoringDefaultPerks; + test ecx, ecx; + jnz skipdefaults; call fo::funcoffs::perk_make_list_; - jmp next; + jmp next; skipdefaults: - xor eax, eax; + xor eax, eax; next: push eax; call HandleExtraSelectablePerks; - pop ebx; - pop ecx; - ret; + pop ecx; + retn; } } static void __declspec(naked) GetPerkSLevelHook() { __asm { - cmp edx, PERK_count; - jl end; + cmp edx, PERK_count; + jl end; push edx; call GetFakeSPerkLevel; - ret; + retn; end: - jmp fo::funcoffs::perk_level_; + jmp fo::funcoffs::perk_level_; } } static void __declspec(naked) GetPerkSImageHook() { __asm { - cmp eax, PERK_count; - jl end; + cmp eax, PERK_count; + jl end; push eax; call GetFakeSPerk; - mov eax, ds:[eax+4]; - ret; + mov eax, ds:[eax + 4]; + retn; end: - jmp fo::funcoffs::perk_skilldex_fid_; + jmp fo::funcoffs::perk_skilldex_fid_; } } static void __declspec(naked) GetPerkSNameHook() { __asm { - cmp eax, PERK_count; - jl end; + cmp eax, PERK_count; + jl end; push eax; call GetFakeSPerk; - lea eax, ds:[eax+8]; - ret; + lea eax, ds:[eax + 8]; + retn; end: - jmp fo::funcoffs::perk_name_; + jmp fo::funcoffs::perk_name_; } } static void __declspec(naked) GetPerkSDescHook() { __asm { - cmp eax, PERK_count; - jl end; + cmp eax, PERK_count; + jl end; push eax; call GetFakeSPerk; - lea eax, ds:[eax+72]; - ret; + lea eax, ds:[eax + 72]; + retn; end: - jmp fo::funcoffs::perk_description_; + jmp fo::funcoffs::perk_description_; } } @@ -556,34 +506,31 @@ static void _stdcall AddFakePerk(DWORD perkID) { if (addPerkMode & 4) { RemoveSelectableID = perkID; //fakeSelectablePerks.remove_at(perkID); - } } static void __declspec(naked) AddPerkHook() { __asm { - cmp edx, PERK_count; - jl end; + cmp edx, PERK_count; + jl normalPerk; push ecx; - push ebx; push edx; call AddFakePerk; - pop ebx; - pop ecx; - xor eax, eax; - ret; -end: + pop ecx; + xor eax, eax; + retn; +normalPerk: push edx; call fo::funcoffs::perk_add_; - pop edx; + pop edx; test eax, eax; - jnz end2; - cmp edx, 84; - jl end2; - cmp edx, 90; - jg end2; - inc ds:[edx*4 + (FO_VAR_pc_proto + 0x24 - (PERK_gain_strength_perk)*4)]; // base_stat_srength -end2: + jnz end; + cmp edx, PERK_gain_strength_perk; + jl end; + cmp edx, PERK_gain_luck_perk; + jg end; + inc ds:[edx * 4 + (FO_VAR_pc_proto + 0x24 - PERK_gain_strength_perk * 4)]; // base_stat_srength +end: retn; } } @@ -593,7 +540,7 @@ static void __declspec(naked) HeaveHoHook() { xor edx, edx; mov eax, ecx; call fo::funcoffs::stat_level_; - lea ebx, [0+eax*4]; + lea ebx, [0 + eax * 4]; sub ebx, eax; cmp ebx, esi; // ebx = dist (3*ST), esi = max dist weapon jle lower; // jump if dist <= max @@ -602,7 +549,7 @@ static void __declspec(naked) HeaveHoHook() { mov eax, ecx; mov edx, PERK_heave_ho; call fo::funcoffs::perk_level_; - lea ecx, [0+eax*8]; + lea ecx, [0 + eax * 8]; sub ecx, eax; sub ecx, eax; mov eax, ecx; @@ -612,57 +559,51 @@ static void __declspec(naked) HeaveHoHook() { } } +static bool perkHeaveHoModFix = false; void _stdcall ApplyHeaveHoFix() { // not really a fix MakeJump(0x478AC4, HeaveHoHook); perks[PERK_heave_ho].strengthMin = 0; + perkHeaveHoModFix = true; } static void PerkSetup() { - //Character screen - HookCall(0x434256, PlayerHasTraitHook); - SafeWrite8(0x43436B, 0xE9); - HookCall(0x43436B, PlayerHasPerkHook); + // Character screen (list_perks_) + HookCall(0x434256, PlayerHasTraitHook); // jz + MakeJump(0x43436B, PlayerHasPerkHack); HookCall(0x4343AC, GetPerkLevelHook); - HookCall(0x4343C1, GetPerkNameHook); - HookCall(0x4343DF, GetPerkNameHook); HookCall(0x43440D, GetPerkImageHook); - HookCall(0x43441B, GetPerkNameHook); HookCall(0x434432, GetPerkDescHook); - SafeWrite8(0x43443D, 0xE9); - HookCall(0x43443D, EndPerkLoopHook); + MakeJump(0x434440, EndPerkLoopHack, 1); + HookCalls(GetPerkNameHook, {0x4343C1, 0x4343DF, 0x43441B}); - //GetPlayerAvailablePerks + // GetPlayerAvailablePerks (ListDPerks_) HookCall(0x43D127, GetAvailablePerksHook); HookCall(0x43D17D, GetPerkSNameHook); - HookCall(0x43D25E, GetPerkSLevelHook); - HookCall(0x43D275, GetPerkSLevelHook); - //ShowPerkBox - HookCall(0x43C82E, GetPerkSLevelHook); - HookCall(0x43C85B, GetPerkSLevelHook); - HookCall(0x43C888, GetPerkSDescHook); - HookCall(0x43C8A6, GetPerkSNameHook); - HookCall(0x43C8D1, GetPerkSDescHook); - HookCall(0x43C8EF, GetPerkSNameHook); + HookCalls(GetPerkSLevelHook, {0x43D25E, 0x43D275}); + + // ShowPerkBox (perks_dialog_) + HookCalls(GetPerkSLevelHook, {0x43C82E, 0x43C85B}); + HookCalls(GetPerkSDescHook, {0x43C888, 0x43C8D1}); + HookCalls(GetPerkSNameHook, {0x43C8A6, 0x43C8EF}); HookCall(0x43C90F, GetPerkSImageHook); HookCall(0x43C952, AddPerkHook); - //PerkboxSwitchPerk - HookCall(0x43C3F1, GetPerkSLevelHook); - HookCall(0x43C41E, GetPerkSLevelHook); - HookCall(0x43C44B, GetPerkSDescHook); - HookCall(0x43C469, GetPerkSNameHook); - HookCall(0x43C494, GetPerkSDescHook); - HookCall(0x43C4B2, GetPerkSNameHook); + + // PerkboxSwitchPerk (RedrwDPrks_) + HookCalls(GetPerkSLevelHook, {0x43C3F1, 0x43C41E}); + HookCalls(GetPerkSDescHook, {0x43C44B, 0x43C494}); + HookCalls(GetPerkSNameHook, {0x43C469, 0x43C4B2}); HookCall(0x43C4D2, GetPerkSImageHook); + // perk_owed hooks + MakeCall(0x4AFB2F, LevelUpHack, 1); // replaces 'mov edx, ds:[PlayerLevel]' + SafeWrite8(0x43C2EC, 0xEB); // skip the block of code which checks if the player has gained a perk (now handled in level up code) + memset(Name, 0, sizeof(Name)); memset(Desc, 0, sizeof(Desc)); memcpy(perks, var::perk_data, sizeof(PerkInfo) * PERK_count); - SafeWrite32(0x496669, (DWORD)perks); - SafeWrite32(0x496837, (DWORD)perks); - SafeWrite32(0x496BAD, (DWORD)perks); - SafeWrite32(0x496C41, (DWORD)perks); - SafeWrite32(0x496D25, (DWORD)perks); + // _perk_data + SafeWriteBatch((DWORD)perks, {0x496669, 0x496837, 0x496BAD, 0x496C41, 0x496D25}); SafeWrite32(0x496696, (DWORD)perks + 4); SafeWrite32(0x496BD1, (DWORD)perks + 4); SafeWrite32(0x496BF5, (DWORD)perks + 8); @@ -672,11 +613,11 @@ static void PerkSetup() { char num[4]; for (int i = 0; i < PERK_count; i++) { _itoa_s(i, num, 10); - if (GetPrivateProfileString(num, "Name", "", &Name[i * 64], 63, perksFile)) { - perks[i].name = &Name[i * 64]; + if (GetPrivateProfileString(num, "Name", "", &Name[i * maxNameLen], maxNameLen - 1, perksFile)) { + perks[i].name = &Name[i * maxNameLen]; } - if (GetPrivateProfileString(num, "Desc", "", &Desc[i * 1024], 1023, perksFile)) { - perks[i].description = &Desc[i * 1024]; + if (GetPrivateProfileString(num, "Desc", "", &Desc[i * maxDescLen], maxDescLen - 1, perksFile)) { + perks[i].description = &Desc[i * maxDescLen]; } int value; value = GetPrivateProfileInt(num, "Image", -99999, perksFile); @@ -717,43 +658,44 @@ static void PerkSetup() { } for (int i = 0; i < PERK_count; i++) { - if (perks[i].name != &Name[64 * i]) { - strcpy_s(&Name[64 * i], 64, perks[i].name); - perks[i].name = &Name[64 * i]; + if (perks[i].name != &Name[maxNameLen * i]) { + strcpy_s(&Name[maxNameLen * i], maxNameLen, perks[i].name); + perks[i].name = &Name[maxNameLen * i]; } - if (perks[i].description&&perks[i].description != &Desc[1024 * i]) { - strcpy_s(&Desc[1024 * i], 1024, perks[i].description); - perks[i].description = &Desc[1024 * i]; + if (perks[i].description && perks[i].description != &Desc[maxDescLen * i]) { + strcpy_s(&Desc[maxDescLen * i], maxDescLen, perks[i].description); + perks[i].description = &Desc[maxDescLen * i]; } } - //perk_owed hooks - MakeCall(0x4AFB2F, LevelUpHook); //replaces 'mov edx, ds:[PlayerLevel] - SafeWrite8(0x4AFB34, 0x90); - - SafeWrite8(0x43C2EC, 0xEB); //skip the block of code which checks if the player has gained a perk (now handled in level up code) - - //Disable losing unused perks + // Disable losing unused perks SafeWrite16(0x43C369, 0x0DFE); // dec byte ptr ds:_free_perk SafeWrite8(0x43C370, 0xB1); // jmp 0x43C322 } -static int _stdcall stat_get_base_direct(DWORD statID) { - DWORD result; +static __declspec(naked) void PerkInitWrapper() { __asm { - mov edx, statID; - mov eax, dword ptr ds:[FO_VAR_obj_dude]; - call fo::funcoffs::stat_get_base_direct_; - mov result, eax; + call fo::funcoffs::perk_init_; + push edx; + push ecx; + call PerkSetup; + pop ecx; + pop edx; + retn; } - return result; +} + +static int stat_get_base_direct(DWORD statID) { + return fo::func::stat_get_base_direct(fo::var::obj_dude, statID); } static int _stdcall trait_adjust_stat_override(DWORD statID) { if (statID > STAT_max_derived) return 0; + int result = 0; - if (var::pc_trait[0] != -1) result += TraitStatBonuses[statID*TRAIT_count + var::pc_trait[0]]; - if (var::pc_trait[1] != -1) result += TraitStatBonuses[statID*TRAIT_count + var::pc_trait[1]]; + if (var::pc_trait[0] != -1) result += TraitStatBonuses[statID * TRAIT_count + var::pc_trait[0]]; + if (var::pc_trait[1] != -1) result += TraitStatBonuses[statID * TRAIT_count + var::pc_trait[1]]; + switch (statID) { case STAT_st: if (check_trait(TRAIT_gifted)) result++; @@ -815,22 +757,22 @@ static int _stdcall trait_adjust_stat_override(DWORD statID) { return result; } -static void __declspec(naked) TraitAdjustStatHook() { +static void __declspec(naked) TraitAdjustStatHack() { __asm { push edx; push ecx; push eax; call trait_adjust_stat_override; - pop ecx; - pop edx; + pop ecx; + pop edx; retn; } } static int _stdcall trait_adjust_skill_override(DWORD skillID) { int result = 0; - if (var::pc_trait[0] != -1) result += TraitSkillBonuses[skillID*TRAIT_count + var::pc_trait[0]]; - if (var::pc_trait[1] != -1) result += TraitSkillBonuses[skillID*TRAIT_count + var::pc_trait[1]]; + if (var::pc_trait[0] != -1) result += TraitSkillBonuses[skillID * TRAIT_count + var::pc_trait[0]]; + if (var::pc_trait[1] != -1) result += TraitSkillBonuses[skillID * TRAIT_count + var::pc_trait[1]]; if (check_trait(TRAIT_gifted)) result -= 10; if (check_trait(TRAIT_good_natured)) { if (skillID <= SKILL_THROWING) result -= 10; @@ -839,28 +781,29 @@ static int _stdcall trait_adjust_skill_override(DWORD skillID) { return result; } -static void __declspec(naked) TraitAdjustSkillHook() { +static void __declspec(naked) TraitAdjustSkillHack() { __asm { push edx; push ecx; push eax; call trait_adjust_skill_override; - pop ecx; - pop edx; + pop ecx; + pop edx; retn; } } static void __declspec(naked) BlockedTrait() { __asm { - xor eax, eax; + xor eax, eax; retn; } } static void TraitSetup() { - MakeJump(0x4B3C7C, TraitAdjustStatHook); - MakeJump(0x4B40FC, TraitAdjustSkillHook); + // Replace functions + MakeJump(0x4B3C7C, TraitAdjustStatHack); // trait_adjust_stat_ + MakeJump(0x4B40FC, TraitAdjustSkillHack); // trait_adjust_skill_ memset(tName, 0, sizeof(tName)); memset(tDesc, 0, sizeof(tDesc)); @@ -868,6 +811,7 @@ static void TraitSetup() { memset(TraitStatBonuses, 0, sizeof(TraitStatBonuses)); memset(TraitSkillBonuses, 0, sizeof(TraitSkillBonuses)); + // _trait_data SafeWrite32(0x4B3A81, (DWORD)traits); SafeWrite32(0x4B3B80, (DWORD)traits); SafeWrite32(0x4B3AAE, (DWORD)traits + 4); @@ -875,16 +819,15 @@ static void TraitSetup() { SafeWrite32(0x4B3BC0, (DWORD)traits + 8); if (strlen(perksFile)) { - char num[5], buf[512]; - num[0] = 't'; + char buf[512], num[5] = {'t'}; char* num2 = &num[1]; for (int i = 0; i < TRAIT_count; i++) { _itoa_s(i, num2, 4, 10); - if (GetPrivateProfileString(num, "Name", "", &tName[i * 64], 63, perksFile)) { - traits[i].name = &tName[i * 64]; + if (GetPrivateProfileString(num, "Name", "", &tName[i * maxNameLen], maxNameLen - 1, perksFile)) { + traits[i].name = &tName[i * maxNameLen]; } - if (GetPrivateProfileString(num, "Desc", "", &tDesc[i * 1024], 1023, perksFile)) { - traits[i].description = &tDesc[i * 1024]; + if (GetPrivateProfileString(num, "Desc", "", &tDesc[i * maxDescLen], maxDescLen - 1, perksFile)) { + traits[i].description = &tDesc[i * maxDescLen]; } int value; value = GetPrivateProfileInt(num, "Image", -99999, perksFile); @@ -896,7 +839,7 @@ static void TraitSetup() { mod = strtok(0, "|"); while (stat&&mod) { int _stat = atoi(stat), _mod = atoi(mod); - if (_stat >= 0 && _stat <= STAT_max_derived) TraitStatBonuses[_stat*TRAIT_count + i] = _mod; + if (_stat >= 0 && _stat <= STAT_max_derived) TraitStatBonuses[_stat * TRAIT_count + i] = _mod; stat = strtok(0, "|"); mod = strtok(0, "|"); } @@ -908,45 +851,45 @@ static void TraitSetup() { mod = strtok(0, "|"); while (stat&&mod) { int _stat = atoi(stat), _mod = atoi(mod); - if (_stat >= 0 && _stat < 18) TraitSkillBonuses[_stat*TRAIT_count + i] = _mod; + if (_stat >= 0 && _stat < 18) TraitSkillBonuses[_stat * TRAIT_count + i] = _mod; stat = strtok(0, "|"); mod = strtok(0, "|"); } } if (GetPrivateProfileInt(num, "NoHardcode", 0, perksFile)) { - disableTraits[i] = 1; + disableTraits[i] = true; switch (i) { case 3: - HookCall(0x4245E0, &BlockedTrait); + HookCall(0x4245E0, BlockedTrait); break; case 4: - HookCall(0x4248F9, &BlockedTrait); + HookCall(0x4248F9, BlockedTrait); break; case 7: - HookCall(0x478C8A, &BlockedTrait); //fast shot - HookCall(0x478E70, &BlockedTrait); + HookCall(0x478C8A, BlockedTrait); // fast shot + HookCall(0x478E70, BlockedTrait); break; case 8: - HookCall(0x410707, &BlockedTrait); + HookCall(0x410707, BlockedTrait); break; case 9: - HookCall(0x42389F, &BlockedTrait); + HookCall(0x42389F, BlockedTrait); break; case 11: - HookCall(0x47A0CD, &BlockedTrait); - HookCall(0x47A51A, &BlockedTrait); + HookCall(0x47A0CD, BlockedTrait); + HookCall(0x47A51A, BlockedTrait); break; case 12: - HookCall(0x479BE1, &BlockedTrait); - HookCall(0x47A0DD, &BlockedTrait); + HookCall(0x479BE1, BlockedTrait); + HookCall(0x47A0DD, BlockedTrait); break; case 14: - HookCall(0x43C295, &BlockedTrait); - HookCall(0x43C2F3, &BlockedTrait); + HookCall(0x43C295, BlockedTrait); + HookCall(0x43C2F3, BlockedTrait); break; case 15: - HookCall(0x43C2A4, &BlockedTrait); + HookCall(0x43C2A4, BlockedTrait); break; } } @@ -954,32 +897,25 @@ static void TraitSetup() { } for (int i = 0; i < TRAIT_count; i++) { - if (traits[i].name != &tName[64 * i]) { - strcpy_s(&tName[64 * i], 64, traits[i].name); - traits[i].name = &tName[64 * i]; + if (traits[i].name != &tName[maxNameLen * i]) { + strcpy_s(&tName[maxNameLen * i], maxNameLen, traits[i].name); + traits[i].name = &tName[maxNameLen * i]; } - if (traits[i].description&&traits[i].description != &tDesc[1024 * i]) { - strcpy_s(&tDesc[1024 * i], 1024, traits[i].description); - traits[i].description = &tDesc[1024 * i]; + if (traits[i].description && traits[i].description != &tDesc[maxDescLen * i]) { + strcpy_s(&tDesc[maxDescLen * i], maxDescLen, traits[i].description); + traits[i].description = &tDesc[maxDescLen * i]; } } } -static __declspec(naked) void PerkInitWrapper() { - __asm { - call fo::funcoffs::perk_init_; - pushad; - call PerkSetup; - popad; - retn; - } -} static __declspec(naked) void TraitInitWrapper() { __asm { call fo::funcoffs::trait_init_; - pushad; + push edx; + push ecx; call TraitSetup; - popad; + pop ecx; + pop edx; retn; } } @@ -991,37 +927,42 @@ void _stdcall SetPerkValue(int id, int value, DWORD offset) { void _stdcall SetPerkName(int id, char* value) { if (id < 0 || id >= PERK_count) return; - strcpy_s(&Name[id * 64], 64, value); + strncpy_s(&Name[id * maxNameLen], maxNameLen, value, _TRUNCATE); } void _stdcall SetPerkDesc(int id, char* value) { if (id < 0 || id >= PERK_count) return; - strcpy_s(&Desc[id * 1024], 1024, value); - perks[id].description = &Desc[1024 * id]; + strncpy_s(&Desc[id * maxDescLen], maxDescLen, value, _TRUNCATE); + perks[id].description = &Desc[maxDescLen * id]; } void PerksReset() { fakeTraits.clear(); fakePerks.clear(); fakeSelectablePerks.clear(); - SafeWrite32(0x43C77D, 0x488CB); IgnoringDefaultPerks = 0; addPerkMode = 2; - SafeWrite8(0x478AC4, 0xBA); - SafeWrite32(0x478AC5, 0x23); PerkFreqOverride = 0; - //Reset some settable game values back to the defaults - //Pyromaniac bonus + if (PerkBoxTitle[0] != 0) { + SafeWrite32(0x43C77D, 0x488CB); + } + + // Reset some settable game values back to the defaults + // Pyromaniac bonus SafeWrite8(0x424AB6, 5); - //Perk level mod + // Perk level mod SafeWrite32(0x496880, 0x00019078); + // Restore 'Heave Ho' modify fix + if (perkHeaveHoModFix) { + SafeWrite8(0x478AC4, 0xBA); + SafeWrite32(0x478AC5, 0x23); + perkHeaveHoModFix = false; + } } void Perks::save(HANDLE file) { - DWORD count; - DWORD unused; - count = fakeTraits.size(); + DWORD unused, count = fakeTraits.size(); WriteFile(file, &count, 4, &unused, 0); for (DWORD i = 0; i < count; i++) { WriteFile(file, &fakeTraits[i], sizeof(FakePerk), &unused, 0); @@ -1039,8 +980,7 @@ void Perks::save(HANDLE file) { } bool Perks::load(HANDLE file) { - DWORD count; - DWORD size; + DWORD count, size; ReadFile(file, &count, 4, &size, 0); if (size != 4) return false; for (DWORD i = 0; i < count; i++) { @@ -1090,8 +1030,8 @@ DWORD _stdcall HasFakeTrait(char* name) { void _stdcall ClearSelectablePerks() { fakeSelectablePerks.clear(); addPerkMode = 2; - SafeWrite32(0x43C77D, 0x488CB); IgnoringDefaultPerks = 0; + SafeWrite32(0x43C77D, 0x488CB); } void PerksEnterCharScreen() { @@ -1116,11 +1056,11 @@ void PerksAcceptCharScreen() { void Perks::init() { for (int i = STAT_st; i <= STAT_lu; i++) SafeWrite8(GainStatPerks[i][0], (BYTE)GainStatPerks[i][1]); - HookCall(0x442729, &PerkInitWrapper); + HookCall(0x442729, PerkInitWrapper); // game_init_ if (GetConfigString("Misc", "PerksFile", "", &perksFile[2], MAX_PATH)) { perksFile[0] = '.'; perksFile[1] = '\\'; - HookCall(0x44272E, &TraitInitWrapper); + HookCall(0x44272E, TraitInitWrapper); // game_init_ } else perksFile[0] = 0; LoadGameHook::OnGameReset() += PerksReset; diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 08cc2ce74..4e518158a 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -45,6 +45,8 @@ namespace sfall using namespace script; +static Delegate<> onMapExit; + static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId); static int idle; @@ -75,16 +77,17 @@ static std::map globalScriptFilesList; static std::vector checkedScripts; static std::vector globalScripts; + // a map of all sfall programs (global and hook scripts) by thier scriptPtr typedef std::unordered_map SfallProgsMap; static SfallProgsMap sfallProgsMap; + // a map scriptPtr => self_obj to override self_obj for all script types using set_self std::unordered_map selfOverrideMap; typedef std::unordered_map ExportedVarsMap; static ExportedVarsMap globalExportedVars; DWORD isGlobalScriptLoading = 0; -DWORD modifiedIni; std::unordered_map<__int64, int> globalVars; typedef std::unordered_map<__int64, int> :: iterator glob_itr; @@ -93,6 +96,7 @@ typedef std::pair<__int64, int> glob_pair; DWORD availableGlobalScriptTypes = 0; bool isGameLoading; +bool alwaysFindScripts; fo::ScriptInstance overrideScriptStruct; @@ -405,7 +409,7 @@ ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr) { } bool _stdcall IsGameScript(const char* filename) { - if ((filename[0] != 'g' || filename[1] != 'l') && (filename[0] != 'h' || filename[1] != 's')) return true; + if (strlen(filename) > 8) return false; // TODO: write better solution for (int i = 0; i < fo::var::maxScriptNum; i++) { if (strcmp(filename, fo::var::scriptListInfo[i].fileName) == 0) return true; @@ -435,6 +439,7 @@ static void LoadGlobalScriptsList() { } static void PrepareGlobalScriptsListByMask() { + globalScriptFilesList.clear(); for (auto& fileMask : globalScriptPathList) { char** filenames; auto basePath = fileMask.substr(0, fileMask.find_last_of("\\/") + 1); // path to scripts without mask @@ -442,8 +447,12 @@ static void PrepareGlobalScriptsListByMask() { for (int i = 0; i < count; i++) { char* name = _strlwr(filenames[i]); // name of the script in lower case + std::string baseName(name); - baseName = baseName.substr(0, baseName.find_last_of('.')); // script name without extension + int lastDot = baseName.find_last_of('.'); + if ((baseName.length() - lastDot) > 4) continue; // skip files with invalid extension (bug in db_get_file_list fuction) + + baseName = baseName.substr(0, lastDot); // script name without extension if (basePath != fo::var::script_path_base || !IsGameScript(baseName.c_str())) { std::string fullPath(basePath); fullPath += name; @@ -457,7 +466,6 @@ static void PrepareGlobalScriptsListByMask() { } fo::func::db_free_file_list(&filenames, 0); } - globalScriptPathList.clear(); // clear path list, it is no longer needed } // this runs after the game was loaded/started @@ -466,9 +474,10 @@ static void LoadGlobalScripts() { isGameLoading = false; LoadHookScripts(); dlogr("Loading global scripts", DL_SCRIPT|DL_INIT); - if (!listIsPrepared) { // runs only once + if (!listIsPrepared) { // only once PrepareGlobalScriptsListByMask(); - listIsPrepared = true; + listIsPrepared = !alwaysFindScripts; + if (listIsPrepared) globalScriptPathList.clear(); // clear path list, it is no longer needed } LoadGlobalScriptsList(); dlogr("Finished loading global scripts", DL_SCRIPT|DL_INIT); @@ -540,7 +549,7 @@ static void RunScript(GlobalScript* script) { */ static void ResetStateAfterFrame() { if (tempArrays.size()) { - for (std::set::iterator it = tempArrays.begin(); it != tempArrays.end(); ++it) + for (std::set::iterator it = tempArrays.begin(); it != tempArrays.end(); ++it) FreeArray(*it); tempArrays.clear(); } @@ -585,6 +594,8 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { RunGlobalScriptsAtProc(procId); // gl* scripts of types 0 and 3 RunHookScriptsAtProc(procId); // all hs_ scripts + if (procId == fo::ScriptProc::map_exit_p_proc) onMapExit.invoke(); + return procId; // restore eax (don't delete) } @@ -677,7 +688,6 @@ void ScriptExtender::init() { SafeWrite8(0x4C9F12, 0x6A); // push SafeWrite8(0x4C9F13, idle); } - modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); arraysBehavior = GetConfigInt("Misc", "arraysBehavior", 1); if (arraysBehavior > 0) { @@ -687,6 +697,9 @@ void ScriptExtender::init() { dlogr("Arrays in backward-compatiblity mode.", DL_SCRIPT); } + alwaysFindScripts = isDebug && (GetPrivateProfileIntA("Debugging", "AlwaysFindScripts", 0, ::sfall::ddrawIni) != 0); + if (alwaysFindScripts) dlogr("Always searching for global scripts behavior enabled.", DL_SCRIPT); + MakeJump(0x4A390C, FindSidHack); MakeJump(0x4A5E34, ScrPtrHack); memset(&overrideScriptStruct, 0, sizeof(fo::ScriptInstance)); @@ -709,4 +722,8 @@ void ScriptExtender::init() { InitNewOpcodes(); } +Delegate<>& ScriptExtender::OnMapExit() { + return onMapExit; +} + } diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index e36e99ee8..0ac7b6203 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -20,6 +20,7 @@ #include "..\main.h" #include "..\FalloutEngine\Structs.h" +#include "..\Delegate.h" #include "Module.h" @@ -30,6 +31,9 @@ class ScriptExtender : public Module { public: const char* name() { return "ScriptExtender"; } void init(); + + // Called before map exit + static Delegate<>& OnMapExit(); }; #pragma pack(8) @@ -71,15 +75,19 @@ void GetAppearanceGlobals(int *race, int *style); void _stdcall RegAnimCombatCheck(DWORD newValue); bool _stdcall ScriptHasLoaded(fo::Program* script); + // loads script from .int file into a sScriptProgram struct, filling script pointer and proc lookup table // prog - reference to program structure // fileName - the script file name without extension (if fullPath is false) or a full file path (if fullPath is true) // fullPath - controls how fileName is used (see above) void LoadScriptProgram(ScriptProgram &prog, const char* fileName, bool fullPath = false); + // init program after load, needs to be called once void InitScriptProgram(ScriptProgram &prog); + // execute script by specific proc name void RunScriptProc(ScriptProgram* prog, const char* procName); + // execute script proc by procId from define.h void RunScriptProc(ScriptProgram* prog, long procId); @@ -90,6 +98,6 @@ ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr); static char regAnimCombatCheck = 1; extern DWORD isGlobalScriptLoading; extern DWORD availableGlobalScriptTypes; -extern DWORD modifiedIni; +extern bool alwaysFindScripts; } diff --git a/sfall/Modules/Scripting/Arrays.cpp b/sfall/Modules/Scripting/Arrays.cpp index c327bc293..6df24ceb3 100644 --- a/sfall/Modules/Scripting/Arrays.cpp +++ b/sfall/Modules/Scripting/Arrays.cpp @@ -109,7 +109,7 @@ DWORD sArrayElement::getHashStatic(DWORD value, DataType type) { str = (const char*)value; int i; DWORD res; - for (i=0, res=0; str[i]!='\0'; i++) { + for (i = 0, res = 0; str[i] != '\0'; i++) { res = ((res << 5) + res) + str[i]; } return res; @@ -197,28 +197,28 @@ void LoadArraysOld(HANDLE h) { dlogr("Loading arrays (old fmt)", DL_MAIN); DWORD count, unused, id, j; ReadFile(h, &count, 4, &unused, 0); - if(unused!=4) return; + if (unused != 4) return; sArrayVarOld var; sArrayVar varN; - for(DWORD i=0;i(arrayVar.key.type) > 4 || arrayVar.key.intVal == 0) { // partial compatibility with 3.4 arrayVar.key.intVal = static_cast(arrayVar.key.type); @@ -248,7 +248,7 @@ void LoadArrays(HANDLE h) { ReadFile(h, &elCount, 4, &unused, 0); // actual number of elements: keys+values bool isAssoc = arrayVar.isAssoc(); arrayVar.val.resize(elCount); - for (j=0; jfirst; - _arrays[pos++]=itr->second.size(); - _arrays[pos++]=itr->second.flags; + int pos = 0; + array_citr itr = arrays.begin(); + while (itr != arrays.end()) { + _arrays[pos++] = itr->first; + _arrays[pos++] = itr->second.size(); + _arrays[pos++] = itr->second.flags; itr++; } } @@ -315,36 +315,39 @@ void DEGetArray(int id, DWORD* types, void* data) { } void DESetArray(int id, const DWORD* types, const void* data) { - //if(types) memcpy(arrays[id].types, types, arrays[id].len*4); + //if (types) memcpy(arrays[id].types, types, arrays[id].len * 4); //memcpy(arrays[id].data, data, arrays[id].len*arrays[id].datalen); } - /* Array manipulation functions for script operators TODO: move somewhere else */ -DWORD _stdcall CreateArray(int len, DWORD nothing) { +DWORD _stdcall CreateArray(int len, DWORD flags) { sArrayVar var; - if (len < 0) var.flags |= ARRAYFLAG_ASSOC; - else if (len > ARRAY_MAX_SIZE) len = ARRAY_MAX_SIZE; // safecheck + var.flags = (flags & 0xFFFFFFFE); // reset 1 bit + if (len < 0) { + var.flags |= ARRAYFLAG_ASSOC; + } else if (len > ARRAY_MAX_SIZE) { + len = ARRAY_MAX_SIZE; // safecheck + } if (!var.isAssoc()) { var.val.resize(len); } - while(arrays.find(nextarrayid)!=arrays.end()) nextarrayid++; + while (arrays.find(nextarrayid) != arrays.end()) nextarrayid++; if (nextarrayid == 0) nextarrayid++; if (arraysBehavior == 0) { var.key = sArrayElement(nextarrayid, DataType::INT); savedArrays[var.key] = nextarrayid; } stackArrayId = nextarrayid; - arrays[nextarrayid]=var; + arrays[nextarrayid] = var; return nextarrayid++; } -DWORD _stdcall TempArray(DWORD len, DWORD nothing) { - DWORD id=CreateArray(len, nothing); +DWORD _stdcall TempArray(DWORD len, DWORD flags) { + DWORD id = CreateArray(len, flags); tempArrays.insert(id); return id; } @@ -425,10 +428,14 @@ void _stdcall SetArray(DWORD id, const ScriptValue& key, const ScriptValue& val, if (arrays[id].isAssoc()) { sArrayElement sEl(key.rawValue(), key.type()); ArrayKeysMap::iterator elIter = arr.keyHash.find(sEl); - el = (elIter != arr.keyHash.end()) - ? elIter->second + el = (elIter != arr.keyHash.end()) + ? elIter->second : -1; - if (val.isInt() && val.asInt() == 0 && allowUnset) { + + bool lookupMap = (arr.flags & ARRAYFLAG_CONSTVAL) != 0; + if (lookupMap && el != -1) return; // don't update value of key + + if (allowUnset && !lookupMap && (val.isInt() && val.rawValue() == 0)) { // after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset if (el >= 0) { // remove from hashtable @@ -464,27 +471,86 @@ void _stdcall SetArray(DWORD id, const ScriptValue& key, const ScriptValue& val, } int _stdcall LenArray(DWORD id) { - if(arrays.find(id)==arrays.end()) return -1; + if (arrays.find(id) == arrays.end()) return -1; else return arrays[id].size(); } -void _stdcall ResizeArray(DWORD id, int newlen) { - if (arrays.find(id) == arrays.end() || arrays[id].size() == newlen) return; +template +static void ListSort(std::vector &arr, int type) { + switch (type) { + case ARRAY_ACTION_SORT: // sort ascending + std::sort(arr.begin(), arr.end()); + break; + case ARRAY_ACTION_RSORT: // sort descending + std::sort(arr.rbegin(), arr.rend()); + break; + case ARRAY_ACTION_REVERSE: // reverse elements + std::reverse(arr.rbegin(), arr.rend()); + break; + case ARRAY_ACTION_SHUFFLE: // shuffle elements + std::random_shuffle(arr.rbegin(), arr.rend()); + break; + } +} + +static void MapSort(sArrayVar& arr, int type) { + std::vector> map; + map.reserve(arr.val.size()); + + bool sortByValue = false; + if (type < ARRAY_ACTION_SHUFFLE) { + type += 4; + sortByValue = true; + } + + sArrayElement key, val; + for (size_t i = 0; i < arr.val.size(); ++i) { + if (sortByValue) { + val = arr.val[i++]; // map key > value + key = arr.val[i]; // map value > key + } else { + key = arr.val[i]; // key + val = arr.val[++i]; // value + } + map.emplace_back(std::make_pair(key, val)); + } + ListSort(map, type); + + arr.val.clear(); + arr.keyHash.clear(); + for (size_t i = 0; i < map.size(); ++i) { + auto el = arr.val.size(); + if (sortByValue) { + arr.val.emplace_back(map[i].second); // map value > key + arr.val.emplace_back(map[i].first); // map key > value + } else { + arr.val.emplace_back(map[i].first); + arr.val.emplace_back(map[i].second); + } + arr.keyHash[arr.val[el]] = el; + } +} + +long _stdcall ResizeArray(DWORD id, int newlen) { + if (newlen == -1 || arrays.find(id) == arrays.end() || arrays[id].size() == newlen) return 0; sArrayVar &arr = arrays[id]; if (arr.isAssoc()) { // only allow to reduce number of elements (adding range of elements is meaningless for maps) - if (newlen < arrays[id].size()) { + if (newlen >= 0 && newlen < arrays[id].size()) { ArrayKeysMap::iterator itHash; std::vector::iterator itVal; - int actualLen = newlen*2; + int actualLen = newlen * 2; for (itVal = arr.val.begin() + actualLen; itVal != arr.val.end(); itVal += 2) { if ((itHash = arr.keyHash.find(*itVal)) != arr.keyHash.end()) arr.keyHash.erase(itHash); } arr.clearRange(actualLen); arr.val.resize(actualLen); + } else if (newlen < 0) { + if (newlen < (ARRAY_ACTION_SHUFFLE - 2)) return -1; + MapSort(arr, newlen); } - return; + return 0; } if (newlen >= 0) { // actual resize if (newlen > ARRAY_MAX_SIZE) // safety @@ -493,21 +559,10 @@ void _stdcall ResizeArray(DWORD id, int newlen) { arr.clearRange(newlen); arr.val.resize(newlen); } else { // special functions for lists... - switch (newlen) { - case ARRAY_ACTION_SORT: // sort ascending - std::sort(arr.val.begin(), arr.val.end()); - break; - case ARRAY_ACTION_RSORT: // sort descending - std::sort(arr.val.rbegin(), arr.val.rend()); - break; - case ARRAY_ACTION_REVERSE: // reverse elements - std::reverse(arr.val.rbegin(), arr.val.rend()); - break; - case ARRAY_ACTION_SHUFFLE: // shuffle elements - std::random_shuffle(arr.val.rbegin(), arr.val.rend()); - break; - } + if (newlen < ARRAY_ACTION_SHUFFLE) return -1; + ListSort(arr.val, newlen); } + return 0; } void _stdcall FixArray(DWORD id) { @@ -522,17 +577,17 @@ ScriptValue _stdcall ScanArray(DWORD id, const ScriptValue& val) { for (size_t i = 0; i < arrays[id].val.size(); i += step) { sArrayElement &el = arrays[id].val[i + step - 1]; if (el.type == val.type()) { - if ((!val.isString() && static_cast(el.intVal) == val.rawValue()) || - (val.isString() && strcmp(el.strVal, val.asString()) == 0)) { - if (arrays[id].isAssoc()) { // return key instead of index for associative arrays - return ScriptValue( - static_cast(arrays[id].val[i].type), - static_cast(arrays[id].val[i].intVal) - ); - } else { - return ScriptValue(static_cast(i)); - } - } + if ((!val.isString() && static_cast(el.intVal) == val.rawValue()) || + (val.isString() && strcmp(el.strVal, val.asString()) == 0)) { + if (arrays[id].isAssoc()) { // return key instead of index for associative arrays + return ScriptValue( + static_cast(arrays[id].val[i].type), + static_cast(arrays[id].val[i].intVal) + ); + } else { + return ScriptValue(static_cast(i)); + } + } } } return ScriptValue(-1); diff --git a/sfall/Modules/Scripting/Arrays.h b/sfall/Modules/Scripting/Arrays.h index 1949db9ba..93734c6d1 100644 --- a/sfall/Modules/Scripting/Arrays.h +++ b/sfall/Modules/Scripting/Arrays.h @@ -82,6 +82,7 @@ struct sArrayVarOld }; #define ARRAYFLAG_ASSOC (1) // is map +#define ARRAYFLAG_CONSTVAL (2) // don't update value of key if the key exists in map typedef std::unordered_map ArrayKeysMap; @@ -96,13 +97,14 @@ class sArrayVar { public: DWORD flags; - sArrayElement key; // array associated key, if it was saved + sArrayElement key; // array associated key, if it was saved ArrayKeysMap keyHash; // key element => element index, for faster lookup std::vector val; // list of values or key=>value pairs (even - keys, odd - values) bool isAssoc() const { return (flags & ARRAYFLAG_ASSOC); } + // logical array size (number of elements for normal arrays; number of key=>value pairs for associative) int size() const { return isAssoc() @@ -118,6 +120,7 @@ class sArrayVar } sArrayVar() : flags(0), key() {} + // free memory used by strings void clear() { clearRange(0); @@ -152,9 +155,9 @@ void DEGetArray(int id, DWORD* types, void* data); void DESetArray(int id, const DWORD* types, const void* data); // creates new normal (persistent) array. len == -1 specifies associative array (map) -DWORD _stdcall CreateArray(int len, DWORD nothing); +DWORD _stdcall CreateArray(int len, DWORD flags); // same as CreateArray, but creates temporary array instead (will die at the end of the frame) -DWORD _stdcall TempArray(DWORD len, DWORD nothing); +DWORD _stdcall TempArray(DWORD len, DWORD flags); // destroys array void _stdcall FreeArray(DWORD id); /* @@ -168,7 +171,7 @@ void _stdcall SetArray(DWORD id, const ScriptValue& key, const ScriptValue& val, // number of elements in list or pairs in map int _stdcall LenArray(DWORD id); // change array size (only works with list) -void _stdcall ResizeArray(DWORD id, int newlen); +long _stdcall ResizeArray(DWORD id, int newlen); // make temporary array persistent void _stdcall FixArray(DWORD id); // searches for a given element in array and returns it's index (for list) or key (for map) or int(-1) if not found diff --git a/sfall/Modules/Scripting/Handlers/Arrays.cpp b/sfall/Modules/Scripting/Handlers/Arrays.cpp index bb430f634..ab90f0e84 100644 --- a/sfall/Modules/Scripting/Handlers/Arrays.cpp +++ b/sfall/Modules/Scripting/Handlers/Arrays.cpp @@ -59,10 +59,10 @@ void sf_get_array(OpcodeContext& ctx) { auto str = Substring(ctx.arg(0).asString(), ctx.arg(1).asInt(), 1); ctx.setReturn(str); } else { - ctx.printOpcodeError("get_array - index must be numeric when used on a string."); + ctx.printOpcodeError("get_array() - index must be numeric when used on a string."); } } else { - ctx.printOpcodeError("get-array - argument 0 must be an array ID or a string."); + ctx.printOpcodeError("get_array() - argument 0 must be an array ID or a string."); } } @@ -77,7 +77,9 @@ void sf_len_array(OpcodeContext& ctx) { } void sf_resize_array(OpcodeContext& ctx) { - ResizeArray(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + if (ResizeArray(ctx.arg(0).asInt(), ctx.arg(1).asInt())) { + ctx.printOpcodeError("resize_array() - array sorting error."); + } } void sf_temp_array(OpcodeContext& ctx) { diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index cdd8adc90..59712045d 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -107,7 +107,7 @@ static const SfallMetarule metarules[] = { {"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}}, {"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}}, {"loot_obj", sf_get_loot_object, 0, 0}, - {"obj_under_cursor", sf_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}}, + {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, {"set_can_rest_on_map", sf_set_rest_on_map, 3, 3, {ARG_INT, ARG_INT, ARG_INT}}, diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 8e7b2ee04..56c3ff36a 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -20,11 +20,11 @@ #include "..\..\..\FalloutEngine\Fallout2.h" #include "..\..\AI.h" +#include "..\..\Combat.h" #include "..\..\Criticals.h" #include "..\..\HeroAppearance.h" #include "..\..\Inventory.h" #include "..\..\KillCounter.h" -#include "..\..\Knockback.h" #include "..\..\MiscPatches.h" #include "..\..\Movies.h" #include "..\..\Objects.h" @@ -1722,9 +1722,5 @@ void sf_get_ini_section(OpcodeContext& ctx) { ctx.setReturn(arrayId); } -void sf_obj_under_cursor(OpcodeContext& ctx) { - ctx.setReturn(fo::func::object_under_mouse(ctx.arg(0).asBool() ? 1 : -1, ctx.arg(1).rawValue(), fo::var::map_elevation)); -} - } } diff --git a/sfall/Modules/Scripting/Handlers/Misc.h b/sfall/Modules/Scripting/Handlers/Misc.h index 3a34f1a79..500f76ea0 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.h +++ b/sfall/Modules/Scripting/Handlers/Misc.h @@ -165,7 +165,5 @@ void sf_get_ini_sections(OpcodeContext&); void sf_get_ini_section(OpcodeContext&); -void sf_obj_under_cursor(OpcodeContext&); - } } diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 1399c88a5..9b524f6fd 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -17,8 +17,8 @@ */ #include "..\..\..\FalloutEngine\Fallout2.h" +#include "..\..\Combat.h" #include "..\..\Explosions.h" -#include "..\..\Knockback.h" #include "..\..\Inventory.h" #include "..\..\LoadGameHook.h" #include "..\..\Objects.h" @@ -212,28 +212,6 @@ void sf_set_weapon_ammo_count(OpcodeContext& ctx) { obj->item.charges = ctx.arg(1).asInt(); } -static DWORD _stdcall obj_blocking_at_wrapper(DWORD obj, DWORD tile, DWORD elevation, DWORD func) { - __asm { - mov eax, obj; - mov edx, tile; - mov ebx, elevation; - call func; - } -} - -static DWORD _stdcall make_straight_path_func_wrapper(fo::GameObject* obj, DWORD tileFrom, DWORD a3, DWORD tileTo, DWORD* result, DWORD a6, DWORD func) { - __asm { - mov eax, obj; - mov edx, tileFrom; - mov ecx, a3; - mov ebx, tileTo; - push func; - push a6; - push result; - call fo::funcoffs::make_straight_path_func_; - } -} - #define BLOCKING_TYPE_BLOCK (0) #define BLOCKING_TYPE_SHOOT (1) #define BLOCKING_TYPE_AI (2) @@ -259,21 +237,25 @@ static DWORD getBlockingFunc(DWORD type) { void sf_make_straight_path(OpcodeContext& ctx) { auto objFrom = ctx.arg(0).asObject(); DWORD tileTo = ctx.arg(1).asInt(), - type = ctx.arg(2).asInt(), - resultObj, arg6; - arg6 = (type == BLOCKING_TYPE_SHOOT) ? 32 : 0; - make_straight_path_func_wrapper(objFrom, objFrom->tile, 0, tileTo, &resultObj, arg6, getBlockingFunc(type)); + type = ctx.arg(2).asInt(); + + long flag = (type == BLOCKING_TYPE_SHOOT) ? 32 : 0; + DWORD resultObj = 0; + fo::func::make_straight_path_func(objFrom, objFrom->tile, tileTo, 0, &resultObj, flag, (void*)getBlockingFunc(type)); ctx.setReturn(resultObj, DataType::INT); } void sf_make_path(OpcodeContext& ctx) { auto objFrom = ctx.arg(0).asObject(); auto tileTo = ctx.arg(1).asInt(), - type = ctx.arg(2).asInt(); + type = ctx.arg(2).asInt(); auto func = getBlockingFunc(type); - long pathLength, a5 = 1; + + // if the object is not a critter, then there is no need to check tile (tileTo) for blocking + long checkFlag = (objFrom->type() == fo::OBJ_TYPE_CRITTER); + char pathData[800]; - pathLength = fo::func::make_path_func(objFrom, objFrom->tile, tileTo, pathData, a5, (void*)func); + long pathLength = fo::func::make_path_func(objFrom, objFrom->tile, tileTo, pathData, checkFlag, (void*)func); auto arrayId = TempArray(pathLength, 0); for (int i = 0; i < pathLength; i++) { arrays[arrayId].val[i].set((long)pathData[i]); @@ -283,15 +265,15 @@ void sf_make_path(OpcodeContext& ctx) { void sf_obj_blocking_at(OpcodeContext& ctx) { DWORD tile = ctx.arg(0).asInt(), - elevation = ctx.arg(1).asInt(), - type = ctx.arg(2).asInt(), - resultObj; - resultObj = obj_blocking_at_wrapper(0, tile, elevation, getBlockingFunc(type)); - if (resultObj && type == BLOCKING_TYPE_SHOOT && (*(DWORD*)(resultObj + 39) & 0x80)) { // don't know what this flag means, copy-pasted from the engine code + elevation = ctx.arg(1).asInt(), + type = ctx.arg(2).asInt(); + + fo::GameObject* resultObj = fo::func::obj_blocking_at_wrapper(0, tile, elevation, (void*)getBlockingFunc(type)); + if (resultObj && type == BLOCKING_TYPE_SHOOT && (resultObj->flags & fo::ObjectFlag::ShootThru)) { // don't know what this flag means, copy-pasted from the engine code // this check was added because the engine always does exactly this when using shoot blocking checks - resultObj = 0; + resultObj = nullptr; } - ctx.setReturn(resultObj, DataType::INT); + ctx.setReturn((DWORD)resultObj, DataType::INT); } void sf_tile_get_objects(OpcodeContext& ctx) { @@ -456,6 +438,10 @@ void sf_get_dialog_object(OpcodeContext& ctx) { ctx.setReturn(InDialog() ? fo::var::dialog_target : 0); } +void sf_get_obj_under_cursor(OpcodeContext& ctx) { + ctx.setReturn(fo::func::object_under_mouse(ctx.arg(0).asBool() ? 1 : -1, ctx.arg(1).rawValue(), fo::var::map_elevation)); +} + void sf_get_loot_object(OpcodeContext& ctx) { ctx.setReturn((GetLoopFlags() & INTFACELOOT) ? LoadGameHook::LootTarget : 0); } diff --git a/sfall/Modules/Scripting/Handlers/Objects.h b/sfall/Modules/Scripting/Handlers/Objects.h index 7ffc3251d..df882b224 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.h +++ b/sfall/Modules/Scripting/Handlers/Objects.h @@ -97,6 +97,8 @@ void sf_get_current_inven_size(OpcodeContext&); void sf_get_dialog_object(OpcodeContext&); +void sf_get_obj_under_cursor(OpcodeContext&); + void sf_get_loot_object(OpcodeContext&); void sf_get_object_data(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Handlers/Stats.cpp b/sfall/Modules/Scripting/Handlers/Stats.cpp index c93d74cef..9aa9f2d85 100644 --- a/sfall/Modules/Scripting/Handlers/Stats.cpp +++ b/sfall/Modules/Scripting/Handlers/Stats.cpp @@ -17,8 +17,8 @@ */ #include "..\..\..\FalloutEngine\Fallout2.h" +#include "..\..\Combat.h" #include "..\..\Criticals.h" -#include "..\..\Knockback.h" #include "..\..\ScriptExtender.h" #include "..\..\Skills.h" #include "..\..\Stats.h" diff --git a/sfall/Modules/Skills.cpp b/sfall/Modules/Skills.cpp index 9c9eefe07..e2bed76ed 100644 --- a/sfall/Modules/Skills.cpp +++ b/sfall/Modules/Skills.cpp @@ -23,8 +23,9 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" -#include "Knockback.h" +#include "Combat.h" #include "LoadGameHook.h" +#include "Objects.h" #include "Skills.h" @@ -32,100 +33,104 @@ namespace sfall { struct SkillModifier { - DWORD id { 0 }; - int maximum { 300 }; - int mod { 0 }; + long id; + int maximum; + //int mod; + + SkillModifier() : id(0), maximum(300)/*, mod(0)*/ {} + + SkillModifier(long _id, int max) { + id = _id; + maximum = max; + //mod = _mod; + } + + void SetDefault() { + maximum = 300; + //mod = 0; + } }; static std::vector skillMaxMods; static SkillModifier baseSkillMax; + static BYTE skillCosts[512 * fo::SKILL_count]; static DWORD basedOnPoints; +static double* multipliers; + +static std::vector pickpocketMods; +static ChanceModifier basePickpocket; -static int _stdcall SkillMaxHook2(int base, DWORD critter) { +static int __fastcall PickpocketMod(int base, fo::GameObject* critter) { + for (DWORD i = 0; i < pickpocketMods.size(); i++) { + if (critter->id == pickpocketMods[i].id) { + return min(base + pickpocketMods[i].mod, pickpocketMods[i].maximum); + } + } + return min(base + basePickpocket.mod, basePickpocket.maximum); +} + +static void __declspec(naked) skill_check_stealing_hack() { + __asm { + push edx; + push ecx; + mov edx, esi; // critter + mov ecx, eax; // base (calculated chance) + call PickpocketMod; + pop ecx; + pop edx; + retn; + } +} + +static int __fastcall CheckSkillMax(fo::GameObject* critter, int base) { for (DWORD i = 0; i < skillMaxMods.size(); i++) { - if (critter == skillMaxMods[i].id) { + if (critter->id == skillMaxMods[i].id) { return min(base, skillMaxMods[i].maximum); } } return min(base, baseSkillMax.maximum); } -static void __declspec(naked) SkillHookA() { +static void __declspec(naked) skill_level_hack() { __asm { - push ecx; - push esi; - call SkillMaxHook2; - push 0x4AA64B; - retn; + mov edx, esi; // level skill (base) + call CheckSkillMax; // ecx - critter + mov edx, 0x4AA64B; + jmp edx; } } -static void __declspec(naked) SkillHookB() { +static void __declspec(naked) skill_inc_point_force_hack() { __asm { - push edx; push ecx; - push ebx; push eax; - push ecx; - push 0x7fffffff; - call SkillMaxHook2; - pop edx; - cmp edx, eax; - pop ebx; + mov edx, 0x7FFFFFFF; // base + call CheckSkillMax; // ecx - critter + pop edx; // skill level (from eax) + cmp edx, eax; // eax = max pop ecx; - pop edx; - jl win; - push 0x4AA84E; - retn; -win: - push 0x4AA85C; retn; } } -static const DWORD SkillHookWin = 0x4AA738; -static const DWORD SkillHookFail = 0x4AA72C; -static void __declspec(naked) SkillHookC() { +static void __declspec(naked) skill_inc_point_hack() { __asm { - pushad; + push ecx; push eax; - push esi; - push 0x7fffffff; - call SkillMaxHook2; - pop edx; - cmp edx, eax; - popad; - jl win; - jmp SkillHookFail; -win: - jmp SkillHookWin; - } -} - -void _stdcall SetSkillMax(DWORD critter, DWORD maximum) { - if (critter == -1) { - baseSkillMax.maximum = maximum; - return; - } - for (DWORD i = 0; i < skillMaxMods.size(); i++) { - if (critter == skillMaxMods[i].id) { - skillMaxMods[i].maximum = maximum; - return; - } + mov edx, 0x7FFFFFFF; // base + mov ecx, esi; // critter + call CheckSkillMax; + pop edx; // skill level (from eax) + cmp edx, eax; // eax = max + pop ecx; + retn; } - SkillModifier cm; - cm.id = critter; - cm.maximum = maximum; - cm.mod = 0; - skillMaxMods.push_back(cm); } -double* multipliers; - -static int _stdcall GetStatBonusHook2(const fo::SkillInfo* info, int skill, int points, fo::GameObject* critter) { +static int __fastcall GetStatBonus(fo::GameObject* critter, const fo::SkillInfo* info, int skill, int points) { double result = 0; - for (int i = fo::Stat::STAT_st; i <= fo::Stat::STAT_lu; i++) { + for (int i = 0; i < 7; i++) { result += fo::func::stat_level(critter, i) * multipliers[skill * 7 + i]; } result += points * info->skillPointMulti; @@ -133,88 +138,119 @@ static int _stdcall GetStatBonusHook2(const fo::SkillInfo* info, int skill, int return (int)result; } -//On input, ebx contains the skill id, ecx contains the critter, edx contains the skill id, edi contains a SkillInfo*, ebp contains the number of skill points +//On input, ebx/edx contains the skill id, ecx contains the critter, edi contains a SkillInfo*, ebp contains the number of skill points //On exit ebx, ecx, edi, ebp are preserved, esi contains skill base + stat bonus + skillpoints * multiplier static const DWORD StatBonusHookRet = 0x4AA5D6; -static void __declspec(naked) GetStatBonusHook() { +static void __declspec(naked) skill_level_hack_bonus() { __asm { - push edx; - push ecx; push ecx; push ebp; push ebx; - push edi; - call GetStatBonusHook2; - mov esi, eax; - pop ecx; - pop edx; - jmp StatBonusHookRet; + mov edx, edi; + call GetStatBonus; // ecx - critter + mov esi, eax; + pop ecx; + jmp StatBonusHookRet; } } static const DWORD SkillIncCostRet = 0x4AA7C1; -static void __declspec(naked) SkillIncCostHook() { - __asm { - //eax - current skill level, ebx - current skill, ecx - num free skill points - mov edx, basedOnPoints; +static void __declspec(naked) skill_inc_point_hack_cost() { + __asm { // eax - current skill level, ebx - current skill, ecx - num free skill points + mov edx, basedOnPoints; test edx, edx; - jz next; - mov edx, ebx; - mov eax, esi; + jz next; + mov edx, ebx; + mov eax, esi; call fo::funcoffs::skill_points_; next: - mov edx, ebx; - shl edx, 9; - add edx, eax; - movzx eax, skillCosts[edx]; - //eax - cost of the skill - jmp SkillIncCostRet; + mov edx, ebx; + shl edx, 9; + add edx, eax; + movzx eax, skillCosts[edx]; // eax - cost of the skill + jmp SkillIncCostRet; } } static const DWORD SkillDecCostRet = 0x4AA98D; -static void __declspec(naked) SkillDecCostHook() { - __asm { - //eax - current skill level, ebx - current skill, ecx - num free skill points - mov edx, basedOnPoints; +static void __declspec(naked) skill_dec_point_hack_cost() { + __asm { // eax - current skill level, ebx - current skill, ecx - num free skill points + mov edx, basedOnPoints; test edx, edx; - jz next; - mov edx, ebx; - mov eax, edi; + jz next; + mov edx, ebx; + mov eax, edi; call fo::funcoffs::skill_points_; + lea ecx, [eax - 1]; next: - lea ecx, [eax-1]; - mov edx, ebx; - shl edx, 9; - add edx, ecx; - movzx eax, skillCosts[edx]; - //eax - cost of the skill - jmp SkillDecCostRet; + mov edx, ebx; + shl edx, 9; + add edx, ecx; + movzx eax, skillCosts[edx]; // eax - cost of the skill + jmp SkillDecCostRet; } } -static void __declspec(naked) SkillLevelCostHook() { +static void __declspec(naked) skill_dec_point_hook_cost() { __asm { - push edx; - mov edx, ebx; - shl edx, 9; - add edx, eax; + mov edx, ebx; + shl edx, 9; + add edx, eax; movzx eax, skillCosts[edx]; - pop edx; retn; } } +void _stdcall SetSkillMax(fo::GameObject* critter, int maximum) { + if ((DWORD)critter == -1) { + baseSkillMax.maximum = maximum; + return; + } + + long id = Objects::SetObjectUniqueID(critter); + for (DWORD i = 0; i < skillMaxMods.size(); i++) { + if (id == skillMaxMods[i].id) { + skillMaxMods[i].maximum = maximum; + return; + } + } + skillMaxMods.push_back(SkillModifier(id, maximum)); +} + +void _stdcall SetPickpocketMax(fo::GameObject* critter, DWORD maximum, DWORD mod) { + if ((DWORD)critter == -1) { + basePickpocket.maximum = maximum; + basePickpocket.mod = mod; + return; + } + + long id = Objects::SetObjectUniqueID(critter); + for (DWORD i = 0; i < pickpocketMods.size(); i++) { + if (id == pickpocketMods[i].id) { + pickpocketMods[i].maximum = maximum; + pickpocketMods[i].mod = mod; + return; + } + } + pickpocketMods.push_back(ChanceModifier(id, maximum, mod)); +} + void Skills_OnGameLoad() { + pickpocketMods.clear(); + basePickpocket.SetDefault(); + skillMaxMods.clear(); - baseSkillMax.maximum = 300; - baseSkillMax.mod = 0; + baseSkillMax.SetDefault(); } void Skills::init() { - MakeJump(0x4AA63C, SkillHookA); - MakeJump(0x4AA847, SkillHookB); - MakeJump(0x4AA725, SkillHookC); + MakeJump(0x4AA63C, skill_level_hack, 1); + MakeCall(0x4AA847, skill_inc_point_force_hack); + MakeCall(0x4AA725, skill_inc_point_hack); + + MakeCall(0x4ABC62, skill_check_stealing_hack); // PickpocketMod + SafeWrite8(0x4ABC67, 0x89); // mov [esp + 0x54], eax + SafeWrite32(0x4ABC6B, 0x90909090); char buf[512], key[16], file[64]; auto skillsFile = GetConfigString("Misc", "SkillsFile", ""); @@ -272,19 +308,21 @@ void Skills::init() { } sprintf(key, "SkillBase%d", i); skills[i].base = GetPrivateProfileIntA("Skills", key, skills[i].base, file); + sprintf(key, "SkillMulti%d", i); skills[i].skillPointMulti = GetPrivateProfileIntA("Skills", key, skills[i].skillPointMulti, file); + sprintf(key, "SkillImage%d", i); skills[i].image = GetPrivateProfileIntA("Skills", key, skills[i].image, file); } - MakeJump(0x4AA59D, GetStatBonusHook); - MakeJump(0x4AA738, SkillIncCostHook); - MakeJump(0x4AA93D, SkillDecCostHook); - HookCall(0x4AA9E1, &SkillLevelCostHook); - HookCall(0x4AA9F1, &SkillLevelCostHook); + MakeJump(0x4AA59D, skill_level_hack_bonus, 1); + MakeJump(0x4AA738, skill_inc_point_hack_cost); + MakeJump(0x4AA940, skill_dec_point_hack_cost, 1); + HookCalls(skill_dec_point_hook_cost, {0x4AA9E1, 0x4AA9F1}); + basedOnPoints = GetPrivateProfileIntA("Skills", "BasedOnPoints", 0, file); - if (basedOnPoints) HookCall(0x4AA9EC, (void*)fo::funcoffs::skill_points_); + if (basedOnPoints) HookCall(0x4AA9EC, (void*)fo::funcoffs::skill_points_); // skill_dec_point_ } LoadGameHook::OnGameReset() += Skills_OnGameLoad; diff --git a/sfall/Modules/Skills.h b/sfall/Modules/Skills.h index 17cfc6dce..a8d83f888 100644 --- a/sfall/Modules/Skills.h +++ b/sfall/Modules/Skills.h @@ -29,6 +29,7 @@ class Skills : public Module { void init(); }; -void _stdcall SetSkillMax(DWORD critter, DWORD maximum); +void _stdcall SetSkillMax(fo::GameObject* critter, int maximum); +void _stdcall SetPickpocketMax(fo::GameObject* critter, DWORD maximum, DWORD mod); } diff --git a/sfall/Modules/Sound.cpp b/sfall/Modules/Sound.cpp index 4cc3b2f0d..5ec64e896 100644 --- a/sfall/Modules/Sound.cpp +++ b/sfall/Modules/Sound.cpp @@ -6,62 +6,67 @@ namespace sfall { -static char attackerSnd[8]; -static char targetSnd[8]; +static char attackerSnd[9] = {0}; +static char targetSnd[9] = {0}; -static void __declspec(naked) MsgCopy() { +static void __declspec(naked) combatai_msg_hook() { __asm { - mov edi, [esp+0xc]; - pushad; - cmp eax, FO_VAR_target_str; - jne attacker; - lea eax, targetSnd; - jmp end; + mov edi, [esp + 0xC]; // lip file from msg + push eax; + cmp eax, FO_VAR_target_str; + jne attacker; + lea eax, targetSnd; + jmp skip; attacker: - lea eax, attackerSnd; -end: - mov edx, edi; - mov ebx, 8; + lea eax, attackerSnd; +skip: + push edx; + push ebx; + mov edx, edi; + mov ebx, 8; call fo::funcoffs::strncpy_; - popad; - jmp fo::funcoffs::strncpy_; + pop ebx; + pop edx; + pop eax; + jmp fo::funcoffs::strncpy_; } } -static void __declspec(naked) DisplayMsg() { +static void __declspec(naked) ai_print_msg_hook() { __asm { - pushad; - cmp edx, FO_VAR_target_str; - jne attacker; - lea eax, targetSnd; - jmp end; + push eax; + cmp edx, FO_VAR_target_str; + jne attacker; + lea eax, targetSnd; + jmp skip; attacker: - lea eax, attackerSnd; -end: - mov ebx, [eax]; - test bl, bl; - jz skip; - call fo::funcoffs::gsound_play_sfx_file_; + lea eax, attackerSnd; skip: - popad; - jmp fo::funcoffs::text_object_create_; + push ecx; + mov ecx, [eax]; + test cl, cl; + jz end; + call fo::funcoffs::gsound_play_sfx_file_; +end: + pop ecx; + pop eax; + jmp fo::funcoffs::text_object_create_; } } void Sound::init() { - int tmp; - if (tmp = GetConfigInt("Sound", "NumSoundBuffers", 0)) { - SafeWrite8(0x451129, (BYTE)tmp); + if (int sBuff = GetConfigInt("Sound", "NumSoundBuffers", 0)) { + SafeWrite8(0x451129, (BYTE)sBuff); } if (GetConfigInt("Sound", "AllowSoundForFloats", 0)) { - HookCall(0x42B7C7, MsgCopy); - HookCall(0x42B849, DisplayMsg); - } + HookCall(0x42B7C7, combatai_msg_hook); // copy msg + HookCall(0x42B849, ai_print_msg_hook); - //Yes, I did leave this in on purpose. Will be of use to anyone trying to add in the sound effects - if (GetConfigInt("Sound", "Test_ForceFloats", 0)) { - SafeWrite8(0x42B772, 0xeb); + //Yes, I did leave this in on purpose. Will be of use to anyone trying to add in the sound effects + if (isDebug && GetConfigInt("Sound", "Test_ForceFloats", 0)) { + SafeWrite8(0x42B6F5, 0xEB); // bypass chance + } } } diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index 826530b06..b3298748e 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -48,15 +48,15 @@ static __declspec(naked) void GetDateWrapper() { push esi; push ebx; call fo::funcoffs::game_time_date_; - mov ecx, ds:[FO_VAR_pc_proto + 0x4C]; - pop esi; + mov ecx, ds:[FO_VAR_pc_proto + 0x4C]; + pop esi; test esi, esi; - jz end; - add ecx, [esi]; - mov[esi], ecx; + jz end; + add ecx, [esi]; + mov [esi], ecx; end: - pop esi; - pop ecx; + pop esi; + pop ecx; retn; } } @@ -71,19 +71,19 @@ static int mapSlotsScrollMax = 27 * (17 - 7); static __declspec(naked) void ScrollCityListHook() { __asm { push ebx; - mov ebx, ds:[0x672F10]; + mov ebx, ds:[0x672F10]; test eax, eax; - jl up; - cmp ebx, mapSlotsScrollMax; - je end; - jmp run; + jl up; + cmp ebx, mapSlotsScrollMax; + je end; + jmp run; up: test ebx, ebx; - jz end; + jz end; run: call fo::funcoffs::wmInterfaceScrollTabsStart_; end: - pop ebx; + pop ebx; retn; } } @@ -95,28 +95,28 @@ static void __stdcall WorldmapLoopHook() { static DWORD worldMapDelay; static void __declspec(naked) WorldMapFpsPatch() { __asm { - pushad; + pushadc; call WorldmapLoopHook; - mov ecx, worldMapDelay; + mov ecx, worldMapDelay; tck: - mov eax, ds : [0x50fb08]; + mov eax, ds:[0x50fb08]; call fo::funcoffs::elapsed_time_; - cmp eax, ecx; - jl tck; + cmp eax, ecx; + jl tck; call fo::funcoffs::get_time_; - mov ds : [0x50fb08], eax; - popad; - jmp fo::funcoffs::get_input_; + mov ds:[0x50fb08], eax; + popadc; + jmp fo::funcoffs::get_input_; } } //Only used if the world map speed patch is disabled, so that world map scripts are still run static void __declspec(naked) WorldMapHook() { __asm { - pushad; + pushadc; call WorldmapLoopHook; - popad; - jmp fo::funcoffs::get_input_; + popadc; + jmp fo::funcoffs::get_input_; } } @@ -139,36 +139,36 @@ static void __declspec(naked) wmRndEncounterOccurred_hack() { static void __declspec(naked) ViewportHook() { __asm { call fo::funcoffs::wmWorldMapLoadTempData_; - mov eax, ViewportX; - mov ds : [FO_VAR_wmWorldOffsetX], eax - mov eax, ViewportY; - mov ds : [FO_VAR_wmWorldOffsetY], eax; + mov eax, ViewportX; + mov ds:[FO_VAR_wmWorldOffsetX], eax; + mov eax, ViewportY; + mov ds:[FO_VAR_wmWorldOffsetY], eax; retn; } } static void __declspec(naked) wmTownMapFunc_hack() { __asm { - cmp edx, 0x31 - jl end - cmp edx, ecx - jge end - push edx - sub edx, 0x31 - lea eax, ds:0[edx*8] - sub eax, edx - pop edx - cmp dword ptr [edi+eax*4+0x0], 0 // Visited - je end - cmp dword ptr [edi+eax*4+0x4], -1 // Xpos - je end - cmp dword ptr [edi+eax*4+0x8], -1 // Ypos - je end - retn + cmp edx, 0x31; + jl end; + cmp edx, ecx; + jge end; + push edx; + sub edx, 0x31; + lea eax, ds:0[edx*8]; + sub eax, edx; + pop edx; + cmp dword ptr [edi+eax*4+0x0], 0; // Visited + je end; + cmp dword ptr [edi+eax*4+0x4], -1; // Xpos + je end; + cmp dword ptr [edi+eax*4+0x8], -1; // Ypos + je end; + retn; end: - add esp, 4 // Destroy the return address - push 0x4C4976 - retn + add esp, 4; // destroy the return address + push 0x4C4976; + retn; } } @@ -187,7 +187,7 @@ static __declspec(naked) void PathfinderFix3() { } static DWORD _stdcall PathfinderFix2(DWORD perkLevel, DWORD ticks) { - double d = MapMulti*MapMulti2; + double d = MapMulti * MapMulti2; if (perkLevel == 1) d *= 0.75; else if (perkLevel == 2) d *= 0.5; else if (perkLevel == 3) d *= 0.25; @@ -426,7 +426,7 @@ void StartingStatePatches() { if (ViewportY != -1) { dlog("Applying starting y view patch.", DL_INIT); SafeWrite32(FO_VAR_wmWorldOffsetY, ViewportY); - HookCall(0x4BCF07, &ViewportHook); + if (ViewportX == -1) HookCall(0x4BCF07, &ViewportHook); dlogr(" Done", DL_INIT); } } @@ -509,7 +509,7 @@ void Worldmap::SetRestMode(DWORD mode) { } if (mode & 2) { // bit2 - disable resting on maps with "can_rest_here=No" in Maps.txt, even if there are no other critters SafeWrite8(0x42E587, 0xE9); - SafeWrite32(0x42E588, 0x00000094); // jmp 0x42E620 + SafeWrite32(0x42E588, 0x94); // jmp 0x42E620 } if (mode & 4) { // bit3 - disable healing during resting SafeWrite16(0x499FD4, 0x9090); diff --git a/sfall/SafeWrite.cpp b/sfall/SafeWrite.cpp index 39962e7af..6772dabef 100644 --- a/sfall/SafeWrite.cpp +++ b/sfall/SafeWrite.cpp @@ -8,10 +8,63 @@ namespace sfall { + +enum CodeType : BYTE { + Call = 0xE8, + Jump = 0xE9, + Nop = 0x90 +}; + #ifndef NDEBUG std::list writeAddress; + +static void CheckConflict(DWORD addr) { + bool exist = false; + for (const auto &wa : writeAddress) { + if (addr == wa) { + exist = true; + char buf[256]; + sprintf_s(buf, "Memory writing conflict at address 0x%x. The address has already been overwritten by other code.", addr); + MessageBoxA(0, buf, "Conflict Detected", MB_TASKMODAL); + } + } + if (!exist) writeAddress.push_back(addr); +} #endif +static void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func) { + DWORD oldProtect, data = (DWORD)func - (addr + 5); + + VirtualProtect((void *)addr, 5, PAGE_EXECUTE_READWRITE, &oldProtect); + *((BYTE*)addr) = code; + *((DWORD*)(addr + 1)) = data; + VirtualProtect((void *)addr, 5, oldProtect, &oldProtect); + + #ifndef NDEBUG + CheckConflict(addr); + #endif +} + +static __declspec(noinline) void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func, DWORD len) { + DWORD oldProtect, + protectLen = len + 5, + addrMem = addr + 5, + data = (DWORD)func - addrMem; + + VirtualProtect((void *)addr, protectLen, PAGE_EXECUTE_READWRITE, &oldProtect); + *((BYTE*)addr) = code; + *((DWORD*)(addr + 1)) = data; + + for (unsigned int i = 0; i < len; i++) { + *((BYTE*)(addrMem + i)) = CodeType::Nop; + } + VirtualProtect((void *)addr, protectLen, oldProtect, &oldProtect); + + #ifndef NDEBUG + CheckConflict(addr); + #endif +} + void SafeWriteBytes(DWORD addr, BYTE* data, int count) { DWORD oldProtect; @@ -54,38 +107,25 @@ void _stdcall SafeWriteStr(DWORD addr, const char* data) { void HookCall(DWORD addr, void* func) { SafeWrite32(addr + 1, (DWORD)func - (addr + 5)); -#ifndef NDEBUG - bool exist = false; - for (const auto &wa : writeAddress) { - if (addr == wa) { - exist = true; - char buf[256]; - sprintf_s(buf, "Memory writing conflict at address 0x%x. The address has already been overwritten by other code.", addr); - MessageBoxA(0, buf, "Conflict Detected", MB_TASKMODAL); - } - } - if (!exist) writeAddress.push_back(addr); -#endif + #ifndef NDEBUG + CheckConflict(addr); + #endif } void MakeCall(DWORD addr, void* func) { - SafeWrite8(addr, 0xE8); - HookCall(addr, func); + SafeWriteFunc(CodeType::Call, addr, func); } void MakeCall(DWORD addr, void* func, int len) { - SafeMemSet(addr + 5, 0x90, len); - MakeCall(addr, func); + SafeWriteFunc(CodeType::Call, addr, func, len); } void MakeJump(DWORD addr, void* func) { - SafeWrite8(addr, 0xE9); - HookCall(addr, func); + SafeWriteFunc(CodeType::Jump, addr, func); } void MakeJump(DWORD addr, void* func, int len) { - SafeMemSet(addr + 5, 0x90, len); - MakeJump(addr, func); + SafeWriteFunc(CodeType::Jump, addr, func, len); } void HookCalls(void* func, std::initializer_list addrs) { @@ -109,7 +149,10 @@ void SafeMemSet(DWORD addr, BYTE val, int len) { } void BlockCall(DWORD addr) { - SafeMemSet(addr, 0x90, 5); + SafeMemSet(addr, CodeType::Nop, 5); + #ifndef NDEBUG + CheckConflict(addr); + #endif } } diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index add946267..ebdafef23 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -186,6 +186,8 @@ $(IntDir)/%(RelativeDir)/ $(IntDir)/%(RelativeDir)/ stdafx.h;%(ForcedIncludeFiles) + true + /Gw %(AdditionalOptions) kernel32.lib;user32.lib;d3d9.lib;d3dx9.lib;Strmiids.lib;dinput.lib;ws2_32.lib;%(AdditionalDependencies) @@ -282,7 +284,7 @@ - + @@ -351,7 +353,7 @@ - + diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index d8a8f7ee1..4d7e147b9 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -37,6 +37,9 @@ Modules + + Modules + Modules @@ -73,9 +76,6 @@ Modules - - Modules - Modules @@ -296,6 +296,9 @@ Modules + + Modules + Modules @@ -332,9 +335,6 @@ Modules - - Modules - Modules diff --git a/sfall/main.cpp b/sfall/main.cpp index afc5079e5..2d738bb0d 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -30,6 +30,7 @@ #include "Modules\Books.h" #include "Modules\BugFixes.h" #include "Modules\BurstMods.h" +#include "Modules\Combat.h" #include "Modules\Console.h" #include "Modules\CRC.h" #include "Modules\Credits.h" @@ -45,7 +46,6 @@ #include "Modules\Inventory.h" #include "Modules\Karma.h" #include "Modules\KillCounter.h" -#include "Modules\knockback.h" #include "Modules\LoadGameHook.h" #include "Modules\LoadOrder.h" #include "Modules\MainMenu.h" @@ -84,8 +84,9 @@ namespace sfall bool isDebug = false; const char ddrawIni[] = ".\\ddraw.ini"; -static char ini[65]; +static char ini[65] = ".\\"; static char translationIni[65]; +DWORD modifiedIni; unsigned int GetConfigInt(const char* section, const char* setting, int defaultValue) { return GetPrivateProfileIntA(section, setting, defaultValue, ini); @@ -131,7 +132,7 @@ static void InitModules() { auto& manager = ModuleManager::getInstance(); // initialize all modules - manager.add(); + manager.add(); // fixes should be applied at the beginning manager.add(); manager.add(); manager.add(); @@ -140,15 +141,15 @@ static void InitModules() { manager.add(); manager.add(); manager.add(); + manager.add(); manager.add(); manager.add(); manager.add(); manager.add(); manager.add(); - manager.add(); + manager.add(); manager.add(); manager.add(); - manager.add(); manager.add(); manager.add(); manager.add(); @@ -168,6 +169,7 @@ static void InitModules() { manager.add(); manager.add(); manager.add(); + manager.add(); manager.add(); manager.add(); @@ -257,12 +259,11 @@ inline void SfallInit() { } if (cmdlineexists && strlen(cmdline)) { - strcpy_s(ini, ".\\"); - strcat_s(ini, cmdline); HANDLE h = CreateFileA(cmdline, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); - if (h != INVALID_HANDLE_VALUE) + if (h != INVALID_HANDLE_VALUE) { CloseHandle(h); - else { + strcat_s(ini, cmdline); + } else { MessageBox(0, "You gave a command line argument to fallout, but it couldn't be matched to a file\n" \ "Using default ddraw.ini instead", "Warning", MB_TASKMODAL); strcpy_s(ini, ::sfall::ddrawIni); @@ -272,6 +273,7 @@ inline void SfallInit() { } GetConfigString("Main", "TranslationsINI", "./Translations.ini", translationIni, 65); + modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); InitModules(); } diff --git a/sfall/main.h b/sfall/main.h index 3309e3532..ba68c901b 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -62,6 +62,10 @@ namespace sfall #define isDebug false #endif +// Macros for quick replacement of pushad/popad assembler opcodes +#define pushadc __asm push eax __asm push edx __asm push ecx +#define popadc __asm pop ecx __asm pop edx __asm pop eax + // Gets the integer value from Sfall configuration INI file. unsigned int GetConfigInt(const char* section, const char* setting, int defaultValue); @@ -87,5 +91,6 @@ std::string Translate(const char* section, const char* setting, const char* defa size_t Translate(const char* section, const char* setting, const char* defaultValue, char* buffer, size_t bufSize = 128); extern const char ddrawIni[]; +extern DWORD modifiedIni; } diff --git a/sfall/version.h b/sfall/version.h index 664c8793f..8c39e8312 100644 --- a/sfall/version.h +++ b/sfall/version.h @@ -20,14 +20,14 @@ #define TARGETVERSION "Fallout 2 v1.02 US" -#define LEGAL_COPYRIGHT "Copyright (C) 2006-2018, sfall team" +#define LEGAL_COPYRIGHT "Copyright (C) 2006-2019, sfall team" #define VERSION_MAJOR 4 #define VERSION_MINOR 1 -#define VERSION_BUILD 3 +#define VERSION_BUILD 4 #define VERSION_REV 0 -#define VERSION_STRING "4.1.3" +#define VERSION_STRING "4.1.4" #define CHECK_VAL (4)